d3dx9/tests: Implemented CloneMesh test.
[wine/multimedia.git] / dlls / wininet / http.c
blobdb85d0b7535264e2aaba0daa424c07e3145543c9
1 /*
2 * Wininet - Http Implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2002 CodeWeavers Inc.
6 * Copyright 2002 TransGaming Technologies Inc.
7 * Copyright 2004 Mike McCormack for CodeWeavers
8 * Copyright 2005 Aric Stewart for CodeWeavers
9 * Copyright 2006 Robert Shearman for CodeWeavers
10 * Copyright 2011 Jacek Caban for CodeWeavers
12 * Ulrich Czekalla
13 * David Hammerton
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "config.h"
31 #include "wine/port.h"
33 #if defined(__MINGW32__) || defined (_MSC_VER)
34 #include <ws2tcpip.h>
35 #endif
37 #include <sys/types.h>
38 #ifdef HAVE_SYS_SOCKET_H
39 # include <sys/socket.h>
40 #endif
41 #ifdef HAVE_ARPA_INET_H
42 # include <arpa/inet.h>
43 #endif
44 #include <stdarg.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #ifdef HAVE_UNISTD_H
48 # include <unistd.h>
49 #endif
50 #include <time.h>
51 #include <assert.h>
52 #ifdef HAVE_ZLIB
53 # include <zlib.h>
54 #endif
56 #include "windef.h"
57 #include "winbase.h"
58 #include "wininet.h"
59 #include "winerror.h"
60 #include "winternl.h"
61 #define NO_SHLWAPI_STREAM
62 #define NO_SHLWAPI_REG
63 #define NO_SHLWAPI_STRFCNS
64 #define NO_SHLWAPI_GDI
65 #include "shlwapi.h"
66 #include "sspi.h"
67 #include "wincrypt.h"
69 #include "internet.h"
70 #include "wine/debug.h"
71 #include "wine/exception.h"
72 #include "wine/unicode.h"
74 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
76 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
77 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
78 static const WCHAR szOK[] = {'O','K',0};
79 static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
80 static const WCHAR hostW[] = { 'H','o','s','t',0 };
81 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
82 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
83 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
84 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
85 static const WCHAR szGET[] = { 'G','E','T', 0 };
86 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
87 static const WCHAR szCrLf[] = {'\r','\n', 0};
89 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
90 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
91 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
92 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
93 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
94 static const WCHAR szAge[] = { 'A','g','e',0 };
95 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
96 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
97 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
98 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
99 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
100 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
101 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
102 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
103 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
104 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
105 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
106 static const WCHAR szContent_Transfer_Encoding[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
107 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
108 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
109 static const WCHAR szDate[] = { 'D','a','t','e',0 };
110 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
111 static const WCHAR szETag[] = { 'E','T','a','g',0 };
112 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
113 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
114 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
115 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
116 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
117 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
118 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
119 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
120 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
121 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
122 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
123 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
124 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
125 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
126 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
127 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
128 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
129 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
130 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
131 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
132 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
133 static const WCHAR szUnless_Modified_Since[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
134 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
135 static const WCHAR szURI[] = { 'U','R','I',0 };
136 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
137 static const WCHAR szVary[] = { 'V','a','r','y',0 };
138 static const WCHAR szVia[] = { 'V','i','a',0 };
139 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
140 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
142 #define HTTP_REFERER szReferer
143 #define HTTP_ACCEPT szAccept
144 #define HTTP_USERAGENT szUser_Agent
146 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
147 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
148 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
149 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
150 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
151 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
152 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
154 #define COLLECT_TIME 60000
156 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
158 struct HttpAuthInfo
160 LPWSTR scheme;
161 CredHandle cred;
162 CtxtHandle ctx;
163 TimeStamp exp;
164 ULONG attr;
165 ULONG max_token;
166 void *auth_data;
167 unsigned int auth_data_len;
168 BOOL finished; /* finished authenticating */
172 typedef struct _basicAuthorizationData
174 struct list entry;
176 LPWSTR host;
177 LPWSTR realm;
178 LPSTR authorization;
179 UINT authorizationLen;
180 } basicAuthorizationData;
182 typedef struct _authorizationData
184 struct list entry;
186 LPWSTR host;
187 LPWSTR scheme;
188 LPWSTR domain;
189 UINT domain_len;
190 LPWSTR user;
191 UINT user_len;
192 LPWSTR password;
193 UINT password_len;
194 } authorizationData;
196 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
197 static struct list authorizationCache = LIST_INIT(authorizationCache);
199 static CRITICAL_SECTION authcache_cs;
200 static CRITICAL_SECTION_DEBUG critsect_debug =
202 0, 0, &authcache_cs,
203 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
204 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
206 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
208 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
209 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
210 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
211 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
212 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
213 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
214 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
215 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
216 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
217 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
218 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
220 static CRITICAL_SECTION connection_pool_cs;
221 static CRITICAL_SECTION_DEBUG connection_pool_debug =
223 0, 0, &connection_pool_cs,
224 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
225 0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
227 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
229 static struct list connection_pool = LIST_INIT(connection_pool);
230 static BOOL collector_running;
232 void server_addref(server_t *server)
234 InterlockedIncrement(&server->ref);
237 void server_release(server_t *server)
239 if(InterlockedDecrement(&server->ref))
240 return;
242 if(!server->ref)
243 server->keep_until = GetTickCount64() + COLLECT_TIME;
246 static server_t *get_server(const WCHAR *name, INTERNET_PORT port)
248 server_t *iter, *server = NULL;
250 EnterCriticalSection(&connection_pool_cs);
252 LIST_FOR_EACH_ENTRY(iter, &connection_pool, server_t, entry) {
253 if(iter->port == port && !strcmpW(iter->name, name)) {
254 server = iter;
255 server_addref(server);
256 break;
260 if(!server) {
261 server = heap_alloc(sizeof(*server));
262 if(server) {
263 server->addr_len = 0;
264 server->ref = 1;
265 server->port = port;
266 list_init(&server->conn_pool);
267 server->name = heap_strdupW(name);
268 if(server->name) {
269 list_add_head(&connection_pool, &server->entry);
270 }else {
271 heap_free(server);
272 server = NULL;
277 LeaveCriticalSection(&connection_pool_cs);
279 return server;
282 BOOL collect_connections(BOOL collect_all)
284 netconn_t *netconn, *netconn_safe;
285 server_t *server, *server_safe;
286 BOOL remaining = FALSE;
287 DWORD64 now;
289 now = GetTickCount64();
291 LIST_FOR_EACH_ENTRY_SAFE(server, server_safe, &connection_pool, server_t, entry) {
292 LIST_FOR_EACH_ENTRY_SAFE(netconn, netconn_safe, &server->conn_pool, netconn_t, pool_entry) {
293 if(collect_all || netconn->keep_until < now) {
294 TRACE("freeing %p\n", netconn);
295 list_remove(&netconn->pool_entry);
296 free_netconn(netconn);
297 }else {
298 remaining = TRUE;
302 if(!server->ref) {
303 if(collect_all || server->keep_until < now) {
304 list_remove(&server->entry);
306 heap_free(server->name);
307 heap_free(server);
308 }else {
309 remaining = TRUE;
314 return remaining;
317 static DWORD WINAPI collect_connections_proc(void *arg)
319 BOOL remaining_conns;
321 do {
322 /* FIXME: Use more sophisticated method */
323 Sleep(5000);
325 EnterCriticalSection(&connection_pool_cs);
327 remaining_conns = collect_connections(FALSE);
328 if(!remaining_conns)
329 collector_running = FALSE;
331 LeaveCriticalSection(&connection_pool_cs);
332 }while(remaining_conns);
334 FreeLibraryAndExitThread(WININET_hModule, 0);
337 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
339 int HeaderIndex = 0;
340 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
341 if (HeaderIndex == -1)
342 return NULL;
343 else
344 return &req->custHeaders[HeaderIndex];
347 typedef enum {
348 READMODE_SYNC,
349 READMODE_ASYNC,
350 READMODE_NOBLOCK
351 } read_mode_t;
353 struct data_stream_vtbl_t {
354 DWORD (*get_avail_data)(data_stream_t*,http_request_t*);
355 BOOL (*end_of_data)(data_stream_t*,http_request_t*);
356 DWORD (*read)(data_stream_t*,http_request_t*,BYTE*,DWORD,DWORD*,read_mode_t);
357 BOOL (*drain_content)(data_stream_t*,http_request_t*);
358 void (*destroy)(data_stream_t*);
361 typedef struct {
362 data_stream_t data_stream;
364 BYTE buf[READ_BUFFER_SIZE];
365 DWORD buf_size;
366 DWORD buf_pos;
367 DWORD chunk_size;
368 } chunked_stream_t;
370 static inline void destroy_data_stream(data_stream_t *stream)
372 stream->vtbl->destroy(stream);
375 static void reset_data_stream(http_request_t *req)
377 destroy_data_stream(req->data_stream);
378 req->data_stream = &req->netconn_stream.data_stream;
379 req->read_pos = req->read_size = req->netconn_stream.content_read = 0;
380 req->read_chunked = req->read_gzip = FALSE;
383 #ifdef HAVE_ZLIB
385 typedef struct {
386 data_stream_t stream;
387 data_stream_t *parent_stream;
388 z_stream zstream;
389 BYTE buf[READ_BUFFER_SIZE];
390 DWORD buf_size;
391 DWORD buf_pos;
392 BOOL end_of_data;
393 } gzip_stream_t;
395 static DWORD gzip_get_avail_data(data_stream_t *stream, http_request_t *req)
397 /* Allow reading only from read buffer */
398 return 0;
401 static BOOL gzip_end_of_data(data_stream_t *stream, http_request_t *req)
403 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
404 return gzip_stream->end_of_data;
407 static DWORD gzip_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
408 DWORD *read, read_mode_t read_mode)
410 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
411 z_stream *zstream = &gzip_stream->zstream;
412 DWORD current_read, ret_read = 0;
413 BOOL end;
414 int zres;
415 DWORD res = ERROR_SUCCESS;
417 while(size && !gzip_stream->end_of_data) {
418 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
420 if(gzip_stream->buf_size <= 64 && !end) {
421 if(gzip_stream->buf_pos) {
422 if(gzip_stream->buf_size)
423 memmove(gzip_stream->buf, gzip_stream->buf+gzip_stream->buf_pos, gzip_stream->buf_size);
424 gzip_stream->buf_pos = 0;
426 res = gzip_stream->parent_stream->vtbl->read(gzip_stream->parent_stream, req, gzip_stream->buf+gzip_stream->buf_size,
427 sizeof(gzip_stream->buf)-gzip_stream->buf_size, &current_read, read_mode);
428 gzip_stream->buf_size += current_read;
429 if(res != ERROR_SUCCESS)
430 break;
431 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
432 if(!current_read && !end) {
433 if(read_mode != READMODE_NOBLOCK) {
434 WARN("unexpected end of data\n");
435 gzip_stream->end_of_data = TRUE;
437 break;
439 if(gzip_stream->buf_size <= 64 && !end)
440 continue;
443 zstream->next_in = gzip_stream->buf+gzip_stream->buf_pos;
444 zstream->avail_in = gzip_stream->buf_size-(end ? 0 : 64);
445 zstream->next_out = buf+ret_read;
446 zstream->avail_out = size;
447 zres = inflate(&gzip_stream->zstream, 0);
448 current_read = size - zstream->avail_out;
449 size -= current_read;
450 ret_read += current_read;
451 gzip_stream->buf_size -= zstream->next_in - (gzip_stream->buf+gzip_stream->buf_pos);
452 gzip_stream->buf_pos = zstream->next_in-gzip_stream->buf;
453 if(zres == Z_STREAM_END) {
454 TRACE("end of data\n");
455 gzip_stream->end_of_data = TRUE;
456 inflateEnd(zstream);
457 }else if(zres != Z_OK) {
458 WARN("inflate failed %d: %s\n", zres, debugstr_a(zstream->msg));
459 if(!ret_read)
460 res = ERROR_INTERNET_DECODING_FAILED;
461 break;
464 if(ret_read && read_mode == READMODE_ASYNC)
465 read_mode = READMODE_NOBLOCK;
468 TRACE("read %u bytes\n", ret_read);
469 *read = ret_read;
470 return res;
473 static BOOL gzip_drain_content(data_stream_t *stream, http_request_t *req)
475 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
476 return gzip_stream->parent_stream->vtbl->drain_content(gzip_stream->parent_stream, req);
479 static void gzip_destroy(data_stream_t *stream)
481 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
483 destroy_data_stream(gzip_stream->parent_stream);
485 if(!gzip_stream->end_of_data)
486 inflateEnd(&gzip_stream->zstream);
487 heap_free(gzip_stream);
490 static const data_stream_vtbl_t gzip_stream_vtbl = {
491 gzip_get_avail_data,
492 gzip_end_of_data,
493 gzip_read,
494 gzip_drain_content,
495 gzip_destroy
498 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
500 return heap_alloc(items*size);
503 static void wininet_zfree(voidpf opaque, voidpf address)
505 heap_free(address);
508 static DWORD init_gzip_stream(http_request_t *req)
510 gzip_stream_t *gzip_stream;
511 int index, zres;
513 gzip_stream = heap_alloc_zero(sizeof(gzip_stream_t));
514 if(!gzip_stream)
515 return ERROR_OUTOFMEMORY;
517 gzip_stream->stream.vtbl = &gzip_stream_vtbl;
518 gzip_stream->zstream.zalloc = wininet_zalloc;
519 gzip_stream->zstream.zfree = wininet_zfree;
521 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
522 if(zres != Z_OK) {
523 ERR("inflateInit failed: %d\n", zres);
524 heap_free(gzip_stream);
525 return ERROR_OUTOFMEMORY;
528 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
529 if(index != -1)
530 HTTP_DeleteCustomHeader(req, index);
532 if(req->read_size) {
533 memcpy(gzip_stream->buf, req->read_buf+req->read_pos, req->read_size);
534 gzip_stream->buf_size = req->read_size;
535 req->read_pos = req->read_size = 0;
538 req->read_gzip = TRUE;
539 gzip_stream->parent_stream = req->data_stream;
540 req->data_stream = &gzip_stream->stream;
541 return ERROR_SUCCESS;
544 #else
546 static DWORD init_gzip_stream(http_request_t *req)
548 ERR("gzip stream not supported, missing zlib.\n");
549 return ERROR_SUCCESS;
552 #endif
554 /***********************************************************************
555 * HTTP_Tokenize (internal)
557 * Tokenize a string, allocating memory for the tokens.
559 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
561 LPWSTR * token_array;
562 int tokens = 0;
563 int i;
564 LPCWSTR next_token;
566 if (string)
568 /* empty string has no tokens */
569 if (*string)
570 tokens++;
571 /* count tokens */
572 for (i = 0; string[i]; i++)
574 if (!strncmpW(string+i, token_string, strlenW(token_string)))
576 DWORD j;
577 tokens++;
578 /* we want to skip over separators, but not the null terminator */
579 for (j = 0; j < strlenW(token_string) - 1; j++)
580 if (!string[i+j])
581 break;
582 i += j;
587 /* add 1 for terminating NULL */
588 token_array = heap_alloc((tokens+1) * sizeof(*token_array));
589 token_array[tokens] = NULL;
590 if (!tokens)
591 return token_array;
592 for (i = 0; i < tokens; i++)
594 int len;
595 next_token = strstrW(string, token_string);
596 if (!next_token) next_token = string+strlenW(string);
597 len = next_token - string;
598 token_array[i] = heap_alloc((len+1)*sizeof(WCHAR));
599 memcpy(token_array[i], string, len*sizeof(WCHAR));
600 token_array[i][len] = '\0';
601 string = next_token+strlenW(token_string);
603 return token_array;
606 /***********************************************************************
607 * HTTP_FreeTokens (internal)
609 * Frees memory returned from HTTP_Tokenize.
611 static void HTTP_FreeTokens(LPWSTR * token_array)
613 int i;
614 for (i = 0; token_array[i]; i++) heap_free(token_array[i]);
615 heap_free(token_array);
618 static void HTTP_FixURL(http_request_t *request)
620 static const WCHAR szSlash[] = { '/',0 };
621 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
623 /* If we don't have a path we set it to root */
624 if (NULL == request->path)
625 request->path = heap_strdupW(szSlash);
626 else /* remove \r and \n*/
628 int nLen = strlenW(request->path);
629 while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n')))
631 nLen--;
632 request->path[nLen]='\0';
634 /* Replace '\' with '/' */
635 while (nLen>0) {
636 nLen--;
637 if (request->path[nLen] == '\\') request->path[nLen]='/';
641 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
642 request->path, strlenW(request->path), szHttp, strlenW(szHttp) )
643 && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */
645 WCHAR *fixurl = heap_alloc((strlenW(request->path) + 2)*sizeof(WCHAR));
646 *fixurl = '/';
647 strcpyW(fixurl + 1, request->path);
648 heap_free( request->path );
649 request->path = fixurl;
653 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *request, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
655 LPWSTR requestString;
656 DWORD len, n;
657 LPCWSTR *req;
658 UINT i;
659 LPWSTR p;
661 static const WCHAR szSpace[] = { ' ',0 };
662 static const WCHAR szColon[] = { ':',' ',0 };
663 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
665 /* allocate space for an array of all the string pointers to be added */
666 len = (request->nCustHeaders)*4 + 10;
667 req = heap_alloc(len*sizeof(LPCWSTR));
669 /* add the verb, path and HTTP version string */
670 n = 0;
671 req[n++] = verb;
672 req[n++] = szSpace;
673 req[n++] = path;
674 req[n++] = szSpace;
675 req[n++] = version;
677 /* Append custom request headers */
678 for (i = 0; i < request->nCustHeaders; i++)
680 if (request->custHeaders[i].wFlags & HDR_ISREQUEST)
682 req[n++] = szCrLf;
683 req[n++] = request->custHeaders[i].lpszField;
684 req[n++] = szColon;
685 req[n++] = request->custHeaders[i].lpszValue;
687 TRACE("Adding custom header %s (%s)\n",
688 debugstr_w(request->custHeaders[i].lpszField),
689 debugstr_w(request->custHeaders[i].lpszValue));
693 if( n >= len )
694 ERR("oops. buffer overrun\n");
696 req[n] = NULL;
697 requestString = HTTP_build_req( req, 4 );
698 heap_free( req );
701 * Set (header) termination string for request
702 * Make sure there's exactly two new lines at the end of the request
704 p = &requestString[strlenW(requestString)-1];
705 while ( (*p == '\n') || (*p == '\r') )
706 p--;
707 strcpyW( p+1, sztwocrlf );
709 return requestString;
712 static void HTTP_ProcessCookies( http_request_t *request )
714 int HeaderIndex;
715 int numCookies = 0;
716 LPHTTPHEADERW setCookieHeader;
718 if(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES)
719 return;
721 while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, szSet_Cookie, numCookies++, FALSE)) != -1)
723 HTTPHEADERW *host;
724 const WCHAR *data;
725 WCHAR *name;
727 setCookieHeader = &request->custHeaders[HeaderIndex];
729 if (!setCookieHeader->lpszValue)
730 continue;
732 host = HTTP_GetHeader(request, hostW);
733 if(!host)
734 continue;
736 data = strchrW(setCookieHeader->lpszValue, '=');
737 if(!data)
738 continue;
740 name = heap_strndupW(setCookieHeader->lpszValue, data-setCookieHeader->lpszValue);
741 if(!name)
742 continue;
744 data++;
745 set_cookie(host->lpszValue, request->path, name, data);
746 heap_free(name);
750 static void strip_spaces(LPWSTR start)
752 LPWSTR str = start;
753 LPWSTR end;
755 while (*str == ' ' && *str != '\0')
756 str++;
758 if (str != start)
759 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
761 end = start + strlenW(start) - 1;
762 while (end >= start && *end == ' ')
764 *end = '\0';
765 end--;
769 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
771 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
772 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
773 BOOL is_basic;
774 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
775 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
776 if (is_basic && pszRealm)
778 LPCWSTR token;
779 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
780 LPCWSTR realm;
781 ptr++;
782 *pszRealm=NULL;
783 token = strchrW(ptr,'=');
784 if (!token)
785 return TRUE;
786 realm = ptr;
787 while (*realm == ' ' && *realm != '\0')
788 realm++;
789 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
790 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
792 token++;
793 while (*token == ' ' && *token != '\0')
794 token++;
795 if (*token == '\0')
796 return TRUE;
797 *pszRealm = heap_strdupW(token);
798 strip_spaces(*pszRealm);
802 return is_basic;
805 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
807 if (!authinfo) return;
809 if (SecIsValidHandle(&authinfo->ctx))
810 DeleteSecurityContext(&authinfo->ctx);
811 if (SecIsValidHandle(&authinfo->cred))
812 FreeCredentialsHandle(&authinfo->cred);
814 heap_free(authinfo->auth_data);
815 heap_free(authinfo->scheme);
816 heap_free(authinfo);
819 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
821 basicAuthorizationData *ad;
822 UINT rc = 0;
824 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
826 EnterCriticalSection(&authcache_cs);
827 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
829 if (!strcmpiW(host,ad->host) && !strcmpW(realm,ad->realm))
831 TRACE("Authorization found in cache\n");
832 *auth_data = heap_alloc(ad->authorizationLen);
833 memcpy(*auth_data,ad->authorization,ad->authorizationLen);
834 rc = ad->authorizationLen;
835 break;
838 LeaveCriticalSection(&authcache_cs);
839 return rc;
842 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
844 struct list *cursor;
845 basicAuthorizationData* ad = NULL;
847 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
849 EnterCriticalSection(&authcache_cs);
850 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
852 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
853 if (!strcmpiW(host,check->host) && !strcmpW(realm,check->realm))
855 ad = check;
856 break;
860 if (ad)
862 TRACE("Found match in cache, replacing\n");
863 heap_free(ad->authorization);
864 ad->authorization = heap_alloc(auth_data_len);
865 memcpy(ad->authorization, auth_data, auth_data_len);
866 ad->authorizationLen = auth_data_len;
868 else
870 ad = heap_alloc(sizeof(basicAuthorizationData));
871 ad->host = heap_strdupW(host);
872 ad->realm = heap_strdupW(realm);
873 ad->authorization = heap_alloc(auth_data_len);
874 memcpy(ad->authorization, auth_data, auth_data_len);
875 ad->authorizationLen = auth_data_len;
876 list_add_head(&basicAuthorizationCache,&ad->entry);
877 TRACE("authorization cached\n");
879 LeaveCriticalSection(&authcache_cs);
882 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
883 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
885 authorizationData *ad;
887 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
889 EnterCriticalSection(&authcache_cs);
890 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
891 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
892 TRACE("Authorization found in cache\n");
894 nt_auth_identity->User = heap_strdupW(ad->user);
895 nt_auth_identity->Password = heap_strdupW(ad->password);
896 nt_auth_identity->Domain = heap_alloc(sizeof(WCHAR)*ad->domain_len);
897 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
898 (!nt_auth_identity->Domain && ad->domain_len)) {
899 heap_free(nt_auth_identity->User);
900 heap_free(nt_auth_identity->Password);
901 heap_free(nt_auth_identity->Domain);
902 break;
905 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
906 nt_auth_identity->UserLength = ad->user_len;
907 nt_auth_identity->PasswordLength = ad->password_len;
908 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
909 nt_auth_identity->DomainLength = ad->domain_len;
910 LeaveCriticalSection(&authcache_cs);
911 return TRUE;
914 LeaveCriticalSection(&authcache_cs);
916 return FALSE;
919 static void cache_authorization(LPWSTR host, LPWSTR scheme,
920 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
922 authorizationData *ad;
923 BOOL found = FALSE;
925 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
927 EnterCriticalSection(&authcache_cs);
928 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
929 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
930 found = TRUE;
931 break;
934 if(found) {
935 heap_free(ad->user);
936 heap_free(ad->password);
937 heap_free(ad->domain);
938 } else {
939 ad = heap_alloc(sizeof(authorizationData));
940 if(!ad) {
941 LeaveCriticalSection(&authcache_cs);
942 return;
945 ad->host = heap_strdupW(host);
946 ad->scheme = heap_strdupW(scheme);
947 list_add_head(&authorizationCache, &ad->entry);
950 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
951 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
952 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
953 ad->user_len = nt_auth_identity->UserLength;
954 ad->password_len = nt_auth_identity->PasswordLength;
955 ad->domain_len = nt_auth_identity->DomainLength;
957 if(!ad->host || !ad->scheme || !ad->user || !ad->password
958 || (nt_auth_identity->Domain && !ad->domain)) {
959 heap_free(ad->host);
960 heap_free(ad->scheme);
961 heap_free(ad->user);
962 heap_free(ad->password);
963 heap_free(ad->domain);
964 list_remove(&ad->entry);
965 heap_free(ad);
968 LeaveCriticalSection(&authcache_cs);
971 static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue,
972 struct HttpAuthInfo **ppAuthInfo,
973 LPWSTR domain_and_username, LPWSTR password,
974 LPWSTR host )
976 SECURITY_STATUS sec_status;
977 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
978 BOOL first = FALSE;
979 LPWSTR szRealm = NULL;
981 TRACE("%s\n", debugstr_w(pszAuthValue));
983 if (!pAuthInfo)
985 TimeStamp exp;
987 first = TRUE;
988 pAuthInfo = heap_alloc(sizeof(*pAuthInfo));
989 if (!pAuthInfo)
990 return FALSE;
992 SecInvalidateHandle(&pAuthInfo->cred);
993 SecInvalidateHandle(&pAuthInfo->ctx);
994 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
995 pAuthInfo->attr = 0;
996 pAuthInfo->auth_data = NULL;
997 pAuthInfo->auth_data_len = 0;
998 pAuthInfo->finished = FALSE;
1000 if (is_basic_auth_value(pszAuthValue,NULL))
1002 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
1003 pAuthInfo->scheme = heap_strdupW(szBasic);
1004 if (!pAuthInfo->scheme)
1006 heap_free(pAuthInfo);
1007 return FALSE;
1010 else
1012 PVOID pAuthData;
1013 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
1015 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
1016 if (!pAuthInfo->scheme)
1018 heap_free(pAuthInfo);
1019 return FALSE;
1022 if (domain_and_username)
1024 WCHAR *user = strchrW(domain_and_username, '\\');
1025 WCHAR *domain = domain_and_username;
1027 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1029 pAuthData = &nt_auth_identity;
1031 if (user) user++;
1032 else
1034 user = domain_and_username;
1035 domain = NULL;
1038 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1039 nt_auth_identity.User = user;
1040 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
1041 nt_auth_identity.Domain = domain;
1042 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
1043 nt_auth_identity.Password = password;
1044 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
1046 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
1048 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
1049 pAuthData = &nt_auth_identity;
1050 else
1051 /* use default credentials */
1052 pAuthData = NULL;
1054 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
1055 SECPKG_CRED_OUTBOUND, NULL,
1056 pAuthData, NULL,
1057 NULL, &pAuthInfo->cred,
1058 &exp);
1060 if(pAuthData && !domain_and_username) {
1061 heap_free(nt_auth_identity.User);
1062 heap_free(nt_auth_identity.Domain);
1063 heap_free(nt_auth_identity.Password);
1066 if (sec_status == SEC_E_OK)
1068 PSecPkgInfoW sec_pkg_info;
1069 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
1070 if (sec_status == SEC_E_OK)
1072 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
1073 FreeContextBuffer(sec_pkg_info);
1076 if (sec_status != SEC_E_OK)
1078 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1079 debugstr_w(pAuthInfo->scheme), sec_status);
1080 heap_free(pAuthInfo->scheme);
1081 heap_free(pAuthInfo);
1082 return FALSE;
1085 *ppAuthInfo = pAuthInfo;
1087 else if (pAuthInfo->finished)
1088 return FALSE;
1090 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
1091 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
1093 ERR("authentication scheme changed from %s to %s\n",
1094 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
1095 return FALSE;
1098 if (is_basic_auth_value(pszAuthValue,&szRealm))
1100 int userlen;
1101 int passlen;
1102 char *auth_data = NULL;
1103 UINT auth_data_len = 0;
1105 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
1107 if (!domain_and_username)
1109 if (host && szRealm)
1110 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
1111 if (auth_data_len == 0)
1113 heap_free(szRealm);
1114 return FALSE;
1117 else
1119 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
1120 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
1122 /* length includes a nul terminator, which will be re-used for the ':' */
1123 auth_data = heap_alloc(userlen + 1 + passlen);
1124 if (!auth_data)
1126 heap_free(szRealm);
1127 return FALSE;
1130 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
1131 auth_data[userlen] = ':';
1132 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
1133 auth_data_len = userlen + 1 + passlen;
1134 if (host && szRealm)
1135 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
1138 pAuthInfo->auth_data = auth_data;
1139 pAuthInfo->auth_data_len = auth_data_len;
1140 pAuthInfo->finished = TRUE;
1141 heap_free(szRealm);
1142 return TRUE;
1144 else
1146 LPCWSTR pszAuthData;
1147 SecBufferDesc out_desc, in_desc;
1148 SecBuffer out, in;
1149 unsigned char *buffer;
1150 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
1151 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
1153 in.BufferType = SECBUFFER_TOKEN;
1154 in.cbBuffer = 0;
1155 in.pvBuffer = NULL;
1157 in_desc.ulVersion = 0;
1158 in_desc.cBuffers = 1;
1159 in_desc.pBuffers = &in;
1161 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
1162 if (*pszAuthData == ' ')
1164 pszAuthData++;
1165 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
1166 in.pvBuffer = heap_alloc(in.cbBuffer);
1167 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
1170 buffer = heap_alloc(pAuthInfo->max_token);
1172 out.BufferType = SECBUFFER_TOKEN;
1173 out.cbBuffer = pAuthInfo->max_token;
1174 out.pvBuffer = buffer;
1176 out_desc.ulVersion = 0;
1177 out_desc.cBuffers = 1;
1178 out_desc.pBuffers = &out;
1180 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
1181 first ? NULL : &pAuthInfo->ctx,
1182 first ? request->session->serverName : NULL,
1183 context_req, 0, SECURITY_NETWORK_DREP,
1184 in.pvBuffer ? &in_desc : NULL,
1185 0, &pAuthInfo->ctx, &out_desc,
1186 &pAuthInfo->attr, &pAuthInfo->exp);
1187 if (sec_status == SEC_E_OK)
1189 pAuthInfo->finished = TRUE;
1190 pAuthInfo->auth_data = out.pvBuffer;
1191 pAuthInfo->auth_data_len = out.cbBuffer;
1192 TRACE("sending last auth packet\n");
1194 else if (sec_status == SEC_I_CONTINUE_NEEDED)
1196 pAuthInfo->auth_data = out.pvBuffer;
1197 pAuthInfo->auth_data_len = out.cbBuffer;
1198 TRACE("sending next auth packet\n");
1200 else
1202 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
1203 heap_free(out.pvBuffer);
1204 destroy_authinfo(pAuthInfo);
1205 *ppAuthInfo = NULL;
1206 return FALSE;
1210 return TRUE;
1213 /***********************************************************************
1214 * HTTP_HttpAddRequestHeadersW (internal)
1216 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *request,
1217 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1219 LPWSTR lpszStart;
1220 LPWSTR lpszEnd;
1221 LPWSTR buffer;
1222 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
1224 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
1226 if( dwHeaderLength == ~0U )
1227 len = strlenW(lpszHeader);
1228 else
1229 len = dwHeaderLength;
1230 buffer = heap_alloc(sizeof(WCHAR)*(len+1));
1231 lstrcpynW( buffer, lpszHeader, len + 1);
1233 lpszStart = buffer;
1237 LPWSTR * pFieldAndValue;
1239 lpszEnd = lpszStart;
1241 while (*lpszEnd != '\0')
1243 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1244 break;
1245 lpszEnd++;
1248 if (*lpszStart == '\0')
1249 break;
1251 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1253 *lpszEnd = '\0';
1254 lpszEnd++; /* Jump over newline */
1256 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1257 if (*lpszStart == '\0')
1259 /* Skip 0-length headers */
1260 lpszStart = lpszEnd;
1261 res = ERROR_SUCCESS;
1262 continue;
1264 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1265 if (pFieldAndValue)
1267 res = HTTP_VerifyValidHeader(request, pFieldAndValue[0]);
1268 if (res == ERROR_SUCCESS)
1269 res = HTTP_ProcessHeader(request, pFieldAndValue[0],
1270 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1271 HTTP_FreeTokens(pFieldAndValue);
1274 lpszStart = lpszEnd;
1275 } while (res == ERROR_SUCCESS);
1277 heap_free(buffer);
1278 return res;
1281 /***********************************************************************
1282 * HttpAddRequestHeadersW (WININET.@)
1284 * Adds one or more HTTP header to the request handler
1286 * NOTE
1287 * On Windows if dwHeaderLength includes the trailing '\0', then
1288 * HttpAddRequestHeadersW() adds it too. However this results in an
1289 * invalid Http header which is rejected by some servers so we probably
1290 * don't need to match Windows on that point.
1292 * RETURNS
1293 * TRUE on success
1294 * FALSE on failure
1297 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1298 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1300 http_request_t *request;
1301 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1303 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1305 if (!lpszHeader)
1306 return TRUE;
1308 request = (http_request_t*) get_handle_object( hHttpRequest );
1309 if (request && request->hdr.htype == WH_HHTTPREQ)
1310 res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier );
1311 if( request )
1312 WININET_Release( &request->hdr );
1314 if(res != ERROR_SUCCESS)
1315 SetLastError(res);
1316 return res == ERROR_SUCCESS;
1319 /***********************************************************************
1320 * HttpAddRequestHeadersA (WININET.@)
1322 * Adds one or more HTTP header to the request handler
1324 * RETURNS
1325 * TRUE on success
1326 * FALSE on failure
1329 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1330 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1332 DWORD len;
1333 LPWSTR hdr;
1334 BOOL r;
1336 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1338 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1339 hdr = heap_alloc(len*sizeof(WCHAR));
1340 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1341 if( dwHeaderLength != ~0U )
1342 dwHeaderLength = len;
1344 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1346 heap_free( hdr );
1347 return r;
1350 static void free_accept_types( WCHAR **accept_types )
1352 WCHAR *ptr, **types = accept_types;
1354 if (!types) return;
1355 while ((ptr = *types))
1357 heap_free( ptr );
1358 types++;
1360 heap_free( accept_types );
1363 static WCHAR **convert_accept_types( const char **accept_types )
1365 unsigned int count;
1366 const char **types = accept_types;
1367 WCHAR **typesW;
1368 BOOL invalid_pointer = FALSE;
1370 if (!types) return NULL;
1371 count = 0;
1372 while (*types)
1374 __TRY
1376 /* find out how many there are */
1377 if (*types && **types)
1379 TRACE("accept type: %s\n", debugstr_a(*types));
1380 count++;
1383 __EXCEPT_PAGE_FAULT
1385 WARN("invalid accept type pointer\n");
1386 invalid_pointer = TRUE;
1388 __ENDTRY;
1389 types++;
1391 if (invalid_pointer) return NULL;
1392 if (!(typesW = heap_alloc( sizeof(WCHAR *) * (count + 1) ))) return NULL;
1393 count = 0;
1394 types = accept_types;
1395 while (*types)
1397 if (*types && **types) typesW[count++] = heap_strdupAtoW( *types );
1398 types++;
1400 typesW[count] = NULL;
1401 return typesW;
1404 /***********************************************************************
1405 * HttpOpenRequestA (WININET.@)
1407 * Open a HTTP request handle
1409 * RETURNS
1410 * HINTERNET a HTTP request handle on success
1411 * NULL on failure
1414 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1415 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1416 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1417 DWORD dwFlags, DWORD_PTR dwContext)
1419 LPWSTR szVerb = NULL, szObjectName = NULL;
1420 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1421 HINTERNET rc = FALSE;
1423 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1424 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1425 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1426 dwFlags, dwContext);
1428 if (lpszVerb)
1430 szVerb = heap_strdupAtoW(lpszVerb);
1431 if ( !szVerb )
1432 goto end;
1435 if (lpszObjectName)
1437 szObjectName = heap_strdupAtoW(lpszObjectName);
1438 if ( !szObjectName )
1439 goto end;
1442 if (lpszVersion)
1444 szVersion = heap_strdupAtoW(lpszVersion);
1445 if ( !szVersion )
1446 goto end;
1449 if (lpszReferrer)
1451 szReferrer = heap_strdupAtoW(lpszReferrer);
1452 if ( !szReferrer )
1453 goto end;
1456 szAcceptTypes = convert_accept_types( lpszAcceptTypes );
1457 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName, szVersion, szReferrer,
1458 (const WCHAR **)szAcceptTypes, dwFlags, dwContext);
1460 end:
1461 free_accept_types(szAcceptTypes);
1462 heap_free(szReferrer);
1463 heap_free(szVersion);
1464 heap_free(szObjectName);
1465 heap_free(szVerb);
1466 return rc;
1469 /***********************************************************************
1470 * HTTP_EncodeBase64
1472 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1474 UINT n = 0, x;
1475 static const CHAR HTTP_Base64Enc[] =
1476 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1478 while( len > 0 )
1480 /* first 6 bits, all from bin[0] */
1481 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1482 x = (bin[0] & 3) << 4;
1484 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1485 if( len == 1 )
1487 base64[n++] = HTTP_Base64Enc[x];
1488 base64[n++] = '=';
1489 base64[n++] = '=';
1490 break;
1492 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1493 x = ( bin[1] & 0x0f ) << 2;
1495 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1496 if( len == 2 )
1498 base64[n++] = HTTP_Base64Enc[x];
1499 base64[n++] = '=';
1500 break;
1502 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1504 /* last 6 bits, all from bin [2] */
1505 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1506 bin += 3;
1507 len -= 3;
1509 base64[n] = 0;
1510 return n;
1513 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1514 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1515 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1516 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1517 static const signed char HTTP_Base64Dec[256] =
1519 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1520 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1521 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1522 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1523 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1524 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1525 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1526 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1527 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1528 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1529 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1530 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1531 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1532 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1533 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1534 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1535 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1536 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1537 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1538 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1539 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1540 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1541 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1542 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1543 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1544 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1546 #undef CH
1548 /***********************************************************************
1549 * HTTP_DecodeBase64
1551 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1553 unsigned int n = 0;
1555 while(*base64)
1557 signed char in[4];
1559 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1560 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1561 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1562 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1564 WARN("invalid base64: %s\n", debugstr_w(base64));
1565 return 0;
1567 if (bin)
1568 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1569 n++;
1571 if ((base64[2] == '=') && (base64[3] == '='))
1572 break;
1573 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1574 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1576 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1577 return 0;
1579 if (bin)
1580 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1581 n++;
1583 if (base64[3] == '=')
1584 break;
1585 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1586 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1588 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1589 return 0;
1591 if (bin)
1592 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1593 n++;
1595 base64 += 4;
1598 return n;
1601 /***********************************************************************
1602 * HTTP_InsertAuthorization
1604 * Insert or delete the authorization field in the request header.
1606 static BOOL HTTP_InsertAuthorization( http_request_t *request, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1608 if (pAuthInfo)
1610 static const WCHAR wszSpace[] = {' ',0};
1611 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1612 unsigned int len;
1613 WCHAR *authorization = NULL;
1615 if (pAuthInfo->auth_data_len)
1617 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1618 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1619 authorization = heap_alloc((len+1)*sizeof(WCHAR));
1620 if (!authorization)
1621 return FALSE;
1623 strcpyW(authorization, pAuthInfo->scheme);
1624 strcatW(authorization, wszSpace);
1625 HTTP_EncodeBase64(pAuthInfo->auth_data,
1626 pAuthInfo->auth_data_len,
1627 authorization+strlenW(authorization));
1629 /* clear the data as it isn't valid now that it has been sent to the
1630 * server, unless it's Basic authentication which doesn't do
1631 * connection tracking */
1632 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1634 heap_free(pAuthInfo->auth_data);
1635 pAuthInfo->auth_data = NULL;
1636 pAuthInfo->auth_data_len = 0;
1640 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1642 HTTP_ProcessHeader(request, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1643 heap_free(authorization);
1645 return TRUE;
1648 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1650 static const WCHAR slash[] = { '/',0 };
1651 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','u',0 };
1652 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','u',0 };
1653 http_session_t *session = req->session;
1654 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1655 DWORD size;
1657 size = sizeof(new_location);
1658 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1660 URL_COMPONENTSW UrlComponents;
1662 if (!(url = heap_alloc(size + sizeof(WCHAR)))) return NULL;
1663 strcpyW( url, new_location );
1665 ZeroMemory(&UrlComponents,sizeof(URL_COMPONENTSW));
1666 if(InternetCrackUrlW(url, 0, 0, &UrlComponents)) goto done;
1667 heap_free(url);
1670 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1671 size += strlenW( session->hostName ) + strlenW( req->path );
1673 if (!(url = heap_alloc(size * sizeof(WCHAR)))) return NULL;
1675 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1676 sprintfW( url, formatSSL, session->hostName, session->hostPort );
1677 else
1678 sprintfW( url, format, session->hostName, session->hostPort );
1679 if (req->path[0] != '/') strcatW( url, slash );
1680 strcatW( url, req->path );
1682 done:
1683 TRACE("url=%s\n", debugstr_w(url));
1684 return url;
1687 /***********************************************************************
1688 * HTTP_DealWithProxy
1690 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_request_t *request)
1692 WCHAR buf[INTERNET_MAX_HOST_NAME_LENGTH];
1693 WCHAR protoProxy[INTERNET_MAX_URL_LENGTH];
1694 DWORD protoProxyLen = INTERNET_MAX_URL_LENGTH;
1695 WCHAR proxy[INTERNET_MAX_URL_LENGTH];
1696 static WCHAR szNul[] = { 0 };
1697 URL_COMPONENTSW UrlComponents;
1698 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1699 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1700 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1702 memset( &UrlComponents, 0, sizeof UrlComponents );
1703 UrlComponents.dwStructSize = sizeof UrlComponents;
1704 UrlComponents.lpszHostName = buf;
1705 UrlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
1707 if (!INTERNET_FindProxyForProtocol(hIC->proxy, protoHttp, protoProxy, &protoProxyLen))
1708 return FALSE;
1709 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1710 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1711 sprintfW(proxy, szFormat, protoProxy);
1712 else
1713 strcpyW(proxy, protoProxy);
1714 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1715 return FALSE;
1716 if( UrlComponents.dwHostNameLength == 0 )
1717 return FALSE;
1719 if( !request->path )
1720 request->path = szNul;
1722 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1723 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1725 heap_free(session->serverName);
1726 session->serverName = heap_strdupW(UrlComponents.lpszHostName);
1727 session->serverPort = UrlComponents.nPort;
1729 TRACE("proxy server=%s port=%d\n", debugstr_w(session->serverName), session->serverPort);
1730 return TRUE;
1733 static DWORD HTTP_ResolveName(http_request_t *request, server_t *server)
1735 socklen_t addr_len;
1736 const void *addr;
1738 if(server->addr_len)
1739 return ERROR_SUCCESS;
1741 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1742 INTERNET_STATUS_RESOLVING_NAME,
1743 server->name,
1744 (strlenW(server->name)+1) * sizeof(WCHAR));
1746 addr_len = sizeof(server->addr);
1747 if (!GetAddress(server->name, server->port, (struct sockaddr *)&server->addr, &addr_len))
1748 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1750 switch(server->addr.ss_family) {
1751 case AF_INET:
1752 addr = &((struct sockaddr_in *)&server->addr)->sin_addr;
1753 break;
1754 case AF_INET6:
1755 addr = &((struct sockaddr_in6 *)&server->addr)->sin6_addr;
1756 break;
1757 default:
1758 WARN("unsupported family %d\n", server->addr.ss_family);
1759 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1762 server->addr_len = addr_len;
1763 inet_ntop(server->addr.ss_family, addr, server->addr_str, sizeof(server->addr_str));
1764 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1765 INTERNET_STATUS_NAME_RESOLVED,
1766 server->addr_str, strlen(server->addr_str)+1);
1768 TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str);
1769 return ERROR_SUCCESS;
1772 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1774 static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
1775 static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
1776 static const WCHAR slash[] = { '/',0 };
1777 LPHTTPHEADERW host_header;
1778 LPCWSTR scheme;
1780 host_header = HTTP_GetHeader(req, hostW);
1781 if(!host_header)
1782 return FALSE;
1784 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1785 scheme = https;
1786 else
1787 scheme = http;
1788 strcpyW(buf, scheme);
1789 strcatW(buf, host_header->lpszValue);
1790 if (req->path[0] != '/')
1791 strcatW(buf, slash);
1792 strcatW(buf, req->path);
1793 return TRUE;
1797 /***********************************************************************
1798 * HTTPREQ_Destroy (internal)
1800 * Deallocate request handle
1803 static void HTTPREQ_Destroy(object_header_t *hdr)
1805 http_request_t *request = (http_request_t*) hdr;
1806 DWORD i;
1808 TRACE("\n");
1810 if(request->hCacheFile) {
1811 WCHAR url[INTERNET_MAX_URL_LENGTH];
1813 CloseHandle(request->hCacheFile);
1815 if(HTTP_GetRequestURL(request, url)) {
1816 DWORD headersLen;
1818 headersLen = request->rawHeaders ? strlenW(request->rawHeaders) : 0;
1819 CommitUrlCacheEntryW(url, request->cacheFile, request->expires,
1820 request->last_modified, NORMAL_CACHE_ENTRY,
1821 request->rawHeaders, headersLen, NULL, 0);
1824 heap_free(request->cacheFile);
1826 DeleteCriticalSection( &request->read_section );
1827 WININET_Release(&request->session->hdr);
1829 destroy_authinfo(request->authInfo);
1830 destroy_authinfo(request->proxyAuthInfo);
1832 heap_free(request->path);
1833 heap_free(request->verb);
1834 heap_free(request->rawHeaders);
1835 heap_free(request->version);
1836 heap_free(request->statusText);
1838 for (i = 0; i < request->nCustHeaders; i++)
1840 heap_free(request->custHeaders[i].lpszField);
1841 heap_free(request->custHeaders[i].lpszValue);
1843 destroy_data_stream(request->data_stream);
1844 heap_free(request->custHeaders);
1847 static void http_release_netconn(http_request_t *req, BOOL reuse)
1849 TRACE("%p %p\n",req, req->netconn);
1851 if(!req->netconn)
1852 return;
1854 if(reuse && req->netconn->keep_alive) {
1855 BOOL run_collector;
1857 EnterCriticalSection(&connection_pool_cs);
1859 list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry);
1860 req->netconn->keep_until = GetTickCount64() + COLLECT_TIME;
1861 req->netconn = NULL;
1863 run_collector = !collector_running;
1864 collector_running = TRUE;
1866 LeaveCriticalSection(&connection_pool_cs);
1868 if(run_collector) {
1869 HANDLE thread = NULL;
1870 HMODULE module;
1872 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
1873 if(module)
1874 thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL);
1875 if(!thread) {
1876 EnterCriticalSection(&connection_pool_cs);
1877 collector_running = FALSE;
1878 LeaveCriticalSection(&connection_pool_cs);
1880 if(module)
1881 FreeLibrary(module);
1884 return;
1887 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1888 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1890 free_netconn(req->netconn);
1891 req->netconn = NULL;
1893 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1894 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1897 static void drain_content(http_request_t *req)
1899 BOOL try_reuse;
1901 if (!req->netconn) return;
1903 if (req->contentLength == -1)
1904 try_reuse = FALSE;
1905 else if(!strcmpW(req->verb, szHEAD))
1906 try_reuse = TRUE;
1907 else
1908 try_reuse = req->data_stream->vtbl->drain_content(req->data_stream, req);
1910 http_release_netconn(req, try_reuse);
1913 static BOOL HTTP_KeepAlive(http_request_t *request)
1915 WCHAR szVersion[10];
1916 WCHAR szConnectionResponse[20];
1917 DWORD dwBufferSize = sizeof(szVersion);
1918 BOOL keepalive = FALSE;
1920 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1921 * the connection is keep-alive by default */
1922 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1923 && !strcmpiW(szVersion, g_szHttp1_1))
1925 keepalive = TRUE;
1928 dwBufferSize = sizeof(szConnectionResponse);
1929 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1930 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1932 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1935 return keepalive;
1938 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1940 http_request_t *req = (http_request_t*)hdr;
1942 drain_content(req);
1945 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1947 http_request_t *req = (http_request_t*)hdr;
1949 switch(option) {
1950 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1952 http_session_t *session = req->session;
1953 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1955 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1957 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1958 return ERROR_INSUFFICIENT_BUFFER;
1959 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1960 /* FIXME: can't get a SOCKET from our connection since we don't use
1961 * winsock
1963 info->Socket = 0;
1964 /* FIXME: get source port from req->netConnection */
1965 info->SourcePort = 0;
1966 info->DestPort = session->hostPort;
1967 info->Flags = 0;
1968 if (HTTP_KeepAlive(req))
1969 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1970 if (session->appInfo->proxy && session->appInfo->proxy[0] != 0)
1971 info->Flags |= IDSI_FLAG_PROXY;
1972 if (req->netconn->useSSL)
1973 info->Flags |= IDSI_FLAG_SECURE;
1975 return ERROR_SUCCESS;
1978 case INTERNET_OPTION_SECURITY_FLAGS:
1980 DWORD flags;
1982 if (*size < sizeof(ULONG))
1983 return ERROR_INSUFFICIENT_BUFFER;
1985 *size = sizeof(DWORD);
1986 flags = 0;
1987 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1988 flags |= SECURITY_FLAG_SECURE;
1989 flags |= req->security_flags;
1990 if(req->netconn) {
1991 int bits = NETCON_GetCipherStrength(req->netconn);
1992 if (bits >= 128)
1993 flags |= SECURITY_FLAG_STRENGTH_STRONG;
1994 else if (bits >= 56)
1995 flags |= SECURITY_FLAG_STRENGTH_MEDIUM;
1996 else
1997 flags |= SECURITY_FLAG_STRENGTH_WEAK;
1999 *(DWORD *)buffer = flags;
2000 return ERROR_SUCCESS;
2003 case INTERNET_OPTION_HANDLE_TYPE:
2004 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2006 if (*size < sizeof(ULONG))
2007 return ERROR_INSUFFICIENT_BUFFER;
2009 *size = sizeof(DWORD);
2010 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
2011 return ERROR_SUCCESS;
2013 case INTERNET_OPTION_URL: {
2014 WCHAR url[INTERNET_MAX_URL_LENGTH];
2015 HTTPHEADERW *host;
2016 DWORD len;
2017 WCHAR *pch;
2019 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
2021 TRACE("INTERNET_OPTION_URL\n");
2023 host = HTTP_GetHeader(req, hostW);
2024 strcpyW(url, httpW);
2025 strcatW(url, host->lpszValue);
2026 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
2027 *pch = 0;
2028 strcatW(url, req->path);
2030 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
2032 if(unicode) {
2033 len = (strlenW(url)+1) * sizeof(WCHAR);
2034 if(*size < len)
2035 return ERROR_INSUFFICIENT_BUFFER;
2037 *size = len;
2038 strcpyW(buffer, url);
2039 return ERROR_SUCCESS;
2040 }else {
2041 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
2042 if(len > *size)
2043 return ERROR_INSUFFICIENT_BUFFER;
2045 *size = len;
2046 return ERROR_SUCCESS;
2050 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2051 INTERNET_CACHE_ENTRY_INFOW *info;
2052 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2053 WCHAR url[INTERNET_MAX_URL_LENGTH];
2054 DWORD nbytes, error;
2055 BOOL ret;
2057 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2059 if (*size < sizeof(*ts))
2061 *size = sizeof(*ts);
2062 return ERROR_INSUFFICIENT_BUFFER;
2064 nbytes = 0;
2065 HTTP_GetRequestURL(req, url);
2066 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
2067 error = GetLastError();
2068 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2070 if (!(info = heap_alloc(nbytes)))
2071 return ERROR_OUTOFMEMORY;
2073 GetUrlCacheEntryInfoW(url, info, &nbytes);
2075 ts->ftExpires = info->ExpireTime;
2076 ts->ftLastModified = info->LastModifiedTime;
2078 heap_free(info);
2079 *size = sizeof(*ts);
2080 return ERROR_SUCCESS;
2082 return error;
2085 case INTERNET_OPTION_DATAFILE_NAME: {
2086 DWORD req_size;
2088 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2090 if(!req->cacheFile) {
2091 *size = 0;
2092 return ERROR_INTERNET_ITEM_NOT_FOUND;
2095 if(unicode) {
2096 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
2097 if(*size < req_size)
2098 return ERROR_INSUFFICIENT_BUFFER;
2100 *size = req_size;
2101 memcpy(buffer, req->cacheFile, *size);
2102 return ERROR_SUCCESS;
2103 }else {
2104 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
2105 if (req_size > *size)
2106 return ERROR_INSUFFICIENT_BUFFER;
2108 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
2109 -1, buffer, *size, NULL, NULL);
2110 return ERROR_SUCCESS;
2114 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2115 PCCERT_CONTEXT context;
2117 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2118 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2119 return ERROR_INSUFFICIENT_BUFFER;
2122 context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2123 if(context) {
2124 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2125 DWORD len;
2127 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
2128 info->ftExpiry = context->pCertInfo->NotAfter;
2129 info->ftStart = context->pCertInfo->NotBefore;
2130 len = CertNameToStrA(context->dwCertEncodingType,
2131 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
2132 info->lpszSubjectInfo = LocalAlloc(0, len);
2133 if(info->lpszSubjectInfo)
2134 CertNameToStrA(context->dwCertEncodingType,
2135 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
2136 info->lpszSubjectInfo, len);
2137 len = CertNameToStrA(context->dwCertEncodingType,
2138 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
2139 info->lpszIssuerInfo = LocalAlloc(0, len);
2140 if(info->lpszIssuerInfo)
2141 CertNameToStrA(context->dwCertEncodingType,
2142 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
2143 info->lpszIssuerInfo, len);
2144 info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2145 CertFreeCertificateContext(context);
2146 return ERROR_SUCCESS;
2151 return INET_QueryOption(hdr, option, buffer, size, unicode);
2154 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2156 http_request_t *req = (http_request_t*)hdr;
2158 switch(option) {
2159 case INTERNET_OPTION_SECURITY_FLAGS:
2161 DWORD flags;
2163 if (!buffer || size != sizeof(DWORD))
2164 return ERROR_INVALID_PARAMETER;
2165 flags = *(DWORD *)buffer;
2166 TRACE("%08x\n", flags);
2167 req->security_flags = flags;
2168 if(req->netconn)
2169 req->netconn->security_flags = flags;
2170 return ERROR_SUCCESS;
2172 case INTERNET_OPTION_SEND_TIMEOUT:
2173 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2174 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
2176 if (size != sizeof(DWORD))
2177 return ERROR_INVALID_PARAMETER;
2179 if(!req->netconn) {
2180 FIXME("unsupported without active connection\n");
2181 return ERROR_SUCCESS;
2184 return NETCON_set_timeout(req->netconn, option == INTERNET_OPTION_SEND_TIMEOUT,
2185 *(DWORD*)buffer);
2187 case INTERNET_OPTION_USERNAME:
2188 heap_free(req->session->userName);
2189 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2190 return ERROR_SUCCESS;
2192 case INTERNET_OPTION_PASSWORD:
2193 heap_free(req->session->password);
2194 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2195 return ERROR_SUCCESS;
2196 case INTERNET_OPTION_HTTP_DECODING:
2197 if(size != sizeof(BOOL))
2198 return ERROR_INVALID_PARAMETER;
2199 req->decoding = *(BOOL*)buffer;
2200 return ERROR_SUCCESS;
2203 return ERROR_INTERNET_INVALID_OPTION;
2206 /* read some more data into the read buffer (the read section must be held) */
2207 static DWORD read_more_data( http_request_t *req, int maxlen )
2209 DWORD res;
2210 int len;
2212 if (req->read_pos)
2214 /* move existing data to the start of the buffer */
2215 if(req->read_size)
2216 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2217 req->read_pos = 0;
2220 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2222 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2223 maxlen - req->read_size, 0, &len );
2224 if(res == ERROR_SUCCESS)
2225 req->read_size += len;
2227 return res;
2230 /* remove some amount of data from the read buffer (the read section must be held) */
2231 static void remove_data( http_request_t *req, int count )
2233 if (!(req->read_size -= count)) req->read_pos = 0;
2234 else req->read_pos += count;
2237 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2239 int count, bytes_read, pos = 0;
2240 DWORD res;
2242 EnterCriticalSection( &req->read_section );
2243 for (;;)
2245 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2247 if (eol)
2249 count = eol - (req->read_buf + req->read_pos);
2250 bytes_read = count + 1;
2252 else count = bytes_read = req->read_size;
2254 count = min( count, *len - pos );
2255 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2256 pos += count;
2257 remove_data( req, bytes_read );
2258 if (eol) break;
2260 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2262 *len = 0;
2263 TRACE( "returning empty string %u\n", res);
2264 LeaveCriticalSection( &req->read_section );
2265 INTERNET_SetLastError(res);
2266 return FALSE;
2269 LeaveCriticalSection( &req->read_section );
2271 if (pos < *len)
2273 if (pos && buffer[pos - 1] == '\r') pos--;
2274 *len = pos + 1;
2276 buffer[*len - 1] = 0;
2277 TRACE( "returning %s\n", debugstr_a(buffer));
2278 return TRUE;
2281 /* check if we have reached the end of the data to read (the read section must be held) */
2282 static BOOL end_of_read_data( http_request_t *req )
2284 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2287 /* fetch some more data into the read buffer (the read section must be held) */
2288 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2290 DWORD res, read=0;
2292 if(req->read_size == sizeof(req->read_buf))
2293 return ERROR_SUCCESS;
2295 if(req->read_pos) {
2296 if(req->read_size)
2297 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2298 req->read_pos = 0;
2301 res = req->data_stream->vtbl->read(req->data_stream, req, req->read_buf+req->read_size,
2302 sizeof(req->read_buf)-req->read_size, &read, read_mode);
2303 req->read_size += read;
2305 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2306 if(read_bytes)
2307 *read_bytes = read;
2308 return res;
2311 /* return the size of data available to be read immediately (the read section must be held) */
2312 static DWORD get_avail_data( http_request_t *req )
2314 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2317 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2319 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2320 DWORD avail = 0;
2322 if(req->netconn)
2323 NETCON_query_data_available(req->netconn, &avail);
2324 return netconn_stream->content_length == ~0u
2325 ? avail
2326 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2329 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2331 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2332 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2335 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2336 DWORD *read, read_mode_t read_mode)
2338 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2339 int len = 0;
2341 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2343 if(read_mode == READMODE_NOBLOCK)
2344 size = min(size, netconn_get_avail_data(stream, req));
2346 if(size && req->netconn) {
2347 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2348 len = 0;
2349 if(!len)
2350 netconn_stream->content_length = netconn_stream->content_read;
2353 netconn_stream->content_read += *read = len;
2354 TRACE("read %u bytes\n", len);
2355 return ERROR_SUCCESS;
2358 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2360 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2361 BYTE buf[1024];
2362 DWORD avail;
2363 int len;
2365 if(netconn_end_of_data(stream, req))
2366 return TRUE;
2368 do {
2369 avail = netconn_get_avail_data(stream, req);
2370 if(!avail)
2371 return FALSE;
2373 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2374 return FALSE;
2376 netconn_stream->content_read += len;
2377 }while(netconn_stream->content_read < netconn_stream->content_length);
2379 return TRUE;
2382 static void netconn_destroy(data_stream_t *stream)
2386 static const data_stream_vtbl_t netconn_stream_vtbl = {
2387 netconn_get_avail_data,
2388 netconn_end_of_data,
2389 netconn_read,
2390 netconn_drain_content,
2391 netconn_destroy
2394 /* read some more data into the read buffer (the read section must be held) */
2395 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2397 DWORD res;
2398 int len;
2400 if (stream->buf_pos)
2402 /* move existing data to the start of the buffer */
2403 if(stream->buf_size)
2404 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2405 stream->buf_pos = 0;
2408 if (maxlen == -1) maxlen = sizeof(stream->buf);
2410 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2411 maxlen - stream->buf_size, 0, &len );
2412 if(res == ERROR_SUCCESS)
2413 stream->buf_size += len;
2415 return res;
2418 /* remove some amount of data from the read buffer (the read section must be held) */
2419 static void remove_chunked_data(chunked_stream_t *stream, int count)
2421 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2422 else stream->buf_pos += count;
2425 /* discard data contents until we reach end of line (the read section must be held) */
2426 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2428 DWORD res;
2432 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2433 if (eol)
2435 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2436 break;
2438 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2439 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2440 } while (stream->buf_size);
2441 return ERROR_SUCCESS;
2444 /* read the size of the next chunk (the read section must be held) */
2445 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2447 /* TODOO */
2448 DWORD chunk_size = 0, res;
2450 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2451 return res;
2453 for (;;)
2455 while (stream->buf_size)
2457 char ch = stream->buf[stream->buf_pos];
2458 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2459 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2460 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2461 else if (ch == ';' || ch == '\r' || ch == '\n')
2463 TRACE( "reading %u byte chunk\n", chunk_size );
2464 stream->chunk_size = chunk_size;
2465 req->contentLength += chunk_size;
2466 return discard_chunked_eol(stream, req);
2468 remove_chunked_data(stream, 1);
2470 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2471 if (!stream->buf_size)
2473 stream->chunk_size = 0;
2474 return ERROR_SUCCESS;
2479 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2481 /* Allow reading only from read buffer */
2482 return 0;
2485 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2487 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2488 return !chunked_stream->chunk_size;
2491 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2492 DWORD *read, read_mode_t read_mode)
2494 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2495 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2497 if(chunked_stream->chunk_size == ~0u) {
2498 res = start_next_chunk(chunked_stream, req);
2499 if(res != ERROR_SUCCESS)
2500 return res;
2503 while(size && chunked_stream->chunk_size) {
2504 if(chunked_stream->buf_size) {
2505 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2507 /* this could block */
2508 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2509 break;
2511 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2512 remove_chunked_data(chunked_stream, read_bytes);
2513 }else {
2514 read_bytes = min(size, chunked_stream->chunk_size);
2516 if(read_mode == READMODE_NOBLOCK) {
2517 DWORD avail;
2519 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2520 break;
2521 if(read_bytes > avail)
2522 read_bytes = avail;
2524 /* this could block */
2525 if(read_bytes == chunked_stream->chunk_size)
2526 break;
2529 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2530 if(res != ERROR_SUCCESS)
2531 break;
2534 chunked_stream->chunk_size -= read_bytes;
2535 size -= read_bytes;
2536 ret_read += read_bytes;
2537 if(!chunked_stream->chunk_size) {
2538 assert(read_mode != READMODE_NOBLOCK);
2539 res = start_next_chunk(chunked_stream, req);
2540 if(res != ERROR_SUCCESS)
2541 break;
2544 if(read_mode == READMODE_ASYNC)
2545 read_mode = READMODE_NOBLOCK;
2548 TRACE("read %u bytes\n", ret_read);
2549 *read = ret_read;
2550 return res;
2553 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2555 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2557 /* FIXME: we can do better */
2558 return !chunked_stream->chunk_size;
2561 static void chunked_destroy(data_stream_t *stream)
2563 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2564 heap_free(chunked_stream);
2567 static const data_stream_vtbl_t chunked_stream_vtbl = {
2568 chunked_get_avail_data,
2569 chunked_end_of_data,
2570 chunked_read,
2571 chunked_drain_content,
2572 chunked_destroy
2575 /* set the request content length based on the headers */
2576 static DWORD set_content_length(http_request_t *request, DWORD status_code)
2578 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2579 WCHAR encoding[20];
2580 DWORD size;
2582 if(status_code == HTTP_STATUS_NO_CONTENT) {
2583 request->contentLength = request->netconn_stream.content_length = 0;
2584 return ERROR_SUCCESS;
2587 size = sizeof(request->contentLength);
2588 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2589 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2590 request->contentLength = ~0u;
2591 request->netconn_stream.content_length = request->contentLength;
2592 request->netconn_stream.content_read = request->read_size;
2594 size = sizeof(encoding);
2595 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2596 !strcmpiW(encoding, szChunked))
2598 chunked_stream_t *chunked_stream;
2600 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2601 if(!chunked_stream)
2602 return ERROR_OUTOFMEMORY;
2604 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2605 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2606 chunked_stream->chunk_size = ~0u;
2608 if(request->read_size) {
2609 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2610 chunked_stream->buf_size = request->read_size;
2611 request->read_size = request->read_pos = 0;
2614 request->data_stream = &chunked_stream->data_stream;
2615 request->contentLength = ~0u;
2616 request->read_chunked = TRUE;
2619 if(request->decoding) {
2620 int encoding_idx;
2622 static const WCHAR gzipW[] = {'g','z','i','p',0};
2624 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2625 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2626 return init_gzip_stream(request);
2629 return ERROR_SUCCESS;
2632 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2634 INTERNET_ASYNC_RESULT iar;
2636 iar.dwResult = result;
2637 iar.dwError = error;
2639 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2640 sizeof(INTERNET_ASYNC_RESULT));
2643 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2645 DWORD res, read = 0, avail = 0;
2646 read_mode_t mode;
2648 TRACE("%p\n", req);
2650 EnterCriticalSection( &req->read_section );
2652 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2653 res = refill_read_buffer(req, mode, &read);
2654 if(res == ERROR_SUCCESS && !first_notif)
2655 avail = get_avail_data(req);
2657 LeaveCriticalSection( &req->read_section );
2659 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2660 WARN("res %u read %u, closing connection\n", res, read);
2661 http_release_netconn(req, FALSE);
2664 if(res == ERROR_SUCCESS)
2665 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2666 else
2667 send_request_complete(req, 0, res);
2670 /* read data from the http connection (the read section must be held) */
2671 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2673 DWORD current_read = 0, ret_read = 0;
2674 read_mode_t read_mode;
2675 DWORD res = ERROR_SUCCESS;
2677 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2679 EnterCriticalSection( &req->read_section );
2681 if(req->read_size) {
2682 ret_read = min(size, req->read_size);
2683 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2684 req->read_size -= ret_read;
2685 req->read_pos += ret_read;
2686 if(read_mode == READMODE_ASYNC)
2687 read_mode = READMODE_NOBLOCK;
2690 if(ret_read < size) {
2691 res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, read_mode);
2692 ret_read += current_read;
2695 LeaveCriticalSection( &req->read_section );
2697 *read = ret_read;
2698 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2700 if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
2701 BOOL res;
2702 DWORD written;
2704 res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
2705 if(!res)
2706 WARN("WriteFile failed: %u\n", GetLastError());
2709 if(size && !ret_read)
2710 http_release_netconn(req, res == ERROR_SUCCESS);
2712 return res;
2716 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2718 http_request_t *req = (http_request_t*)hdr;
2719 DWORD res;
2721 EnterCriticalSection( &req->read_section );
2722 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2723 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2725 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2726 if(res == ERROR_SUCCESS)
2727 res = hdr->dwError;
2728 LeaveCriticalSection( &req->read_section );
2730 return res;
2733 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2735 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2736 http_request_t *req = (http_request_t*)workRequest->hdr;
2737 DWORD res;
2739 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2741 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2742 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2744 send_request_complete(req, res == ERROR_SUCCESS, res);
2747 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2748 DWORD flags, DWORD_PTR context)
2750 http_request_t *req = (http_request_t*)hdr;
2751 DWORD res, size, read, error = ERROR_SUCCESS;
2753 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2754 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2756 if (buffers->dwStructSize != sizeof(*buffers))
2757 return ERROR_INVALID_PARAMETER;
2759 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2761 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2763 WORKREQUEST workRequest;
2765 if (TryEnterCriticalSection( &req->read_section ))
2767 if (get_avail_data(req))
2769 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2770 &buffers->dwBufferLength, FALSE);
2771 size = buffers->dwBufferLength;
2772 LeaveCriticalSection( &req->read_section );
2773 goto done;
2775 LeaveCriticalSection( &req->read_section );
2778 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2779 workRequest.hdr = WININET_AddRef(&req->hdr);
2780 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2782 INTERNET_AsyncCall(&workRequest);
2784 return ERROR_IO_PENDING;
2787 read = 0;
2788 size = buffers->dwBufferLength;
2790 EnterCriticalSection( &req->read_section );
2791 if(hdr->dwError == ERROR_SUCCESS)
2792 hdr->dwError = INTERNET_HANDLE_IN_USE;
2793 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2794 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2796 while(1) {
2797 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2798 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2799 if(res != ERROR_SUCCESS)
2800 break;
2802 read += buffers->dwBufferLength;
2803 if(read == size || end_of_read_data(req))
2804 break;
2806 LeaveCriticalSection( &req->read_section );
2808 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2809 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2810 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2811 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2813 EnterCriticalSection( &req->read_section );
2816 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2817 hdr->dwError = ERROR_SUCCESS;
2818 else
2819 error = hdr->dwError;
2821 LeaveCriticalSection( &req->read_section );
2822 size = buffers->dwBufferLength;
2823 buffers->dwBufferLength = read;
2825 done:
2826 if (res == ERROR_SUCCESS) {
2827 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2828 &size, sizeof(size));
2831 return res==ERROR_SUCCESS ? error : res;
2834 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2836 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2837 http_request_t *req = (http_request_t*)workRequest->hdr;
2838 DWORD res;
2840 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2842 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2843 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2845 send_request_complete(req, res == ERROR_SUCCESS, res);
2848 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2849 DWORD flags, DWORD_PTR context)
2852 http_request_t *req = (http_request_t*)hdr;
2853 DWORD res, size, read, error = ERROR_SUCCESS;
2855 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2856 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2858 if (buffers->dwStructSize != sizeof(*buffers))
2859 return ERROR_INVALID_PARAMETER;
2861 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2863 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2865 WORKREQUEST workRequest;
2867 if (TryEnterCriticalSection( &req->read_section ))
2869 if (get_avail_data(req))
2871 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2872 &buffers->dwBufferLength, FALSE);
2873 size = buffers->dwBufferLength;
2874 LeaveCriticalSection( &req->read_section );
2875 goto done;
2877 LeaveCriticalSection( &req->read_section );
2880 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2881 workRequest.hdr = WININET_AddRef(&req->hdr);
2882 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2884 INTERNET_AsyncCall(&workRequest);
2886 return ERROR_IO_PENDING;
2889 read = 0;
2890 size = buffers->dwBufferLength;
2892 EnterCriticalSection( &req->read_section );
2893 if(hdr->dwError == ERROR_SUCCESS)
2894 hdr->dwError = INTERNET_HANDLE_IN_USE;
2895 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2896 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2898 while(1) {
2899 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2900 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2901 if(res != ERROR_SUCCESS)
2902 break;
2904 read += buffers->dwBufferLength;
2905 if(read == size || end_of_read_data(req))
2906 break;
2908 LeaveCriticalSection( &req->read_section );
2910 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2911 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2912 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2913 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2915 EnterCriticalSection( &req->read_section );
2918 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2919 hdr->dwError = ERROR_SUCCESS;
2920 else
2921 error = hdr->dwError;
2923 LeaveCriticalSection( &req->read_section );
2924 size = buffers->dwBufferLength;
2925 buffers->dwBufferLength = read;
2927 done:
2928 if (res == ERROR_SUCCESS) {
2929 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2930 &size, sizeof(size));
2933 return res==ERROR_SUCCESS ? error : res;
2936 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2938 DWORD res;
2939 http_request_t *request = (http_request_t*)hdr;
2941 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2943 *written = 0;
2944 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
2945 if (res == ERROR_SUCCESS)
2946 request->bytesWritten += *written;
2948 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2949 return res;
2952 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2954 http_request_t *req = (http_request_t*)workRequest->hdr;
2956 HTTP_ReceiveRequestData(req, FALSE);
2959 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2961 http_request_t *req = (http_request_t*)hdr;
2963 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2965 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2967 WORKREQUEST workRequest;
2969 /* never wait, if we can't enter the section we queue an async request right away */
2970 if (TryEnterCriticalSection( &req->read_section ))
2972 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
2973 if ((*available = get_avail_data( req ))) goto done;
2974 if (end_of_read_data( req )) goto done;
2975 LeaveCriticalSection( &req->read_section );
2978 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2979 workRequest.hdr = WININET_AddRef( &req->hdr );
2981 INTERNET_AsyncCall(&workRequest);
2983 return ERROR_IO_PENDING;
2986 EnterCriticalSection( &req->read_section );
2988 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2990 refill_read_buffer( req, READMODE_ASYNC, NULL );
2991 *available = get_avail_data( req );
2994 done:
2995 LeaveCriticalSection( &req->read_section );
2997 TRACE( "returning %u\n", *available );
2998 return ERROR_SUCCESS;
3001 static const object_vtbl_t HTTPREQVtbl = {
3002 HTTPREQ_Destroy,
3003 HTTPREQ_CloseConnection,
3004 HTTPREQ_QueryOption,
3005 HTTPREQ_SetOption,
3006 HTTPREQ_ReadFile,
3007 HTTPREQ_ReadFileExA,
3008 HTTPREQ_ReadFileExW,
3009 HTTPREQ_WriteFile,
3010 HTTPREQ_QueryDataAvailable,
3011 NULL
3014 /***********************************************************************
3015 * HTTP_HttpOpenRequestW (internal)
3017 * Open a HTTP request handle
3019 * RETURNS
3020 * HINTERNET a HTTP request handle on success
3021 * NULL on failure
3024 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3025 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3026 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3027 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3029 appinfo_t *hIC = session->appInfo;
3030 http_request_t *request;
3031 DWORD len, res = ERROR_SUCCESS;
3033 TRACE("-->\n");
3035 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3036 if(!request)
3037 return ERROR_OUTOFMEMORY;
3039 request->hdr.htype = WH_HHTTPREQ;
3040 request->hdr.dwFlags = dwFlags;
3041 request->hdr.dwContext = dwContext;
3042 request->contentLength = ~0u;
3044 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3045 request->data_stream = &request->netconn_stream.data_stream;
3047 InitializeCriticalSection( &request->read_section );
3049 WININET_AddRef( &session->hdr );
3050 request->session = session;
3051 list_add_head( &session->hdr.children, &request->hdr.entry );
3053 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3054 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3055 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3056 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3058 if (lpszObjectName && *lpszObjectName) {
3059 HRESULT rc;
3061 len = 0;
3062 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3063 if (rc != E_POINTER)
3064 len = strlenW(lpszObjectName)+1;
3065 request->path = heap_alloc(len*sizeof(WCHAR));
3066 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3067 URL_ESCAPE_SPACES_ONLY);
3068 if (rc != S_OK)
3070 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3071 strcpyW(request->path,lpszObjectName);
3073 }else {
3074 static const WCHAR slashW[] = {'/',0};
3076 request->path = heap_strdupW(slashW);
3079 if (lpszReferrer && *lpszReferrer)
3080 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3082 if (lpszAcceptTypes)
3084 int i;
3085 for (i = 0; lpszAcceptTypes[i]; i++)
3087 if (!*lpszAcceptTypes[i]) continue;
3088 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3089 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3090 HTTP_ADDHDR_FLAG_REQ |
3091 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3095 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3096 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
3098 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
3099 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
3100 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
3102 WCHAR *host_name;
3104 static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
3106 host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
3107 if (!host_name) {
3108 res = ERROR_OUTOFMEMORY;
3109 goto lend;
3112 sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
3113 HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3114 heap_free(host_name);
3116 else
3117 HTTP_ProcessHeader(request, hostW, session->hostName,
3118 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3120 if (session->serverPort == INTERNET_INVALID_PORT_NUMBER)
3121 session->serverPort = (dwFlags & INTERNET_FLAG_SECURE ?
3122 INTERNET_DEFAULT_HTTPS_PORT :
3123 INTERNET_DEFAULT_HTTP_PORT);
3125 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3126 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3127 INTERNET_DEFAULT_HTTPS_PORT :
3128 INTERNET_DEFAULT_HTTP_PORT);
3130 if (hIC->proxy && hIC->proxy[0])
3131 HTTP_DealWithProxy( hIC, session, request );
3133 INTERNET_SendCallback(&session->hdr, dwContext,
3134 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3135 sizeof(HINTERNET));
3137 lend:
3138 TRACE("<-- %u (%p)\n", res, request);
3140 if(res != ERROR_SUCCESS) {
3141 WININET_Release( &request->hdr );
3142 *ret = NULL;
3143 return res;
3146 *ret = request->hdr.hInternet;
3147 return ERROR_SUCCESS;
3150 /***********************************************************************
3151 * HttpOpenRequestW (WININET.@)
3153 * Open a HTTP request handle
3155 * RETURNS
3156 * HINTERNET a HTTP request handle on success
3157 * NULL on failure
3160 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3161 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3162 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3163 DWORD dwFlags, DWORD_PTR dwContext)
3165 http_session_t *session;
3166 HINTERNET handle = NULL;
3167 DWORD res;
3169 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3170 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3171 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3172 dwFlags, dwContext);
3173 if(lpszAcceptTypes!=NULL)
3175 int i;
3176 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3177 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3180 session = (http_session_t*) get_handle_object( hHttpSession );
3181 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3183 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3184 goto lend;
3188 * My tests seem to show that the windows version does not
3189 * become asynchronous until after this point. And anyhow
3190 * if this call was asynchronous then how would you get the
3191 * necessary HINTERNET pointer returned by this function.
3194 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3195 lpszVersion, lpszReferrer, lpszAcceptTypes,
3196 dwFlags, dwContext, &handle);
3197 lend:
3198 if( session )
3199 WININET_Release( &session->hdr );
3200 TRACE("returning %p\n", handle);
3201 if(res != ERROR_SUCCESS)
3202 SetLastError(res);
3203 return handle;
3206 static const LPCWSTR header_lookup[] = {
3207 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3208 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3209 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3210 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3211 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3212 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3213 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3214 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3215 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3216 szDate, /* HTTP_QUERY_DATE = 9 */
3217 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3218 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3219 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3220 szURI, /* HTTP_QUERY_URI = 13 */
3221 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3222 NULL, /* HTTP_QUERY_COST = 15 */
3223 NULL, /* HTTP_QUERY_LINK = 16 */
3224 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3225 NULL, /* HTTP_QUERY_VERSION = 18 */
3226 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3227 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3228 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3229 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3230 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3231 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3232 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3233 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3234 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3235 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3236 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3237 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3238 NULL, /* HTTP_QUERY_FROM = 31 */
3239 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3240 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3241 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3242 szReferer, /* HTTP_QUERY_REFERER = 35 */
3243 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3244 szServer, /* HTTP_QUERY_SERVER = 37 */
3245 NULL, /* HTTP_TITLE = 38 */
3246 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3247 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3248 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3249 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3250 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3251 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3252 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3253 NULL, /* HTTP_QUERY_REFRESH = 46 */
3254 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3255 szAge, /* HTTP_QUERY_AGE = 48 */
3256 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3257 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3258 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3259 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3260 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3261 szETag, /* HTTP_QUERY_ETAG = 54 */
3262 hostW, /* HTTP_QUERY_HOST = 55 */
3263 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3264 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3265 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3266 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3267 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3268 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3269 szRange, /* HTTP_QUERY_RANGE = 62 */
3270 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3271 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3272 szVary, /* HTTP_QUERY_VARY = 65 */
3273 szVia, /* HTTP_QUERY_VIA = 66 */
3274 szWarning, /* HTTP_QUERY_WARNING = 67 */
3275 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3276 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3277 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3280 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3282 /***********************************************************************
3283 * HTTP_HttpQueryInfoW (internal)
3285 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3286 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3288 LPHTTPHEADERW lphttpHdr = NULL;
3289 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3290 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3291 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3292 INT index = -1;
3294 /* Find requested header structure */
3295 switch (level)
3297 case HTTP_QUERY_CUSTOM:
3298 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3299 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3300 break;
3301 case HTTP_QUERY_RAW_HEADERS_CRLF:
3303 LPWSTR headers;
3304 DWORD len = 0;
3305 DWORD res = ERROR_INVALID_PARAMETER;
3307 if (request_only)
3308 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3309 else
3310 headers = request->rawHeaders;
3312 if (headers)
3313 len = strlenW(headers) * sizeof(WCHAR);
3315 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3317 len += sizeof(WCHAR);
3318 res = ERROR_INSUFFICIENT_BUFFER;
3320 else if (lpBuffer)
3322 if (headers)
3323 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3324 else
3326 len = strlenW(szCrLf) * sizeof(WCHAR);
3327 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3329 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3330 res = ERROR_SUCCESS;
3332 *lpdwBufferLength = len;
3334 if (request_only) heap_free(headers);
3335 return res;
3337 case HTTP_QUERY_RAW_HEADERS:
3339 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3340 DWORD i, size = 0;
3341 LPWSTR pszString = lpBuffer;
3343 for (i = 0; ppszRawHeaderLines[i]; i++)
3344 size += strlenW(ppszRawHeaderLines[i]) + 1;
3346 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3348 HTTP_FreeTokens(ppszRawHeaderLines);
3349 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3350 return ERROR_INSUFFICIENT_BUFFER;
3352 if (pszString)
3354 for (i = 0; ppszRawHeaderLines[i]; i++)
3356 DWORD len = strlenW(ppszRawHeaderLines[i]);
3357 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3358 pszString += len+1;
3360 *pszString = '\0';
3361 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3363 *lpdwBufferLength = size * sizeof(WCHAR);
3364 HTTP_FreeTokens(ppszRawHeaderLines);
3366 return ERROR_SUCCESS;
3368 case HTTP_QUERY_STATUS_TEXT:
3369 if (request->statusText)
3371 DWORD len = strlenW(request->statusText);
3372 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3374 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3375 return ERROR_INSUFFICIENT_BUFFER;
3377 if (lpBuffer)
3379 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3380 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3382 *lpdwBufferLength = len * sizeof(WCHAR);
3383 return ERROR_SUCCESS;
3385 break;
3386 case HTTP_QUERY_VERSION:
3387 if (request->version)
3389 DWORD len = strlenW(request->version);
3390 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3392 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3393 return ERROR_INSUFFICIENT_BUFFER;
3395 if (lpBuffer)
3397 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3398 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3400 *lpdwBufferLength = len * sizeof(WCHAR);
3401 return ERROR_SUCCESS;
3403 break;
3404 case HTTP_QUERY_CONTENT_ENCODING:
3405 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3406 requested_index,request_only);
3407 break;
3408 default:
3409 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3411 if (level < LAST_TABLE_HEADER && header_lookup[level])
3412 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3413 requested_index,request_only);
3416 if (index >= 0)
3417 lphttpHdr = &request->custHeaders[index];
3419 /* Ensure header satisfies requested attributes */
3420 if (!lphttpHdr ||
3421 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3422 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3424 return ERROR_HTTP_HEADER_NOT_FOUND;
3427 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
3429 /* coalesce value to requested type */
3430 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3432 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3433 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3435 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3437 time_t tmpTime;
3438 struct tm tmpTM;
3439 SYSTEMTIME *STHook;
3441 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3443 tmpTM = *gmtime(&tmpTime);
3444 STHook = (SYSTEMTIME *)lpBuffer;
3445 STHook->wDay = tmpTM.tm_mday;
3446 STHook->wHour = tmpTM.tm_hour;
3447 STHook->wMilliseconds = 0;
3448 STHook->wMinute = tmpTM.tm_min;
3449 STHook->wDayOfWeek = tmpTM.tm_wday;
3450 STHook->wMonth = tmpTM.tm_mon + 1;
3451 STHook->wSecond = tmpTM.tm_sec;
3452 STHook->wYear = tmpTM.tm_year;
3454 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3455 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3456 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3458 else if (lphttpHdr->lpszValue)
3460 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3462 if (len > *lpdwBufferLength)
3464 *lpdwBufferLength = len;
3465 return ERROR_INSUFFICIENT_BUFFER;
3467 if (lpBuffer)
3469 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3470 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3472 *lpdwBufferLength = len - sizeof(WCHAR);
3474 return ERROR_SUCCESS;
3477 /***********************************************************************
3478 * HttpQueryInfoW (WININET.@)
3480 * Queries for information about an HTTP request
3482 * RETURNS
3483 * TRUE on success
3484 * FALSE on failure
3487 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3488 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3490 http_request_t *request;
3491 DWORD res;
3493 if (TRACE_ON(wininet)) {
3494 #define FE(x) { x, #x }
3495 static const wininet_flag_info query_flags[] = {
3496 FE(HTTP_QUERY_MIME_VERSION),
3497 FE(HTTP_QUERY_CONTENT_TYPE),
3498 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3499 FE(HTTP_QUERY_CONTENT_ID),
3500 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3501 FE(HTTP_QUERY_CONTENT_LENGTH),
3502 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3503 FE(HTTP_QUERY_ALLOW),
3504 FE(HTTP_QUERY_PUBLIC),
3505 FE(HTTP_QUERY_DATE),
3506 FE(HTTP_QUERY_EXPIRES),
3507 FE(HTTP_QUERY_LAST_MODIFIED),
3508 FE(HTTP_QUERY_MESSAGE_ID),
3509 FE(HTTP_QUERY_URI),
3510 FE(HTTP_QUERY_DERIVED_FROM),
3511 FE(HTTP_QUERY_COST),
3512 FE(HTTP_QUERY_LINK),
3513 FE(HTTP_QUERY_PRAGMA),
3514 FE(HTTP_QUERY_VERSION),
3515 FE(HTTP_QUERY_STATUS_CODE),
3516 FE(HTTP_QUERY_STATUS_TEXT),
3517 FE(HTTP_QUERY_RAW_HEADERS),
3518 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3519 FE(HTTP_QUERY_CONNECTION),
3520 FE(HTTP_QUERY_ACCEPT),
3521 FE(HTTP_QUERY_ACCEPT_CHARSET),
3522 FE(HTTP_QUERY_ACCEPT_ENCODING),
3523 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3524 FE(HTTP_QUERY_AUTHORIZATION),
3525 FE(HTTP_QUERY_CONTENT_ENCODING),
3526 FE(HTTP_QUERY_FORWARDED),
3527 FE(HTTP_QUERY_FROM),
3528 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3529 FE(HTTP_QUERY_LOCATION),
3530 FE(HTTP_QUERY_ORIG_URI),
3531 FE(HTTP_QUERY_REFERER),
3532 FE(HTTP_QUERY_RETRY_AFTER),
3533 FE(HTTP_QUERY_SERVER),
3534 FE(HTTP_QUERY_TITLE),
3535 FE(HTTP_QUERY_USER_AGENT),
3536 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3537 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3538 FE(HTTP_QUERY_ACCEPT_RANGES),
3539 FE(HTTP_QUERY_SET_COOKIE),
3540 FE(HTTP_QUERY_COOKIE),
3541 FE(HTTP_QUERY_REQUEST_METHOD),
3542 FE(HTTP_QUERY_REFRESH),
3543 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3544 FE(HTTP_QUERY_AGE),
3545 FE(HTTP_QUERY_CACHE_CONTROL),
3546 FE(HTTP_QUERY_CONTENT_BASE),
3547 FE(HTTP_QUERY_CONTENT_LOCATION),
3548 FE(HTTP_QUERY_CONTENT_MD5),
3549 FE(HTTP_QUERY_CONTENT_RANGE),
3550 FE(HTTP_QUERY_ETAG),
3551 FE(HTTP_QUERY_HOST),
3552 FE(HTTP_QUERY_IF_MATCH),
3553 FE(HTTP_QUERY_IF_NONE_MATCH),
3554 FE(HTTP_QUERY_IF_RANGE),
3555 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3556 FE(HTTP_QUERY_MAX_FORWARDS),
3557 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3558 FE(HTTP_QUERY_RANGE),
3559 FE(HTTP_QUERY_TRANSFER_ENCODING),
3560 FE(HTTP_QUERY_UPGRADE),
3561 FE(HTTP_QUERY_VARY),
3562 FE(HTTP_QUERY_VIA),
3563 FE(HTTP_QUERY_WARNING),
3564 FE(HTTP_QUERY_CUSTOM)
3566 static const wininet_flag_info modifier_flags[] = {
3567 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3568 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3569 FE(HTTP_QUERY_FLAG_NUMBER),
3570 FE(HTTP_QUERY_FLAG_COALESCE)
3572 #undef FE
3573 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3574 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3575 DWORD i;
3577 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3578 TRACE(" Attribute:");
3579 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3580 if (query_flags[i].val == info) {
3581 TRACE(" %s", query_flags[i].name);
3582 break;
3585 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3586 TRACE(" Unknown (%08x)", info);
3589 TRACE(" Modifier:");
3590 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3591 if (modifier_flags[i].val & info_mod) {
3592 TRACE(" %s", modifier_flags[i].name);
3593 info_mod &= ~ modifier_flags[i].val;
3597 if (info_mod) {
3598 TRACE(" Unknown (%08x)", info_mod);
3600 TRACE("\n");
3603 request = (http_request_t*) get_handle_object( hHttpRequest );
3604 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3606 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3607 goto lend;
3610 if (lpBuffer == NULL)
3611 *lpdwBufferLength = 0;
3612 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3613 lpBuffer, lpdwBufferLength, lpdwIndex);
3615 lend:
3616 if( request )
3617 WININET_Release( &request->hdr );
3619 TRACE("%u <--\n", res);
3620 if(res != ERROR_SUCCESS)
3621 SetLastError(res);
3622 return res == ERROR_SUCCESS;
3625 /***********************************************************************
3626 * HttpQueryInfoA (WININET.@)
3628 * Queries for information about an HTTP request
3630 * RETURNS
3631 * TRUE on success
3632 * FALSE on failure
3635 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3636 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3638 BOOL result;
3639 DWORD len;
3640 WCHAR* bufferW;
3642 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3643 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3645 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3646 lpdwBufferLength, lpdwIndex );
3649 if (lpBuffer)
3651 DWORD alloclen;
3652 len = (*lpdwBufferLength)*sizeof(WCHAR);
3653 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3655 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3656 if (alloclen < len)
3657 alloclen = len;
3659 else
3660 alloclen = len;
3661 bufferW = heap_alloc(alloclen);
3662 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3663 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3664 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3665 } else
3667 bufferW = NULL;
3668 len = 0;
3671 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3672 &len, lpdwIndex );
3673 if( result )
3675 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3676 lpBuffer, *lpdwBufferLength, NULL, NULL );
3677 *lpdwBufferLength = len - 1;
3679 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3681 else
3682 /* since the strings being returned from HttpQueryInfoW should be
3683 * only ASCII characters, it is reasonable to assume that all of
3684 * the Unicode characters can be reduced to a single byte */
3685 *lpdwBufferLength = len / sizeof(WCHAR);
3687 heap_free( bufferW );
3688 return result;
3691 /***********************************************************************
3692 * HTTP_GetRedirectURL (internal)
3694 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3696 static WCHAR szHttp[] = {'h','t','t','p',0};
3697 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3698 http_session_t *session = request->session;
3699 URL_COMPONENTSW urlComponents;
3700 DWORD url_length = 0;
3701 LPWSTR orig_url;
3702 LPWSTR combined_url;
3704 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3705 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3706 urlComponents.dwSchemeLength = 0;
3707 urlComponents.lpszHostName = session->hostName;
3708 urlComponents.dwHostNameLength = 0;
3709 urlComponents.nPort = session->hostPort;
3710 urlComponents.lpszUserName = session->userName;
3711 urlComponents.dwUserNameLength = 0;
3712 urlComponents.lpszPassword = NULL;
3713 urlComponents.dwPasswordLength = 0;
3714 urlComponents.lpszUrlPath = request->path;
3715 urlComponents.dwUrlPathLength = 0;
3716 urlComponents.lpszExtraInfo = NULL;
3717 urlComponents.dwExtraInfoLength = 0;
3719 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3720 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3721 return NULL;
3723 orig_url = heap_alloc(url_length);
3725 /* convert from bytes to characters */
3726 url_length = url_length / sizeof(WCHAR) - 1;
3727 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3729 heap_free(orig_url);
3730 return NULL;
3733 url_length = 0;
3734 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3735 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3737 heap_free(orig_url);
3738 return NULL;
3740 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3742 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3744 heap_free(orig_url);
3745 heap_free(combined_url);
3746 return NULL;
3748 heap_free(orig_url);
3749 return combined_url;
3753 /***********************************************************************
3754 * HTTP_HandleRedirect (internal)
3756 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3758 http_session_t *session = request->session;
3759 appinfo_t *hIC = session->appInfo;
3760 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3761 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3762 int index;
3764 if(lpszUrl[0]=='/')
3766 /* if it's an absolute path, keep the same session info */
3767 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3769 else
3771 URL_COMPONENTSW urlComponents;
3772 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3773 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3774 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3775 static WCHAR szHttp[] = {'h','t','t','p',0};
3776 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3778 userName[0] = 0;
3779 hostName[0] = 0;
3780 protocol[0] = 0;
3782 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3783 urlComponents.lpszScheme = protocol;
3784 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3785 urlComponents.lpszHostName = hostName;
3786 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3787 urlComponents.lpszUserName = userName;
3788 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3789 urlComponents.lpszPassword = NULL;
3790 urlComponents.dwPasswordLength = 0;
3791 urlComponents.lpszUrlPath = path;
3792 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3793 urlComponents.lpszExtraInfo = NULL;
3794 urlComponents.dwExtraInfoLength = 0;
3795 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3796 return INTERNET_GetLastError();
3798 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3799 (request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3801 TRACE("redirect from secure page to non-secure page\n");
3802 /* FIXME: warn about from secure redirect to non-secure page */
3803 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3805 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3806 !(request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3808 TRACE("redirect from non-secure page to secure page\n");
3809 /* FIXME: notify about redirect to secure page */
3810 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3813 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3815 if (lstrlenW(protocol)>4) /*https*/
3816 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3817 else /*http*/
3818 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3821 #if 0
3823 * This upsets redirects to binary files on sourceforge.net
3824 * and gives an html page instead of the target file
3825 * Examination of the HTTP request sent by native wininet.dll
3826 * reveals that it doesn't send a referrer in that case.
3827 * Maybe there's a flag that enables this, or maybe a referrer
3828 * shouldn't be added in case of a redirect.
3831 /* consider the current host as the referrer */
3832 if (session->lpszServerName && *session->lpszServerName)
3833 HTTP_ProcessHeader(request, HTTP_REFERER, session->lpszServerName,
3834 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3835 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3836 #endif
3838 heap_free(session->hostName);
3839 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3840 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3842 int len;
3843 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3844 len = lstrlenW(hostName);
3845 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3846 session->hostName = heap_alloc(len*sizeof(WCHAR));
3847 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3849 else
3850 session->hostName = heap_strdupW(hostName);
3852 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3854 heap_free(session->userName);
3855 session->userName = NULL;
3856 if (userName[0])
3857 session->userName = heap_strdupW(userName);
3859 reset_data_stream(request);
3861 if(!using_proxy) {
3862 if(strcmpiW(session->serverName, hostName)) {
3863 heap_free(session->serverName);
3864 session->serverName = heap_strdupW(hostName);
3866 session->serverPort = urlComponents.nPort;
3869 heap_free(request->path);
3870 request->path=NULL;
3871 if (*path)
3873 DWORD needed = 0;
3874 HRESULT rc;
3876 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3877 if (rc != E_POINTER)
3878 needed = strlenW(path)+1;
3879 request->path = heap_alloc(needed*sizeof(WCHAR));
3880 rc = UrlEscapeW(path, request->path, &needed,
3881 URL_ESCAPE_SPACES_ONLY);
3882 if (rc != S_OK)
3884 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3885 strcpyW(request->path,path);
3889 /* Remove custom content-type/length headers on redirects. */
3890 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3891 if (0 <= index)
3892 HTTP_DeleteCustomHeader(request, index);
3893 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3894 if (0 <= index)
3895 HTTP_DeleteCustomHeader(request, index);
3897 return ERROR_SUCCESS;
3900 /***********************************************************************
3901 * HTTP_build_req (internal)
3903 * concatenate all the strings in the request together
3905 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3907 LPCWSTR *t;
3908 LPWSTR str;
3910 for( t = list; *t ; t++ )
3911 len += strlenW( *t );
3912 len++;
3914 str = heap_alloc(len*sizeof(WCHAR));
3915 *str = 0;
3917 for( t = list; *t ; t++ )
3918 strcatW( str, *t );
3920 return str;
3923 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3925 LPWSTR lpszPath;
3926 LPWSTR requestString;
3927 INT len;
3928 INT cnt;
3929 INT responseLen;
3930 char *ascii_req;
3931 DWORD res;
3932 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3933 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
3934 http_session_t *session = request->session;
3936 TRACE("\n");
3938 lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
3939 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
3940 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
3941 heap_free( lpszPath );
3943 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3944 NULL, 0, NULL, NULL );
3945 len--; /* the nul terminator isn't needed */
3946 ascii_req = heap_alloc(len);
3947 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
3948 heap_free( requestString );
3950 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3952 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
3953 heap_free( ascii_req );
3954 if (res != ERROR_SUCCESS)
3955 return res;
3957 responseLen = HTTP_GetResponseHeaders( request, TRUE );
3958 if (!responseLen)
3959 return ERROR_HTTP_INVALID_HEADER;
3961 return ERROR_SUCCESS;
3964 static void HTTP_InsertCookies(http_request_t *request)
3966 DWORD cookie_size, size, cnt = 0;
3967 HTTPHEADERW *host;
3968 WCHAR *cookies;
3970 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
3972 host = HTTP_GetHeader(request, hostW);
3973 if(!host)
3974 return;
3976 if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
3977 return;
3979 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
3980 if(!(cookies = heap_alloc(size)))
3981 return;
3983 cnt += sprintfW(cookies, cookieW);
3984 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
3985 strcatW(cookies, szCrLf);
3987 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
3989 heap_free(cookies);
3992 static WORD HTTP_ParseWkday(LPCWSTR day)
3994 static const WCHAR days[7][4] = {{ 's','u','n',0 },
3995 { 'm','o','n',0 },
3996 { 't','u','e',0 },
3997 { 'w','e','d',0 },
3998 { 't','h','u',0 },
3999 { 'f','r','i',0 },
4000 { 's','a','t',0 }};
4001 int i;
4002 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4003 if (!strcmpiW(day, days[i]))
4004 return i;
4006 /* Invalid */
4007 return 7;
4010 static WORD HTTP_ParseMonth(LPCWSTR month)
4012 static const WCHAR jan[] = { 'j','a','n',0 };
4013 static const WCHAR feb[] = { 'f','e','b',0 };
4014 static const WCHAR mar[] = { 'm','a','r',0 };
4015 static const WCHAR apr[] = { 'a','p','r',0 };
4016 static const WCHAR may[] = { 'm','a','y',0 };
4017 static const WCHAR jun[] = { 'j','u','n',0 };
4018 static const WCHAR jul[] = { 'j','u','l',0 };
4019 static const WCHAR aug[] = { 'a','u','g',0 };
4020 static const WCHAR sep[] = { 's','e','p',0 };
4021 static const WCHAR oct[] = { 'o','c','t',0 };
4022 static const WCHAR nov[] = { 'n','o','v',0 };
4023 static const WCHAR dec[] = { 'd','e','c',0 };
4025 if (!strcmpiW(month, jan)) return 1;
4026 if (!strcmpiW(month, feb)) return 2;
4027 if (!strcmpiW(month, mar)) return 3;
4028 if (!strcmpiW(month, apr)) return 4;
4029 if (!strcmpiW(month, may)) return 5;
4030 if (!strcmpiW(month, jun)) return 6;
4031 if (!strcmpiW(month, jul)) return 7;
4032 if (!strcmpiW(month, aug)) return 8;
4033 if (!strcmpiW(month, sep)) return 9;
4034 if (!strcmpiW(month, oct)) return 10;
4035 if (!strcmpiW(month, nov)) return 11;
4036 if (!strcmpiW(month, dec)) return 12;
4037 /* Invalid */
4038 return 0;
4041 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4042 * optionally preceded by whitespace.
4043 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4044 * st, and sets *str to the first character after the time format.
4046 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4048 LPCWSTR ptr = *str;
4049 WCHAR *nextPtr;
4050 unsigned long num;
4052 while (isspaceW(*ptr))
4053 ptr++;
4055 num = strtoulW(ptr, &nextPtr, 10);
4056 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4058 ERR("unexpected time format %s\n", debugstr_w(ptr));
4059 return FALSE;
4061 if (num > 23)
4063 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4064 return FALSE;
4066 ptr = nextPtr + 1;
4067 st->wHour = (WORD)num;
4068 num = strtoulW(ptr, &nextPtr, 10);
4069 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4071 ERR("unexpected time format %s\n", debugstr_w(ptr));
4072 return FALSE;
4074 if (num > 59)
4076 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4077 return FALSE;
4079 ptr = nextPtr + 1;
4080 st->wMinute = (WORD)num;
4081 num = strtoulW(ptr, &nextPtr, 10);
4082 if (!nextPtr || nextPtr <= ptr)
4084 ERR("unexpected time format %s\n", debugstr_w(ptr));
4085 return FALSE;
4087 if (num > 59)
4089 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4090 return FALSE;
4092 ptr = nextPtr + 1;
4093 *str = ptr;
4094 st->wSecond = (WORD)num;
4095 return TRUE;
4098 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4100 static const WCHAR gmt[]= { 'G','M','T',0 };
4101 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4102 LPCWSTR ptr;
4103 SYSTEMTIME st = { 0 };
4104 unsigned long num;
4106 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4107 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4108 *dayPtr = *ptr;
4109 *dayPtr = 0;
4110 st.wDayOfWeek = HTTP_ParseWkday(day);
4111 if (st.wDayOfWeek >= 7)
4113 ERR("unexpected weekday %s\n", debugstr_w(day));
4114 return FALSE;
4117 while (isspaceW(*ptr))
4118 ptr++;
4120 for (monthPtr = month; !isspace(*ptr) &&
4121 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4122 monthPtr++, ptr++)
4123 *monthPtr = *ptr;
4124 *monthPtr = 0;
4125 st.wMonth = HTTP_ParseMonth(month);
4126 if (!st.wMonth || st.wMonth > 12)
4128 ERR("unexpected month %s\n", debugstr_w(month));
4129 return FALSE;
4132 while (isspaceW(*ptr))
4133 ptr++;
4135 num = strtoulW(ptr, &nextPtr, 10);
4136 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4138 ERR("unexpected day %s\n", debugstr_w(ptr));
4139 return FALSE;
4141 ptr = nextPtr;
4142 st.wDay = (WORD)num;
4144 while (isspaceW(*ptr))
4145 ptr++;
4147 if (!HTTP_ParseTime(&st, &ptr))
4148 return FALSE;
4150 while (isspaceW(*ptr))
4151 ptr++;
4153 num = strtoulW(ptr, &nextPtr, 10);
4154 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4156 ERR("unexpected year %s\n", debugstr_w(ptr));
4157 return FALSE;
4159 ptr = nextPtr;
4160 st.wYear = (WORD)num;
4162 while (isspaceW(*ptr))
4163 ptr++;
4165 /* asctime() doesn't report a timezone, but some web servers do, so accept
4166 * with or without GMT.
4168 if (*ptr && strcmpW(ptr, gmt))
4170 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4171 return FALSE;
4173 return SystemTimeToFileTime(&st, ft);
4176 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4178 static const WCHAR gmt[]= { 'G','M','T',0 };
4179 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4180 LPCWSTR ptr;
4181 unsigned long num;
4182 SYSTEMTIME st = { 0 };
4184 ptr = strchrW(value, ',');
4185 if (!ptr)
4186 return FALSE;
4187 if (ptr - value != 3)
4189 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4190 return FALSE;
4192 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4193 day[3] = 0;
4194 st.wDayOfWeek = HTTP_ParseWkday(day);
4195 if (st.wDayOfWeek > 6)
4197 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4198 return FALSE;
4200 ptr++;
4202 while (isspaceW(*ptr))
4203 ptr++;
4205 num = strtoulW(ptr, &nextPtr, 10);
4206 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4208 WARN("unexpected day %s\n", debugstr_w(value));
4209 return FALSE;
4211 ptr = nextPtr;
4212 st.wDay = (WORD)num;
4214 while (isspaceW(*ptr))
4215 ptr++;
4217 for (monthPtr = month; !isspace(*ptr) &&
4218 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4219 monthPtr++, ptr++)
4220 *monthPtr = *ptr;
4221 *monthPtr = 0;
4222 st.wMonth = HTTP_ParseMonth(month);
4223 if (!st.wMonth || st.wMonth > 12)
4225 WARN("unexpected month %s\n", debugstr_w(month));
4226 return FALSE;
4229 while (isspaceW(*ptr))
4230 ptr++;
4232 num = strtoulW(ptr, &nextPtr, 10);
4233 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4235 ERR("unexpected year %s\n", debugstr_w(value));
4236 return FALSE;
4238 ptr = nextPtr;
4239 st.wYear = (WORD)num;
4241 if (!HTTP_ParseTime(&st, &ptr))
4242 return FALSE;
4244 while (isspaceW(*ptr))
4245 ptr++;
4247 if (strcmpW(ptr, gmt))
4249 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4250 return FALSE;
4252 return SystemTimeToFileTime(&st, ft);
4255 static WORD HTTP_ParseWeekday(LPCWSTR day)
4257 static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4258 { 'm','o','n','d','a','y',0 },
4259 { 't','u','e','s','d','a','y',0 },
4260 { 'w','e','d','n','e','s','d','a','y',0 },
4261 { 't','h','u','r','s','d','a','y',0 },
4262 { 'f','r','i','d','a','y',0 },
4263 { 's','a','t','u','r','d','a','y',0 }};
4264 int i;
4265 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4266 if (!strcmpiW(day, days[i]))
4267 return i;
4269 /* Invalid */
4270 return 7;
4273 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4275 static const WCHAR gmt[]= { 'G','M','T',0 };
4276 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4277 LPCWSTR ptr;
4278 unsigned long num;
4279 SYSTEMTIME st = { 0 };
4281 ptr = strchrW(value, ',');
4282 if (!ptr)
4283 return FALSE;
4284 if (ptr - value == 3)
4286 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4287 day[3] = 0;
4288 st.wDayOfWeek = HTTP_ParseWkday(day);
4289 if (st.wDayOfWeek > 6)
4291 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4292 return FALSE;
4295 else if (ptr - value < sizeof(day) / sizeof(day[0]))
4297 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4298 day[ptr - value + 1] = 0;
4299 st.wDayOfWeek = HTTP_ParseWeekday(day);
4300 if (st.wDayOfWeek > 6)
4302 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4303 return FALSE;
4306 else
4308 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4309 return FALSE;
4311 ptr++;
4313 while (isspaceW(*ptr))
4314 ptr++;
4316 num = strtoulW(ptr, &nextPtr, 10);
4317 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4319 ERR("unexpected day %s\n", debugstr_w(value));
4320 return FALSE;
4322 ptr = nextPtr;
4323 st.wDay = (WORD)num;
4325 if (*ptr != '-')
4327 ERR("unexpected month format %s\n", debugstr_w(ptr));
4328 return FALSE;
4330 ptr++;
4332 for (monthPtr = month; *ptr != '-' &&
4333 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4334 monthPtr++, ptr++)
4335 *monthPtr = *ptr;
4336 *monthPtr = 0;
4337 st.wMonth = HTTP_ParseMonth(month);
4338 if (!st.wMonth || st.wMonth > 12)
4340 ERR("unexpected month %s\n", debugstr_w(month));
4341 return FALSE;
4344 if (*ptr != '-')
4346 ERR("unexpected year format %s\n", debugstr_w(ptr));
4347 return FALSE;
4349 ptr++;
4351 num = strtoulW(ptr, &nextPtr, 10);
4352 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4354 ERR("unexpected year %s\n", debugstr_w(value));
4355 return FALSE;
4357 ptr = nextPtr;
4358 st.wYear = (WORD)num;
4360 if (!HTTP_ParseTime(&st, &ptr))
4361 return FALSE;
4363 while (isspaceW(*ptr))
4364 ptr++;
4366 if (strcmpW(ptr, gmt))
4368 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4369 return FALSE;
4371 return SystemTimeToFileTime(&st, ft);
4374 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4376 static const WCHAR zero[] = { '0',0 };
4377 BOOL ret;
4379 if (!strcmpW(value, zero))
4381 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4382 ret = TRUE;
4384 else if (strchrW(value, ','))
4386 ret = HTTP_ParseRfc1123Date(value, ft);
4387 if (!ret)
4389 ret = HTTP_ParseRfc850Date(value, ft);
4390 if (!ret)
4391 ERR("unexpected date format %s\n", debugstr_w(value));
4394 else
4396 ret = HTTP_ParseDateAsAsctime(value, ft);
4397 if (!ret)
4398 ERR("unexpected date format %s\n", debugstr_w(value));
4400 return ret;
4403 static void HTTP_ProcessExpires(http_request_t *request)
4405 BOOL expirationFound = FALSE;
4406 int headerIndex;
4408 /* Look for a Cache-Control header with a max-age directive, as it takes
4409 * precedence over the Expires header.
4411 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4412 if (headerIndex != -1)
4414 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4415 LPWSTR ptr;
4417 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4419 LPWSTR comma = strchrW(ptr, ','), end, equal;
4421 if (comma)
4422 end = comma;
4423 else
4424 end = ptr + strlenW(ptr);
4425 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4427 if (*equal == '=')
4429 static const WCHAR max_age[] = {
4430 'm','a','x','-','a','g','e',0 };
4432 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4434 LPWSTR nextPtr;
4435 unsigned long age;
4437 age = strtoulW(equal + 1, &nextPtr, 10);
4438 if (nextPtr > equal + 1)
4440 LARGE_INTEGER ft;
4442 NtQuerySystemTime( &ft );
4443 /* Age is in seconds, FILETIME resolution is in
4444 * 100 nanosecond intervals.
4446 ft.QuadPart += age * (ULONGLONG)1000000;
4447 request->expires.dwLowDateTime = ft.u.LowPart;
4448 request->expires.dwHighDateTime = ft.u.HighPart;
4449 expirationFound = TRUE;
4453 if (comma)
4455 ptr = comma + 1;
4456 while (isspaceW(*ptr))
4457 ptr++;
4459 else
4460 ptr = NULL;
4463 if (!expirationFound)
4465 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4466 if (headerIndex != -1)
4468 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4469 FILETIME ft;
4471 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4473 expirationFound = TRUE;
4474 request->expires = ft;
4478 if (!expirationFound)
4480 LARGE_INTEGER t;
4482 /* With no known age, default to 10 minutes until expiration. */
4483 NtQuerySystemTime( &t );
4484 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4485 request->expires.dwLowDateTime = t.u.LowPart;
4486 request->expires.dwHighDateTime = t.u.HighPart;
4490 static void HTTP_ProcessLastModified(http_request_t *request)
4492 int headerIndex;
4494 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4495 if (headerIndex != -1)
4497 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4498 FILETIME ft;
4500 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4501 request->last_modified = ft;
4505 static void http_process_keep_alive(http_request_t *req)
4507 int index;
4509 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4510 if(index != -1)
4511 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4512 else
4513 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4516 static void HTTP_CacheRequest(http_request_t *request)
4518 WCHAR url[INTERNET_MAX_URL_LENGTH];
4519 WCHAR cacheFileName[MAX_PATH+1];
4520 BOOL b;
4522 b = HTTP_GetRequestURL(request, url);
4523 if(!b) {
4524 WARN("Could not get URL\n");
4525 return;
4528 b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4529 if(b) {
4530 heap_free(request->cacheFile);
4531 CloseHandle(request->hCacheFile);
4533 request->cacheFile = heap_strdupW(cacheFileName);
4534 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4535 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4536 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4537 WARN("Could not create file: %u\n", GetLastError());
4538 request->hCacheFile = NULL;
4540 }else {
4541 WARN("Could not create cache entry: %08x\n", GetLastError());
4545 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4547 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4548 http_session_t *session = request->session;
4549 netconn_t *netconn = NULL;
4550 server_t *server;
4551 DWORD res;
4553 assert(!request->netconn);
4554 reset_data_stream(request);
4556 server = get_server(session->serverName, session->serverPort);
4557 if(!server)
4558 return ERROR_OUTOFMEMORY;
4560 res = HTTP_ResolveName(request, server);
4561 if(res != ERROR_SUCCESS) {
4562 server_release(server);
4563 return res;
4566 EnterCriticalSection(&connection_pool_cs);
4568 while(!list_empty(&server->conn_pool)) {
4569 netconn = LIST_ENTRY(list_head(&server->conn_pool), netconn_t, pool_entry);
4570 list_remove(&netconn->pool_entry);
4572 if(NETCON_is_alive(netconn))
4573 break;
4575 TRACE("connection %p closed during idle\n", netconn);
4576 free_netconn(netconn);
4577 netconn = NULL;
4580 LeaveCriticalSection(&connection_pool_cs);
4582 if(netconn) {
4583 TRACE("<-- reusing %p netconn\n", netconn);
4584 request->netconn = netconn;
4585 *reusing = TRUE;
4586 return ERROR_SUCCESS;
4589 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4590 INTERNET_STATUS_CONNECTING_TO_SERVER,
4591 server->addr_str,
4592 strlen(server->addr_str)+1);
4594 res = create_netconn(is_https, server, request->security_flags, &netconn);
4595 server_release(server);
4596 if(res != ERROR_SUCCESS) {
4597 ERR("create_netconn failed: %u\n", res);
4598 return res;
4601 request->netconn = netconn;
4603 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4604 INTERNET_STATUS_CONNECTED_TO_SERVER,
4605 server->addr_str, strlen(server->addr_str)+1);
4607 if(is_https) {
4608 /* Note: we differ from Microsoft's WinINet here. they seem to have
4609 * a bug that causes no status callbacks to be sent when starting
4610 * a tunnel to a proxy server using the CONNECT verb. i believe our
4611 * behaviour to be more correct and to not cause any incompatibilities
4612 * because using a secure connection through a proxy server is a rare
4613 * case that would be hard for anyone to depend on */
4614 if(session->appInfo->proxy)
4615 res = HTTP_SecureProxyConnect(request);
4616 if(res == ERROR_SUCCESS)
4617 res = NETCON_secure_connect(request->netconn, session->hostName);
4618 if(res != ERROR_SUCCESS)
4620 WARN("Couldn't connect securely to host\n");
4622 if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4623 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4624 || res == ERROR_INTERNET_INVALID_CA
4625 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4626 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4627 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4628 || res == ERROR_INTERNET_SEC_INVALID_CERT
4629 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4630 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4634 if(res != ERROR_SUCCESS) {
4635 http_release_netconn(request, FALSE);
4636 return res;
4639 *reusing = FALSE;
4640 TRACE("Created connection to %s: %p\n", debugstr_w(server->name), netconn);
4641 return ERROR_SUCCESS;
4644 /***********************************************************************
4645 * HTTP_HttpSendRequestW (internal)
4647 * Sends the specified request to the HTTP server
4649 * RETURNS
4650 * ERROR_SUCCESS on success
4651 * win32 error code on failure
4654 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4655 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4656 DWORD dwContentLength, BOOL bEndRequest)
4658 INT cnt;
4659 BOOL redirected = FALSE;
4660 LPWSTR requestString = NULL;
4661 INT responseLen;
4662 BOOL loop_next;
4663 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4664 static const WCHAR szContentLength[] =
4665 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4666 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4667 DWORD res;
4669 TRACE("--> %p\n", request);
4671 assert(request->hdr.htype == WH_HHTTPREQ);
4673 /* if the verb is NULL default to GET */
4674 if (!request->verb)
4675 request->verb = heap_strdupW(szGET);
4677 if (dwContentLength || strcmpW(request->verb, szGET))
4679 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4680 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4681 request->bytesToWrite = dwContentLength;
4683 if (request->session->appInfo->agent)
4685 WCHAR *agent_header;
4686 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4687 int len;
4689 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4690 agent_header = heap_alloc(len * sizeof(WCHAR));
4691 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4693 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4694 heap_free(agent_header);
4696 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4698 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4699 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4701 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4703 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4704 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4705 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4710 DWORD len;
4711 BOOL reusing_connection;
4712 char *ascii_req;
4714 loop_next = FALSE;
4716 /* like native, just in case the caller forgot to call InternetReadFile
4717 * for all the data */
4718 drain_content(request);
4719 if(redirected) {
4720 request->contentLength = ~0u;
4721 request->bytesToWrite = 0;
4724 if (TRACE_ON(wininet))
4726 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4727 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4730 HTTP_FixURL(request);
4731 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4733 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4735 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4736 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4738 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4739 HTTP_InsertCookies(request);
4741 /* add the headers the caller supplied */
4742 if( lpszHeaders && dwHeaderLength )
4744 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength,
4745 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4748 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4750 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4751 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4752 heap_free(url);
4754 else
4755 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4758 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4760 if ((res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4761 break;
4763 /* send the request as ASCII, tack on the optional data */
4764 if (!lpOptional || redirected)
4765 dwOptionalLength = 0;
4766 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4767 NULL, 0, NULL, NULL );
4768 ascii_req = heap_alloc(len + dwOptionalLength);
4769 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4770 ascii_req, len, NULL, NULL );
4771 if( lpOptional )
4772 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4773 len = (len + dwOptionalLength - 1);
4774 ascii_req[len] = 0;
4775 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4777 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4778 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4780 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4781 heap_free( ascii_req );
4782 if(res != ERROR_SUCCESS) {
4783 TRACE("send failed: %u\n", res);
4784 if(!reusing_connection)
4785 break;
4786 http_release_netconn(request, FALSE);
4787 loop_next = TRUE;
4788 continue;
4791 request->bytesWritten = dwOptionalLength;
4793 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4794 INTERNET_STATUS_REQUEST_SENT,
4795 &len, sizeof(DWORD));
4797 if (bEndRequest)
4799 DWORD dwBufferSize;
4800 DWORD dwStatusCode;
4802 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4803 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4805 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4806 /* FIXME: We should know that connection is closed before sending
4807 * headers. Otherwise wrong callbacks are executed */
4808 if(!responseLen && reusing_connection) {
4809 TRACE("Connection closed by server, reconnecting\n");
4810 http_release_netconn(request, FALSE);
4811 loop_next = TRUE;
4812 continue;
4815 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4816 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4817 sizeof(DWORD));
4819 http_process_keep_alive(request);
4820 HTTP_ProcessCookies(request);
4821 HTTP_ProcessExpires(request);
4822 HTTP_ProcessLastModified(request);
4824 dwBufferSize = sizeof(dwStatusCode);
4825 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4826 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
4827 dwStatusCode = 0;
4829 res = set_content_length(request, dwStatusCode);
4830 if(res != ERROR_SUCCESS)
4831 goto lend;
4832 if(!request->contentLength)
4833 http_release_netconn(request, TRUE);
4835 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4837 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4838 dwBufferSize=sizeof(szNewLocation);
4839 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
4840 dwStatusCode == HTTP_STATUS_MOVED ||
4841 dwStatusCode == HTTP_STATUS_REDIRECT_KEEP_VERB ||
4842 dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
4843 HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
4845 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4846 dwStatusCode != HTTP_STATUS_REDIRECT_KEEP_VERB)
4848 heap_free(request->verb);
4849 request->verb = heap_strdupW(szGET);
4851 drain_content(request);
4852 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4854 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4855 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4856 res = HTTP_HandleRedirect(request, new_url);
4857 if (res == ERROR_SUCCESS)
4859 heap_free(requestString);
4860 loop_next = TRUE;
4862 heap_free( new_url );
4864 redirected = TRUE;
4867 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4869 WCHAR szAuthValue[2048];
4870 dwBufferSize=2048;
4871 if (dwStatusCode == HTTP_STATUS_DENIED)
4873 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4874 DWORD dwIndex = 0;
4875 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4877 if (HTTP_DoAuthorization(request, szAuthValue,
4878 &request->authInfo,
4879 request->session->userName,
4880 request->session->password,
4881 Host->lpszValue))
4883 heap_free(requestString);
4884 loop_next = TRUE;
4885 break;
4889 if(!loop_next) {
4890 TRACE("Cleaning wrong authorization data\n");
4891 destroy_authinfo(request->authInfo);
4892 request->authInfo = NULL;
4895 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
4897 DWORD dwIndex = 0;
4898 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4900 if (HTTP_DoAuthorization(request, szAuthValue,
4901 &request->proxyAuthInfo,
4902 request->session->appInfo->proxyUsername,
4903 request->session->appInfo->proxyPassword,
4904 NULL))
4906 loop_next = TRUE;
4907 break;
4911 if(!loop_next) {
4912 TRACE("Cleaning wrong proxy authorization data\n");
4913 destroy_authinfo(request->proxyAuthInfo);
4914 request->proxyAuthInfo = NULL;
4919 else
4920 res = ERROR_SUCCESS;
4922 while (loop_next);
4924 if(res == ERROR_SUCCESS)
4925 HTTP_CacheRequest(request);
4927 lend:
4928 heap_free(requestString);
4930 /* TODO: send notification for P3P header */
4932 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4934 if (res == ERROR_SUCCESS) {
4935 if(request->contentLength && request->bytesWritten == request->bytesToWrite)
4936 HTTP_ReceiveRequestData(request, TRUE);
4937 else
4938 send_request_complete(request,
4939 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
4940 }else {
4941 send_request_complete(request, 0, res);
4945 TRACE("<--\n");
4946 return res;
4949 /***********************************************************************
4951 * Helper functions for the HttpSendRequest(Ex) functions
4954 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
4956 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4957 http_request_t *request = (http_request_t*) workRequest->hdr;
4959 TRACE("%p\n", request);
4961 HTTP_HttpSendRequestW(request, req->lpszHeader,
4962 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
4963 req->dwContentLength, req->bEndRequest);
4965 heap_free(req->lpszHeader);
4969 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
4971 INT responseLen;
4972 DWORD dwCode, dwCodeLength;
4973 DWORD dwBufferSize;
4974 DWORD res = ERROR_SUCCESS;
4976 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4977 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4979 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4980 if (!responseLen)
4981 res = ERROR_HTTP_HEADER_NOT_FOUND;
4983 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4984 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
4986 /* process cookies here. Is this right? */
4987 http_process_keep_alive(request);
4988 HTTP_ProcessCookies(request);
4989 HTTP_ProcessExpires(request);
4990 HTTP_ProcessLastModified(request);
4992 dwCodeLength = sizeof(dwCode);
4993 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4994 &dwCode,&dwCodeLength,NULL) != ERROR_SUCCESS)
4995 dwCode = 0;
4997 if ((res = set_content_length( request, dwCode )) == ERROR_SUCCESS) {
4998 if(!request->contentLength)
4999 http_release_netconn(request, TRUE);
5002 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5004 if (dwCode == HTTP_STATUS_REDIRECT ||
5005 dwCode == HTTP_STATUS_MOVED ||
5006 dwCode == HTTP_STATUS_REDIRECT_METHOD ||
5007 dwCode == HTTP_STATUS_REDIRECT_KEEP_VERB)
5009 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5010 dwBufferSize=sizeof(szNewLocation);
5011 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
5013 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5014 dwCode != HTTP_STATUS_REDIRECT_KEEP_VERB)
5016 heap_free(request->verb);
5017 request->verb = heap_strdupW(szGET);
5019 drain_content(request);
5020 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5022 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5023 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5024 res = HTTP_HandleRedirect(request, new_url);
5025 if (res == ERROR_SUCCESS)
5026 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5027 heap_free( new_url );
5033 if (res == ERROR_SUCCESS && request->contentLength)
5034 HTTP_ReceiveRequestData(request, TRUE);
5035 else
5036 send_request_complete(request, res == ERROR_SUCCESS, res);
5038 return res;
5041 /***********************************************************************
5042 * HttpEndRequestA (WININET.@)
5044 * Ends an HTTP request that was started by HttpSendRequestEx
5046 * RETURNS
5047 * TRUE if successful
5048 * FALSE on failure
5051 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5052 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5054 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5056 if (lpBuffersOut)
5058 SetLastError(ERROR_INVALID_PARAMETER);
5059 return FALSE;
5062 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5065 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
5067 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
5068 http_request_t *request = (http_request_t*)work->hdr;
5070 TRACE("%p\n", request);
5072 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
5075 /***********************************************************************
5076 * HttpEndRequestW (WININET.@)
5078 * Ends an HTTP request that was started by HttpSendRequestEx
5080 * RETURNS
5081 * TRUE if successful
5082 * FALSE on failure
5085 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5086 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5088 http_request_t *request;
5089 DWORD res;
5091 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5093 if (lpBuffersOut)
5095 SetLastError(ERROR_INVALID_PARAMETER);
5096 return FALSE;
5099 request = (http_request_t*) get_handle_object( hRequest );
5101 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5103 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5104 if (request)
5105 WININET_Release( &request->hdr );
5106 return FALSE;
5108 request->hdr.dwFlags |= dwFlags;
5110 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5112 WORKREQUEST work;
5113 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5115 work.asyncproc = AsyncHttpEndRequestProc;
5116 work.hdr = WININET_AddRef( &request->hdr );
5118 work_endrequest = &work.u.HttpEndRequestW;
5119 work_endrequest->dwFlags = dwFlags;
5120 work_endrequest->dwContext = dwContext;
5122 INTERNET_AsyncCall(&work);
5123 res = ERROR_IO_PENDING;
5125 else
5126 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5128 WININET_Release( &request->hdr );
5129 TRACE("%u <--\n", res);
5130 if(res != ERROR_SUCCESS)
5131 SetLastError(res);
5132 return res == ERROR_SUCCESS;
5135 /***********************************************************************
5136 * HttpSendRequestExA (WININET.@)
5138 * Sends the specified request to the HTTP server and allows chunked
5139 * transfers.
5141 * RETURNS
5142 * Success: TRUE
5143 * Failure: FALSE, call GetLastError() for more information.
5145 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5146 LPINTERNET_BUFFERSA lpBuffersIn,
5147 LPINTERNET_BUFFERSA lpBuffersOut,
5148 DWORD dwFlags, DWORD_PTR dwContext)
5150 INTERNET_BUFFERSW BuffersInW;
5151 BOOL rc = FALSE;
5152 DWORD headerlen;
5153 LPWSTR header = NULL;
5155 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5156 lpBuffersOut, dwFlags, dwContext);
5158 if (lpBuffersIn)
5160 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5161 if (lpBuffersIn->lpcszHeader)
5163 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5164 lpBuffersIn->dwHeadersLength,0,0);
5165 header = heap_alloc(headerlen*sizeof(WCHAR));
5166 if (!(BuffersInW.lpcszHeader = header))
5168 SetLastError(ERROR_OUTOFMEMORY);
5169 return FALSE;
5171 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5172 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5173 header, headerlen);
5175 else
5176 BuffersInW.lpcszHeader = NULL;
5177 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5178 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5179 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5180 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5181 BuffersInW.Next = NULL;
5184 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5186 heap_free(header);
5187 return rc;
5190 /***********************************************************************
5191 * HttpSendRequestExW (WININET.@)
5193 * Sends the specified request to the HTTP server and allows chunked
5194 * transfers
5196 * RETURNS
5197 * Success: TRUE
5198 * Failure: FALSE, call GetLastError() for more information.
5200 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5201 LPINTERNET_BUFFERSW lpBuffersIn,
5202 LPINTERNET_BUFFERSW lpBuffersOut,
5203 DWORD dwFlags, DWORD_PTR dwContext)
5205 http_request_t *request;
5206 http_session_t *session;
5207 appinfo_t *hIC;
5208 DWORD res;
5210 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5211 lpBuffersOut, dwFlags, dwContext);
5213 request = (http_request_t*) get_handle_object( hRequest );
5215 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5217 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5218 goto lend;
5221 session = request->session;
5222 assert(session->hdr.htype == WH_HHTTPSESSION);
5223 hIC = session->appInfo;
5224 assert(hIC->hdr.htype == WH_HINIT);
5226 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5228 WORKREQUEST workRequest;
5229 struct WORKREQ_HTTPSENDREQUESTW *req;
5231 workRequest.asyncproc = AsyncHttpSendRequestProc;
5232 workRequest.hdr = WININET_AddRef( &request->hdr );
5233 req = &workRequest.u.HttpSendRequestW;
5234 if (lpBuffersIn)
5236 DWORD size = 0;
5238 if (lpBuffersIn->lpcszHeader)
5240 if (lpBuffersIn->dwHeadersLength == ~0u)
5241 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5242 else
5243 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5245 req->lpszHeader = heap_alloc(size);
5246 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5248 else req->lpszHeader = NULL;
5250 req->dwHeaderLength = size / sizeof(WCHAR);
5251 req->lpOptional = lpBuffersIn->lpvBuffer;
5252 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5253 req->dwContentLength = lpBuffersIn->dwBufferTotal;
5255 else
5257 req->lpszHeader = NULL;
5258 req->dwHeaderLength = 0;
5259 req->lpOptional = NULL;
5260 req->dwOptionalLength = 0;
5261 req->dwContentLength = 0;
5264 req->bEndRequest = FALSE;
5266 INTERNET_AsyncCall(&workRequest);
5268 * This is from windows.
5270 res = ERROR_IO_PENDING;
5272 else
5274 if (lpBuffersIn)
5275 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5276 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5277 lpBuffersIn->dwBufferTotal, FALSE);
5278 else
5279 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5282 lend:
5283 if ( request )
5284 WININET_Release( &request->hdr );
5286 TRACE("<---\n");
5287 SetLastError(res);
5288 return res == ERROR_SUCCESS;
5291 /***********************************************************************
5292 * HttpSendRequestW (WININET.@)
5294 * Sends the specified request to the HTTP server
5296 * RETURNS
5297 * TRUE on success
5298 * FALSE on failure
5301 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5302 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5304 http_request_t *request;
5305 http_session_t *session = NULL;
5306 appinfo_t *hIC = NULL;
5307 DWORD res = ERROR_SUCCESS;
5309 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5310 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5312 request = (http_request_t*) get_handle_object( hHttpRequest );
5313 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5315 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5316 goto lend;
5319 session = request->session;
5320 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5322 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5323 goto lend;
5326 hIC = session->appInfo;
5327 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5329 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5330 goto lend;
5333 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5335 WORKREQUEST workRequest;
5336 struct WORKREQ_HTTPSENDREQUESTW *req;
5338 workRequest.asyncproc = AsyncHttpSendRequestProc;
5339 workRequest.hdr = WININET_AddRef( &request->hdr );
5340 req = &workRequest.u.HttpSendRequestW;
5341 if (lpszHeaders)
5343 DWORD size;
5345 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5346 else size = dwHeaderLength * sizeof(WCHAR);
5348 req->lpszHeader = heap_alloc(size);
5349 memcpy(req->lpszHeader, lpszHeaders, size);
5351 else
5352 req->lpszHeader = 0;
5353 req->dwHeaderLength = dwHeaderLength;
5354 req->lpOptional = lpOptional;
5355 req->dwOptionalLength = dwOptionalLength;
5356 req->dwContentLength = dwOptionalLength;
5357 req->bEndRequest = TRUE;
5359 INTERNET_AsyncCall(&workRequest);
5361 * This is from windows.
5363 res = ERROR_IO_PENDING;
5365 else
5367 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5368 dwHeaderLength, lpOptional, dwOptionalLength,
5369 dwOptionalLength, TRUE);
5371 lend:
5372 if( request )
5373 WININET_Release( &request->hdr );
5375 SetLastError(res);
5376 return res == ERROR_SUCCESS;
5379 /***********************************************************************
5380 * HttpSendRequestA (WININET.@)
5382 * Sends the specified request to the HTTP server
5384 * RETURNS
5385 * TRUE on success
5386 * FALSE on failure
5389 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5390 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5392 BOOL result;
5393 LPWSTR szHeaders=NULL;
5394 DWORD nLen=dwHeaderLength;
5395 if(lpszHeaders!=NULL)
5397 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5398 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5399 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5401 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5402 heap_free(szHeaders);
5403 return result;
5406 /***********************************************************************
5407 * HTTPSESSION_Destroy (internal)
5409 * Deallocate session handle
5412 static void HTTPSESSION_Destroy(object_header_t *hdr)
5414 http_session_t *session = (http_session_t*) hdr;
5416 TRACE("%p\n", session);
5418 WININET_Release(&session->appInfo->hdr);
5420 heap_free(session->hostName);
5421 heap_free(session->serverName);
5422 heap_free(session->password);
5423 heap_free(session->userName);
5426 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5428 switch(option) {
5429 case INTERNET_OPTION_HANDLE_TYPE:
5430 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5432 if (*size < sizeof(ULONG))
5433 return ERROR_INSUFFICIENT_BUFFER;
5435 *size = sizeof(DWORD);
5436 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5437 return ERROR_SUCCESS;
5440 return INET_QueryOption(hdr, option, buffer, size, unicode);
5443 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5445 http_session_t *ses = (http_session_t*)hdr;
5447 switch(option) {
5448 case INTERNET_OPTION_USERNAME:
5450 heap_free(ses->userName);
5451 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5452 return ERROR_SUCCESS;
5454 case INTERNET_OPTION_PASSWORD:
5456 heap_free(ses->password);
5457 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5458 return ERROR_SUCCESS;
5460 default: break;
5463 return ERROR_INTERNET_INVALID_OPTION;
5466 static const object_vtbl_t HTTPSESSIONVtbl = {
5467 HTTPSESSION_Destroy,
5468 NULL,
5469 HTTPSESSION_QueryOption,
5470 HTTPSESSION_SetOption,
5471 NULL,
5472 NULL,
5473 NULL,
5474 NULL,
5475 NULL
5479 /***********************************************************************
5480 * HTTP_Connect (internal)
5482 * Create http session handle
5484 * RETURNS
5485 * HINTERNET a session handle on success
5486 * NULL on failure
5489 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5490 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5491 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5492 DWORD dwInternalFlags, HINTERNET *ret)
5494 http_session_t *session = NULL;
5496 TRACE("-->\n");
5498 if (!lpszServerName || !lpszServerName[0])
5499 return ERROR_INVALID_PARAMETER;
5501 assert( hIC->hdr.htype == WH_HINIT );
5503 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5504 if (!session)
5505 return ERROR_OUTOFMEMORY;
5508 * According to my tests. The name is not resolved until a request is sent
5511 session->hdr.htype = WH_HHTTPSESSION;
5512 session->hdr.dwFlags = dwFlags;
5513 session->hdr.dwContext = dwContext;
5514 session->hdr.dwInternalFlags |= dwInternalFlags;
5516 WININET_AddRef( &hIC->hdr );
5517 session->appInfo = hIC;
5518 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5520 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5521 if(hIC->proxyBypass)
5522 FIXME("Proxy bypass is ignored.\n");
5524 session->serverName = heap_strdupW(lpszServerName);
5525 session->hostName = heap_strdupW(lpszServerName);
5526 if (lpszUserName && lpszUserName[0])
5527 session->userName = heap_strdupW(lpszUserName);
5528 if (lpszPassword && lpszPassword[0])
5529 session->password = heap_strdupW(lpszPassword);
5530 session->serverPort = serverPort;
5531 session->hostPort = serverPort;
5533 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5534 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5536 INTERNET_SendCallback(&hIC->hdr, dwContext,
5537 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5538 sizeof(HINTERNET));
5542 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5543 * windows
5546 TRACE("%p --> %p\n", hIC, session);
5548 *ret = session->hdr.hInternet;
5549 return ERROR_SUCCESS;
5552 /***********************************************************************
5553 * HTTP_clear_response_headers (internal)
5555 * clear out any old response headers
5557 static void HTTP_clear_response_headers( http_request_t *request )
5559 DWORD i;
5561 for( i=0; i<request->nCustHeaders; i++)
5563 if( !request->custHeaders[i].lpszField )
5564 continue;
5565 if( !request->custHeaders[i].lpszValue )
5566 continue;
5567 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5568 continue;
5569 HTTP_DeleteCustomHeader( request, i );
5570 i--;
5574 /***********************************************************************
5575 * HTTP_GetResponseHeaders (internal)
5577 * Read server response
5579 * RETURNS
5581 * TRUE on success
5582 * FALSE on error
5584 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5586 INT cbreaks = 0;
5587 WCHAR buffer[MAX_REPLY_LEN];
5588 DWORD buflen = MAX_REPLY_LEN;
5589 BOOL bSuccess = FALSE;
5590 INT rc = 0;
5591 char bufferA[MAX_REPLY_LEN];
5592 LPWSTR status_code = NULL, status_text = NULL;
5593 DWORD cchMaxRawHeaders = 1024;
5594 LPWSTR lpszRawHeaders = NULL;
5595 LPWSTR temp;
5596 DWORD cchRawHeaders = 0;
5597 BOOL codeHundred = FALSE;
5599 TRACE("-->\n");
5601 if(!request->netconn)
5602 goto lend;
5604 do {
5605 static const WCHAR szHundred[] = {'1','0','0',0};
5607 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5609 buflen = MAX_REPLY_LEN;
5610 if (!read_line(request, bufferA, &buflen))
5611 goto lend;
5613 /* clear old response headers (eg. from a redirect response) */
5614 if (clear) {
5615 HTTP_clear_response_headers( request );
5616 clear = FALSE;
5619 rc += buflen;
5620 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5621 /* check is this a status code line? */
5622 if (!strncmpW(buffer, g_szHttp1_0, 4))
5624 /* split the version from the status code */
5625 status_code = strchrW( buffer, ' ' );
5626 if( !status_code )
5627 goto lend;
5628 *status_code++=0;
5630 /* split the status code from the status text */
5631 status_text = strchrW( status_code, ' ' );
5632 if( !status_text )
5633 goto lend;
5634 *status_text++=0;
5636 TRACE("version [%s] status code [%s] status text [%s]\n",
5637 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5639 codeHundred = (!strcmpW(status_code, szHundred));
5641 else if (!codeHundred)
5643 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5645 heap_free(request->version);
5646 heap_free(request->statusText);
5648 request->version = heap_strdupW(g_szHttp1_0);
5649 request->statusText = heap_strdupW(szOK);
5651 heap_free(request->rawHeaders);
5652 request->rawHeaders = heap_strdupW(szDefaultHeader);
5654 bSuccess = TRUE;
5655 goto lend;
5657 } while (codeHundred);
5659 /* Add status code */
5660 HTTP_ProcessHeader(request, szStatus, status_code,
5661 HTTP_ADDHDR_FLAG_REPLACE);
5663 heap_free(request->version);
5664 heap_free(request->statusText);
5666 request->version = heap_strdupW(buffer);
5667 request->statusText = heap_strdupW(status_text);
5669 /* Restore the spaces */
5670 *(status_code-1) = ' ';
5671 *(status_text-1) = ' ';
5673 /* regenerate raw headers */
5674 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5675 if (!lpszRawHeaders) goto lend;
5677 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5678 cchMaxRawHeaders *= 2;
5679 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5680 if (temp == NULL) goto lend;
5681 lpszRawHeaders = temp;
5682 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5683 cchRawHeaders += (buflen-1);
5684 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5685 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5686 lpszRawHeaders[cchRawHeaders] = '\0';
5688 /* Parse each response line */
5691 buflen = MAX_REPLY_LEN;
5692 if (read_line(request, bufferA, &buflen))
5694 LPWSTR * pFieldAndValue;
5696 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5698 if (!bufferA[0]) break;
5699 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5701 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5702 if (pFieldAndValue)
5704 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5705 cchMaxRawHeaders *= 2;
5706 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5707 if (temp == NULL) goto lend;
5708 lpszRawHeaders = temp;
5709 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5710 cchRawHeaders += (buflen-1);
5711 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5712 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5713 lpszRawHeaders[cchRawHeaders] = '\0';
5715 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5716 HTTP_ADDREQ_FLAG_ADD );
5718 HTTP_FreeTokens(pFieldAndValue);
5721 else
5723 cbreaks++;
5724 if (cbreaks >= 2)
5725 break;
5727 }while(1);
5729 /* make sure the response header is terminated with an empty line. Some apps really
5730 truly care about that empty line being there for some reason. Just add it to the
5731 header. */
5732 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5734 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5735 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5736 if (temp == NULL) goto lend;
5737 lpszRawHeaders = temp;
5740 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5742 heap_free(request->rawHeaders);
5743 request->rawHeaders = lpszRawHeaders;
5744 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5745 bSuccess = TRUE;
5747 lend:
5749 TRACE("<--\n");
5750 if (bSuccess)
5751 return rc;
5752 else
5754 heap_free(lpszRawHeaders);
5755 return 0;
5759 /***********************************************************************
5760 * HTTP_InterpretHttpHeader (internal)
5762 * Parse server response
5764 * RETURNS
5766 * Pointer to array of field, value, NULL on success.
5767 * NULL on error.
5769 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5771 LPWSTR * pTokenPair;
5772 LPWSTR pszColon;
5773 INT len;
5775 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5777 pszColon = strchrW(buffer, ':');
5778 /* must have two tokens */
5779 if (!pszColon)
5781 HTTP_FreeTokens(pTokenPair);
5782 if (buffer[0])
5783 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5784 return NULL;
5787 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5788 if (!pTokenPair[0])
5790 HTTP_FreeTokens(pTokenPair);
5791 return NULL;
5793 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5794 pTokenPair[0][pszColon - buffer] = '\0';
5796 /* skip colon */
5797 pszColon++;
5798 len = strlenW(pszColon);
5799 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5800 if (!pTokenPair[1])
5802 HTTP_FreeTokens(pTokenPair);
5803 return NULL;
5805 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5807 strip_spaces(pTokenPair[0]);
5808 strip_spaces(pTokenPair[1]);
5810 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5811 return pTokenPair;
5814 /***********************************************************************
5815 * HTTP_ProcessHeader (internal)
5817 * Stuff header into header tables according to <dwModifier>
5821 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5823 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5825 LPHTTPHEADERW lphttpHdr = NULL;
5826 INT index = -1;
5827 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5828 DWORD res = ERROR_HTTP_INVALID_HEADER;
5830 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5832 /* REPLACE wins out over ADD */
5833 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5834 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5836 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5837 index = -1;
5838 else
5839 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5841 if (index >= 0)
5843 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5844 return ERROR_HTTP_INVALID_HEADER;
5845 lphttpHdr = &request->custHeaders[index];
5847 else if (value)
5849 HTTPHEADERW hdr;
5851 hdr.lpszField = (LPWSTR)field;
5852 hdr.lpszValue = (LPWSTR)value;
5853 hdr.wFlags = hdr.wCount = 0;
5855 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5856 hdr.wFlags |= HDR_ISREQUEST;
5858 return HTTP_InsertCustomHeader(request, &hdr);
5860 /* no value to delete */
5861 else return ERROR_SUCCESS;
5863 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5864 lphttpHdr->wFlags |= HDR_ISREQUEST;
5865 else
5866 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5868 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5870 HTTP_DeleteCustomHeader( request, index );
5872 if (value)
5874 HTTPHEADERW hdr;
5876 hdr.lpszField = (LPWSTR)field;
5877 hdr.lpszValue = (LPWSTR)value;
5878 hdr.wFlags = hdr.wCount = 0;
5880 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5881 hdr.wFlags |= HDR_ISREQUEST;
5883 return HTTP_InsertCustomHeader(request, &hdr);
5886 return ERROR_SUCCESS;
5888 else if (dwModifier & COALESCEFLAGS)
5890 LPWSTR lpsztmp;
5891 WCHAR ch = 0;
5892 INT len = 0;
5893 INT origlen = strlenW(lphttpHdr->lpszValue);
5894 INT valuelen = strlenW(value);
5896 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5898 ch = ',';
5899 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5901 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5903 ch = ';';
5904 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5907 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5909 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5910 if (lpsztmp)
5912 lphttpHdr->lpszValue = lpsztmp;
5913 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5914 if (ch > 0)
5916 lphttpHdr->lpszValue[origlen] = ch;
5917 origlen++;
5918 lphttpHdr->lpszValue[origlen] = ' ';
5919 origlen++;
5922 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5923 lphttpHdr->lpszValue[len] = '\0';
5924 res = ERROR_SUCCESS;
5926 else
5928 WARN("heap_realloc (%d bytes) failed\n",len+1);
5929 res = ERROR_OUTOFMEMORY;
5932 TRACE("<-- %d\n", res);
5933 return res;
5936 /***********************************************************************
5937 * HTTP_GetCustomHeaderIndex (internal)
5939 * Return index of custom header from header array
5942 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
5943 int requested_index, BOOL request_only)
5945 DWORD index;
5947 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5949 for (index = 0; index < request->nCustHeaders; index++)
5951 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
5952 continue;
5954 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
5955 continue;
5957 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
5958 continue;
5960 if (requested_index == 0)
5961 break;
5962 requested_index --;
5965 if (index >= request->nCustHeaders)
5966 index = -1;
5968 TRACE("Return: %d\n", index);
5969 return index;
5973 /***********************************************************************
5974 * HTTP_InsertCustomHeader (internal)
5976 * Insert header into array
5979 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
5981 INT count;
5982 LPHTTPHEADERW lph = NULL;
5984 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5985 count = request->nCustHeaders + 1;
5986 if (count > 1)
5987 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
5988 else
5989 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
5991 if (!lph)
5992 return ERROR_OUTOFMEMORY;
5994 request->custHeaders = lph;
5995 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
5996 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
5997 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
5998 request->custHeaders[count-1].wCount= lpHdr->wCount;
5999 request->nCustHeaders++;
6001 return ERROR_SUCCESS;
6005 /***********************************************************************
6006 * HTTP_DeleteCustomHeader (internal)
6008 * Delete header from array
6009 * If this function is called, the indexs may change.
6011 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6013 if( request->nCustHeaders <= 0 )
6014 return FALSE;
6015 if( index >= request->nCustHeaders )
6016 return FALSE;
6017 request->nCustHeaders--;
6019 heap_free(request->custHeaders[index].lpszField);
6020 heap_free(request->custHeaders[index].lpszValue);
6022 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6023 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6024 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6026 return TRUE;
6030 /***********************************************************************
6031 * HTTP_VerifyValidHeader (internal)
6033 * Verify the given header is not invalid for the given http request
6036 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6038 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6039 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6040 return ERROR_HTTP_INVALID_HEADER;
6042 return ERROR_SUCCESS;
6045 /***********************************************************************
6046 * IsHostInProxyBypassList (@)
6048 * Undocumented
6051 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6053 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6054 return FALSE;
6057 /***********************************************************************
6058 * InternetShowSecurityInfoByURLA (@)
6060 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
6062 FIXME("stub: %s %p\n", url, window);
6063 return FALSE;
6066 /***********************************************************************
6067 * InternetShowSecurityInfoByURLW (@)
6069 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
6071 FIXME("stub: %s %p\n", debugstr_w(url), window);
6072 return FALSE;