ntdll: Expand previous patch to use pthread_mutex
[wine/multimedia.git] / dlls / wininet / http.c
blob54ac0d5ea78a6406bb93c8cfbcebf690d3c8204d
1 /*
2 * Wininet - HTTP Implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2002 CodeWeavers Inc.
6 * Copyright 2002 TransGaming Technologies Inc.
7 * Copyright 2004 Mike McCormack for CodeWeavers
8 * Copyright 2005 Aric Stewart for CodeWeavers
9 * Copyright 2006 Robert Shearman for CodeWeavers
10 * Copyright 2011 Jacek Caban for CodeWeavers
12 * Ulrich Czekalla
13 * David Hammerton
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "config.h"
31 #include "wine/port.h"
33 #if defined(__MINGW32__) || defined (_MSC_VER)
34 #include <ws2tcpip.h>
35 #endif
37 #include <sys/types.h>
38 #ifdef HAVE_SYS_SOCKET_H
39 # include <sys/socket.h>
40 #endif
41 #ifdef HAVE_ARPA_INET_H
42 # include <arpa/inet.h>
43 #endif
44 #include <stdarg.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #ifdef HAVE_UNISTD_H
48 # include <unistd.h>
49 #endif
50 #include <time.h>
51 #include <assert.h>
52 #ifdef HAVE_ZLIB
53 # include <zlib.h>
54 #endif
56 #include "windef.h"
57 #include "winbase.h"
58 #include "wininet.h"
59 #include "winerror.h"
60 #include "winternl.h"
61 #define NO_SHLWAPI_STREAM
62 #define NO_SHLWAPI_REG
63 #define NO_SHLWAPI_STRFCNS
64 #define NO_SHLWAPI_GDI
65 #include "shlwapi.h"
66 #include "sspi.h"
67 #include "wincrypt.h"
68 #include "winuser.h"
69 #include "cryptuiapi.h"
71 #include "internet.h"
72 #include "wine/debug.h"
73 #include "wine/exception.h"
74 #include "wine/unicode.h"
76 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
78 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
79 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
80 static const WCHAR szOK[] = {'O','K',0};
81 static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
82 static const WCHAR hostW[] = { 'H','o','s','t',0 };
83 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
84 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
85 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
86 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
87 static const WCHAR szGET[] = { 'G','E','T', 0 };
88 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
89 static const WCHAR szCrLf[] = {'\r','\n', 0};
91 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
92 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
93 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
94 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
95 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
96 static const WCHAR szAge[] = { 'A','g','e',0 };
97 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
98 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
99 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
100 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
101 static const WCHAR szContent_Disposition[] = { 'C','o','n','t','e','n','t','-','D','i','s','p','o','s','i','t','i','o','n',0 };
102 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
103 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
104 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
105 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
106 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
107 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
108 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
109 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 };
110 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
111 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
112 static const WCHAR szDate[] = { 'D','a','t','e',0 };
113 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
114 static const WCHAR szETag[] = { 'E','T','a','g',0 };
115 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
116 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
117 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
118 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
119 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
120 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
121 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
122 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
123 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
124 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
125 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
126 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
127 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
128 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
129 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
130 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
131 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
132 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
133 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
134 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
135 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
136 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 };
137 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
138 static const WCHAR szURI[] = { 'U','R','I',0 };
139 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
140 static const WCHAR szVary[] = { 'V','a','r','y',0 };
141 static const WCHAR szVia[] = { 'V','i','a',0 };
142 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
143 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
145 #define HTTP_REFERER szReferer
146 #define HTTP_ACCEPT szAccept
147 #define HTTP_USERAGENT szUser_Agent
149 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
150 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
151 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
152 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
153 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
154 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
155 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
157 #define COLLECT_TIME 60000
159 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
161 struct HttpAuthInfo
163 LPWSTR scheme;
164 CredHandle cred;
165 CtxtHandle ctx;
166 TimeStamp exp;
167 ULONG attr;
168 ULONG max_token;
169 void *auth_data;
170 unsigned int auth_data_len;
171 BOOL finished; /* finished authenticating */
175 typedef struct _basicAuthorizationData
177 struct list entry;
179 LPWSTR host;
180 LPWSTR realm;
181 LPSTR authorization;
182 UINT authorizationLen;
183 } basicAuthorizationData;
185 typedef struct _authorizationData
187 struct list entry;
189 LPWSTR host;
190 LPWSTR scheme;
191 LPWSTR domain;
192 UINT domain_len;
193 LPWSTR user;
194 UINT user_len;
195 LPWSTR password;
196 UINT password_len;
197 } authorizationData;
199 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
200 static struct list authorizationCache = LIST_INIT(authorizationCache);
202 static CRITICAL_SECTION authcache_cs;
203 static CRITICAL_SECTION_DEBUG critsect_debug =
205 0, 0, &authcache_cs,
206 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
207 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
209 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
211 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
212 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
213 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
214 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
215 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
216 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
217 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
218 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
219 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
220 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
221 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
223 static CRITICAL_SECTION connection_pool_cs;
224 static CRITICAL_SECTION_DEBUG connection_pool_debug =
226 0, 0, &connection_pool_cs,
227 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
228 0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
230 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
232 static struct list connection_pool = LIST_INIT(connection_pool);
233 static BOOL collector_running;
235 void server_addref(server_t *server)
237 InterlockedIncrement(&server->ref);
240 void server_release(server_t *server)
242 if(InterlockedDecrement(&server->ref))
243 return;
245 list_remove(&server->entry);
247 heap_free(server->name);
248 heap_free(server);
251 static server_t *get_server(const WCHAR *name, INTERNET_PORT port)
253 server_t *iter, *server = NULL;
255 EnterCriticalSection(&connection_pool_cs);
257 LIST_FOR_EACH_ENTRY(iter, &connection_pool, server_t, entry) {
258 if(iter->port == port && !strcmpW(iter->name, name)) {
259 server = iter;
260 server_addref(server);
261 break;
265 if(!server) {
266 server = heap_alloc(sizeof(*server));
267 if(server) {
268 server->addr_len = 0;
269 server->ref = 2; /* list reference and return */
270 server->port = port;
271 server->security_flags = 0;
272 list_init(&server->conn_pool);
273 server->name = heap_strdupW(name);
274 if(server->name) {
275 list_add_head(&connection_pool, &server->entry);
276 }else {
277 heap_free(server);
278 server = NULL;
283 LeaveCriticalSection(&connection_pool_cs);
285 return server;
288 BOOL collect_connections(collect_type_t collect_type)
290 netconn_t *netconn, *netconn_safe;
291 server_t *server, *server_safe;
292 BOOL remaining = FALSE;
293 DWORD64 now;
295 now = GetTickCount64();
297 LIST_FOR_EACH_ENTRY_SAFE(server, server_safe, &connection_pool, server_t, entry) {
298 LIST_FOR_EACH_ENTRY_SAFE(netconn, netconn_safe, &server->conn_pool, netconn_t, pool_entry) {
299 if(collect_type > COLLECT_TIMEOUT || netconn->keep_until < now) {
300 TRACE("freeing %p\n", netconn);
301 list_remove(&netconn->pool_entry);
302 free_netconn(netconn);
303 }else {
304 remaining = TRUE;
308 if(collect_type == COLLECT_CLEANUP) {
309 list_remove(&server->entry);
310 list_init(&server->entry);
311 server_release(server);
315 return remaining;
318 static DWORD WINAPI collect_connections_proc(void *arg)
320 BOOL remaining_conns;
322 do {
323 /* FIXME: Use more sophisticated method */
324 Sleep(5000);
326 EnterCriticalSection(&connection_pool_cs);
328 remaining_conns = collect_connections(COLLECT_TIMEOUT);
329 if(!remaining_conns)
330 collector_running = FALSE;
332 LeaveCriticalSection(&connection_pool_cs);
333 }while(remaining_conns);
335 FreeLibraryAndExitThread(WININET_hModule, 0);
338 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
340 int HeaderIndex = 0;
341 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
342 if (HeaderIndex == -1)
343 return NULL;
344 else
345 return &req->custHeaders[HeaderIndex];
348 typedef enum {
349 READMODE_SYNC,
350 READMODE_ASYNC,
351 READMODE_NOBLOCK
352 } read_mode_t;
354 struct data_stream_vtbl_t {
355 DWORD (*get_avail_data)(data_stream_t*,http_request_t*);
356 BOOL (*end_of_data)(data_stream_t*,http_request_t*);
357 DWORD (*read)(data_stream_t*,http_request_t*,BYTE*,DWORD,DWORD*,read_mode_t);
358 BOOL (*drain_content)(data_stream_t*,http_request_t*);
359 void (*destroy)(data_stream_t*);
362 typedef struct {
363 data_stream_t data_stream;
365 BYTE buf[READ_BUFFER_SIZE];
366 DWORD buf_size;
367 DWORD buf_pos;
368 DWORD chunk_size;
369 } chunked_stream_t;
371 static inline void destroy_data_stream(data_stream_t *stream)
373 stream->vtbl->destroy(stream);
376 static void reset_data_stream(http_request_t *req)
378 destroy_data_stream(req->data_stream);
379 req->data_stream = &req->netconn_stream.data_stream;
380 req->read_pos = req->read_size = req->netconn_stream.content_read = 0;
381 req->read_chunked = req->read_gzip = FALSE;
384 #ifdef HAVE_ZLIB
386 typedef struct {
387 data_stream_t stream;
388 data_stream_t *parent_stream;
389 z_stream zstream;
390 BYTE buf[READ_BUFFER_SIZE];
391 DWORD buf_size;
392 DWORD buf_pos;
393 BOOL end_of_data;
394 } gzip_stream_t;
396 static DWORD gzip_get_avail_data(data_stream_t *stream, http_request_t *req)
398 /* Allow reading only from read buffer */
399 return 0;
402 static BOOL gzip_end_of_data(data_stream_t *stream, http_request_t *req)
404 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
405 return gzip_stream->end_of_data;
408 static DWORD gzip_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
409 DWORD *read, read_mode_t read_mode)
411 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
412 z_stream *zstream = &gzip_stream->zstream;
413 DWORD current_read, ret_read = 0;
414 BOOL end;
415 int zres;
416 DWORD res = ERROR_SUCCESS;
418 while(size && !gzip_stream->end_of_data) {
419 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
421 if(gzip_stream->buf_size <= 64 && !end) {
422 if(gzip_stream->buf_pos) {
423 if(gzip_stream->buf_size)
424 memmove(gzip_stream->buf, gzip_stream->buf+gzip_stream->buf_pos, gzip_stream->buf_size);
425 gzip_stream->buf_pos = 0;
427 res = gzip_stream->parent_stream->vtbl->read(gzip_stream->parent_stream, req, gzip_stream->buf+gzip_stream->buf_size,
428 sizeof(gzip_stream->buf)-gzip_stream->buf_size, &current_read, read_mode);
429 gzip_stream->buf_size += current_read;
430 if(res != ERROR_SUCCESS)
431 break;
432 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
433 if(!current_read && !end) {
434 if(read_mode != READMODE_NOBLOCK) {
435 WARN("unexpected end of data\n");
436 gzip_stream->end_of_data = TRUE;
438 break;
440 if(gzip_stream->buf_size <= 64 && !end)
441 continue;
444 zstream->next_in = gzip_stream->buf+gzip_stream->buf_pos;
445 zstream->avail_in = gzip_stream->buf_size-(end ? 0 : 64);
446 zstream->next_out = buf+ret_read;
447 zstream->avail_out = size;
448 zres = inflate(&gzip_stream->zstream, 0);
449 current_read = size - zstream->avail_out;
450 size -= current_read;
451 ret_read += current_read;
452 gzip_stream->buf_size -= zstream->next_in - (gzip_stream->buf+gzip_stream->buf_pos);
453 gzip_stream->buf_pos = zstream->next_in-gzip_stream->buf;
454 if(zres == Z_STREAM_END) {
455 TRACE("end of data\n");
456 gzip_stream->end_of_data = TRUE;
457 inflateEnd(zstream);
458 }else if(zres != Z_OK) {
459 WARN("inflate failed %d: %s\n", zres, debugstr_a(zstream->msg));
460 if(!ret_read)
461 res = ERROR_INTERNET_DECODING_FAILED;
462 break;
465 if(ret_read && read_mode == READMODE_ASYNC)
466 read_mode = READMODE_NOBLOCK;
469 TRACE("read %u bytes\n", ret_read);
470 *read = ret_read;
471 return res;
474 static BOOL gzip_drain_content(data_stream_t *stream, http_request_t *req)
476 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
477 return gzip_stream->parent_stream->vtbl->drain_content(gzip_stream->parent_stream, req);
480 static void gzip_destroy(data_stream_t *stream)
482 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
484 destroy_data_stream(gzip_stream->parent_stream);
486 if(!gzip_stream->end_of_data)
487 inflateEnd(&gzip_stream->zstream);
488 heap_free(gzip_stream);
491 static const data_stream_vtbl_t gzip_stream_vtbl = {
492 gzip_get_avail_data,
493 gzip_end_of_data,
494 gzip_read,
495 gzip_drain_content,
496 gzip_destroy
499 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
501 return heap_alloc(items*size);
504 static void wininet_zfree(voidpf opaque, voidpf address)
506 heap_free(address);
509 static DWORD init_gzip_stream(http_request_t *req)
511 gzip_stream_t *gzip_stream;
512 int index, zres;
514 gzip_stream = heap_alloc_zero(sizeof(gzip_stream_t));
515 if(!gzip_stream)
516 return ERROR_OUTOFMEMORY;
518 gzip_stream->stream.vtbl = &gzip_stream_vtbl;
519 gzip_stream->zstream.zalloc = wininet_zalloc;
520 gzip_stream->zstream.zfree = wininet_zfree;
522 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
523 if(zres != Z_OK) {
524 ERR("inflateInit failed: %d\n", zres);
525 heap_free(gzip_stream);
526 return ERROR_OUTOFMEMORY;
529 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
530 if(index != -1)
531 HTTP_DeleteCustomHeader(req, index);
533 if(req->read_size) {
534 memcpy(gzip_stream->buf, req->read_buf+req->read_pos, req->read_size);
535 gzip_stream->buf_size = req->read_size;
536 req->read_pos = req->read_size = 0;
539 req->read_gzip = TRUE;
540 gzip_stream->parent_stream = req->data_stream;
541 req->data_stream = &gzip_stream->stream;
542 return ERROR_SUCCESS;
545 #else
547 static DWORD init_gzip_stream(http_request_t *req)
549 ERR("gzip stream not supported, missing zlib.\n");
550 return ERROR_SUCCESS;
553 #endif
555 /***********************************************************************
556 * HTTP_Tokenize (internal)
558 * Tokenize a string, allocating memory for the tokens.
560 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
562 LPWSTR * token_array;
563 int tokens = 0;
564 int i;
565 LPCWSTR next_token;
567 if (string)
569 /* empty string has no tokens */
570 if (*string)
571 tokens++;
572 /* count tokens */
573 for (i = 0; string[i]; i++)
575 if (!strncmpW(string+i, token_string, strlenW(token_string)))
577 DWORD j;
578 tokens++;
579 /* we want to skip over separators, but not the null terminator */
580 for (j = 0; j < strlenW(token_string) - 1; j++)
581 if (!string[i+j])
582 break;
583 i += j;
588 /* add 1 for terminating NULL */
589 token_array = heap_alloc((tokens+1) * sizeof(*token_array));
590 token_array[tokens] = NULL;
591 if (!tokens)
592 return token_array;
593 for (i = 0; i < tokens; i++)
595 int len;
596 next_token = strstrW(string, token_string);
597 if (!next_token) next_token = string+strlenW(string);
598 len = next_token - string;
599 token_array[i] = heap_alloc((len+1)*sizeof(WCHAR));
600 memcpy(token_array[i], string, len*sizeof(WCHAR));
601 token_array[i][len] = '\0';
602 string = next_token+strlenW(token_string);
604 return token_array;
607 /***********************************************************************
608 * HTTP_FreeTokens (internal)
610 * Frees memory returned from HTTP_Tokenize.
612 static void HTTP_FreeTokens(LPWSTR * token_array)
614 int i;
615 for (i = 0; token_array[i]; i++) heap_free(token_array[i]);
616 heap_free(token_array);
619 static void HTTP_FixURL(http_request_t *request)
621 static const WCHAR szSlash[] = { '/',0 };
622 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
624 /* If we don't have a path we set it to root */
625 if (NULL == request->path)
626 request->path = heap_strdupW(szSlash);
627 else /* remove \r and \n*/
629 int nLen = strlenW(request->path);
630 while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n')))
632 nLen--;
633 request->path[nLen]='\0';
635 /* Replace '\' with '/' */
636 while (nLen>0) {
637 nLen--;
638 if (request->path[nLen] == '\\') request->path[nLen]='/';
642 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
643 request->path, strlenW(request->path), szHttp, strlenW(szHttp) )
644 && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */
646 WCHAR *fixurl = heap_alloc((strlenW(request->path) + 2)*sizeof(WCHAR));
647 *fixurl = '/';
648 strcpyW(fixurl + 1, request->path);
649 heap_free( request->path );
650 request->path = fixurl;
654 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *request, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
656 LPWSTR requestString;
657 DWORD len, n;
658 LPCWSTR *req;
659 UINT i;
660 LPWSTR p;
662 static const WCHAR szSpace[] = { ' ',0 };
663 static const WCHAR szColon[] = { ':',' ',0 };
664 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
666 /* allocate space for an array of all the string pointers to be added */
667 len = (request->nCustHeaders)*4 + 10;
668 req = heap_alloc(len*sizeof(LPCWSTR));
670 /* add the verb, path and HTTP version string */
671 n = 0;
672 req[n++] = verb;
673 req[n++] = szSpace;
674 req[n++] = path;
675 req[n++] = szSpace;
676 req[n++] = version;
678 /* Append custom request headers */
679 for (i = 0; i < request->nCustHeaders; i++)
681 if (request->custHeaders[i].wFlags & HDR_ISREQUEST)
683 req[n++] = szCrLf;
684 req[n++] = request->custHeaders[i].lpszField;
685 req[n++] = szColon;
686 req[n++] = request->custHeaders[i].lpszValue;
688 TRACE("Adding custom header %s (%s)\n",
689 debugstr_w(request->custHeaders[i].lpszField),
690 debugstr_w(request->custHeaders[i].lpszValue));
694 if( n >= len )
695 ERR("oops. buffer overrun\n");
697 req[n] = NULL;
698 requestString = HTTP_build_req( req, 4 );
699 heap_free( req );
702 * Set (header) termination string for request
703 * Make sure there's exactly two new lines at the end of the request
705 p = &requestString[strlenW(requestString)-1];
706 while ( (*p == '\n') || (*p == '\r') )
707 p--;
708 strcpyW( p+1, sztwocrlf );
710 return requestString;
713 static void HTTP_ProcessCookies( http_request_t *request )
715 int HeaderIndex;
716 int numCookies = 0;
717 LPHTTPHEADERW setCookieHeader;
719 if(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES)
720 return;
722 while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, szSet_Cookie, numCookies++, FALSE)) != -1)
724 HTTPHEADERW *host;
725 const WCHAR *data;
726 WCHAR *name;
728 setCookieHeader = &request->custHeaders[HeaderIndex];
730 if (!setCookieHeader->lpszValue)
731 continue;
733 host = HTTP_GetHeader(request, hostW);
734 if(!host)
735 continue;
737 data = strchrW(setCookieHeader->lpszValue, '=');
738 if(!data)
739 continue;
741 name = heap_strndupW(setCookieHeader->lpszValue, data-setCookieHeader->lpszValue);
742 if(!name)
743 continue;
745 data++;
746 set_cookie(host->lpszValue, request->path, name, data);
747 heap_free(name);
751 static void strip_spaces(LPWSTR start)
753 LPWSTR str = start;
754 LPWSTR end;
756 while (*str == ' ' && *str != '\0')
757 str++;
759 if (str != start)
760 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
762 end = start + strlenW(start) - 1;
763 while (end >= start && *end == ' ')
765 *end = '\0';
766 end--;
770 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
772 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
773 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
774 BOOL is_basic;
775 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
776 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
777 if (is_basic && pszRealm)
779 LPCWSTR token;
780 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
781 LPCWSTR realm;
782 ptr++;
783 *pszRealm=NULL;
784 token = strchrW(ptr,'=');
785 if (!token)
786 return TRUE;
787 realm = ptr;
788 while (*realm == ' ' && *realm != '\0')
789 realm++;
790 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
791 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
793 token++;
794 while (*token == ' ' && *token != '\0')
795 token++;
796 if (*token == '\0')
797 return TRUE;
798 *pszRealm = heap_strdupW(token);
799 strip_spaces(*pszRealm);
803 return is_basic;
806 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
808 if (!authinfo) return;
810 if (SecIsValidHandle(&authinfo->ctx))
811 DeleteSecurityContext(&authinfo->ctx);
812 if (SecIsValidHandle(&authinfo->cred))
813 FreeCredentialsHandle(&authinfo->cred);
815 heap_free(authinfo->auth_data);
816 heap_free(authinfo->scheme);
817 heap_free(authinfo);
820 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
822 basicAuthorizationData *ad;
823 UINT rc = 0;
825 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
827 EnterCriticalSection(&authcache_cs);
828 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
830 if (!strcmpiW(host,ad->host) && !strcmpW(realm,ad->realm))
832 TRACE("Authorization found in cache\n");
833 *auth_data = heap_alloc(ad->authorizationLen);
834 memcpy(*auth_data,ad->authorization,ad->authorizationLen);
835 rc = ad->authorizationLen;
836 break;
839 LeaveCriticalSection(&authcache_cs);
840 return rc;
843 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
845 struct list *cursor;
846 basicAuthorizationData* ad = NULL;
848 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
850 EnterCriticalSection(&authcache_cs);
851 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
853 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
854 if (!strcmpiW(host,check->host) && !strcmpW(realm,check->realm))
856 ad = check;
857 break;
861 if (ad)
863 TRACE("Found match in cache, replacing\n");
864 heap_free(ad->authorization);
865 ad->authorization = heap_alloc(auth_data_len);
866 memcpy(ad->authorization, auth_data, auth_data_len);
867 ad->authorizationLen = auth_data_len;
869 else
871 ad = heap_alloc(sizeof(basicAuthorizationData));
872 ad->host = heap_strdupW(host);
873 ad->realm = heap_strdupW(realm);
874 ad->authorization = heap_alloc(auth_data_len);
875 memcpy(ad->authorization, auth_data, auth_data_len);
876 ad->authorizationLen = auth_data_len;
877 list_add_head(&basicAuthorizationCache,&ad->entry);
878 TRACE("authorization cached\n");
880 LeaveCriticalSection(&authcache_cs);
883 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
884 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
886 authorizationData *ad;
888 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
890 EnterCriticalSection(&authcache_cs);
891 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
892 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
893 TRACE("Authorization found in cache\n");
895 nt_auth_identity->User = heap_strdupW(ad->user);
896 nt_auth_identity->Password = heap_strdupW(ad->password);
897 nt_auth_identity->Domain = heap_alloc(sizeof(WCHAR)*ad->domain_len);
898 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
899 (!nt_auth_identity->Domain && ad->domain_len)) {
900 heap_free(nt_auth_identity->User);
901 heap_free(nt_auth_identity->Password);
902 heap_free(nt_auth_identity->Domain);
903 break;
906 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
907 nt_auth_identity->UserLength = ad->user_len;
908 nt_auth_identity->PasswordLength = ad->password_len;
909 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
910 nt_auth_identity->DomainLength = ad->domain_len;
911 LeaveCriticalSection(&authcache_cs);
912 return TRUE;
915 LeaveCriticalSection(&authcache_cs);
917 return FALSE;
920 static void cache_authorization(LPWSTR host, LPWSTR scheme,
921 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
923 authorizationData *ad;
924 BOOL found = FALSE;
926 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
928 EnterCriticalSection(&authcache_cs);
929 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
930 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
931 found = TRUE;
932 break;
935 if(found) {
936 heap_free(ad->user);
937 heap_free(ad->password);
938 heap_free(ad->domain);
939 } else {
940 ad = heap_alloc(sizeof(authorizationData));
941 if(!ad) {
942 LeaveCriticalSection(&authcache_cs);
943 return;
946 ad->host = heap_strdupW(host);
947 ad->scheme = heap_strdupW(scheme);
948 list_add_head(&authorizationCache, &ad->entry);
951 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
952 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
953 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
954 ad->user_len = nt_auth_identity->UserLength;
955 ad->password_len = nt_auth_identity->PasswordLength;
956 ad->domain_len = nt_auth_identity->DomainLength;
958 if(!ad->host || !ad->scheme || !ad->user || !ad->password
959 || (nt_auth_identity->Domain && !ad->domain)) {
960 heap_free(ad->host);
961 heap_free(ad->scheme);
962 heap_free(ad->user);
963 heap_free(ad->password);
964 heap_free(ad->domain);
965 list_remove(&ad->entry);
966 heap_free(ad);
969 LeaveCriticalSection(&authcache_cs);
972 static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue,
973 struct HttpAuthInfo **ppAuthInfo,
974 LPWSTR domain_and_username, LPWSTR password,
975 LPWSTR host )
977 SECURITY_STATUS sec_status;
978 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
979 BOOL first = FALSE;
980 LPWSTR szRealm = NULL;
982 TRACE("%s\n", debugstr_w(pszAuthValue));
984 if (!pAuthInfo)
986 TimeStamp exp;
988 first = TRUE;
989 pAuthInfo = heap_alloc(sizeof(*pAuthInfo));
990 if (!pAuthInfo)
991 return FALSE;
993 SecInvalidateHandle(&pAuthInfo->cred);
994 SecInvalidateHandle(&pAuthInfo->ctx);
995 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
996 pAuthInfo->attr = 0;
997 pAuthInfo->auth_data = NULL;
998 pAuthInfo->auth_data_len = 0;
999 pAuthInfo->finished = FALSE;
1001 if (is_basic_auth_value(pszAuthValue,NULL))
1003 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
1004 pAuthInfo->scheme = heap_strdupW(szBasic);
1005 if (!pAuthInfo->scheme)
1007 heap_free(pAuthInfo);
1008 return FALSE;
1011 else
1013 PVOID pAuthData;
1014 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
1016 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
1017 if (!pAuthInfo->scheme)
1019 heap_free(pAuthInfo);
1020 return FALSE;
1023 if (domain_and_username)
1025 WCHAR *user = strchrW(domain_and_username, '\\');
1026 WCHAR *domain = domain_and_username;
1028 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1030 pAuthData = &nt_auth_identity;
1032 if (user) user++;
1033 else
1035 user = domain_and_username;
1036 domain = NULL;
1039 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1040 nt_auth_identity.User = user;
1041 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
1042 nt_auth_identity.Domain = domain;
1043 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
1044 nt_auth_identity.Password = password;
1045 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
1047 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
1049 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
1050 pAuthData = &nt_auth_identity;
1051 else
1052 /* use default credentials */
1053 pAuthData = NULL;
1055 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
1056 SECPKG_CRED_OUTBOUND, NULL,
1057 pAuthData, NULL,
1058 NULL, &pAuthInfo->cred,
1059 &exp);
1061 if(pAuthData && !domain_and_username) {
1062 heap_free(nt_auth_identity.User);
1063 heap_free(nt_auth_identity.Domain);
1064 heap_free(nt_auth_identity.Password);
1067 if (sec_status == SEC_E_OK)
1069 PSecPkgInfoW sec_pkg_info;
1070 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
1071 if (sec_status == SEC_E_OK)
1073 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
1074 FreeContextBuffer(sec_pkg_info);
1077 if (sec_status != SEC_E_OK)
1079 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1080 debugstr_w(pAuthInfo->scheme), sec_status);
1081 heap_free(pAuthInfo->scheme);
1082 heap_free(pAuthInfo);
1083 return FALSE;
1086 *ppAuthInfo = pAuthInfo;
1088 else if (pAuthInfo->finished)
1089 return FALSE;
1091 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
1092 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
1094 ERR("authentication scheme changed from %s to %s\n",
1095 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
1096 return FALSE;
1099 if (is_basic_auth_value(pszAuthValue,&szRealm))
1101 int userlen;
1102 int passlen;
1103 char *auth_data = NULL;
1104 UINT auth_data_len = 0;
1106 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
1108 if (!domain_and_username)
1110 if (host && szRealm)
1111 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
1112 if (auth_data_len == 0)
1114 heap_free(szRealm);
1115 return FALSE;
1118 else
1120 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
1121 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
1123 /* length includes a nul terminator, which will be re-used for the ':' */
1124 auth_data = heap_alloc(userlen + 1 + passlen);
1125 if (!auth_data)
1127 heap_free(szRealm);
1128 return FALSE;
1131 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
1132 auth_data[userlen] = ':';
1133 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
1134 auth_data_len = userlen + 1 + passlen;
1135 if (host && szRealm)
1136 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
1139 pAuthInfo->auth_data = auth_data;
1140 pAuthInfo->auth_data_len = auth_data_len;
1141 pAuthInfo->finished = TRUE;
1142 heap_free(szRealm);
1143 return TRUE;
1145 else
1147 LPCWSTR pszAuthData;
1148 SecBufferDesc out_desc, in_desc;
1149 SecBuffer out, in;
1150 unsigned char *buffer;
1151 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
1152 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
1154 in.BufferType = SECBUFFER_TOKEN;
1155 in.cbBuffer = 0;
1156 in.pvBuffer = NULL;
1158 in_desc.ulVersion = 0;
1159 in_desc.cBuffers = 1;
1160 in_desc.pBuffers = &in;
1162 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
1163 if (*pszAuthData == ' ')
1165 pszAuthData++;
1166 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
1167 in.pvBuffer = heap_alloc(in.cbBuffer);
1168 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
1171 buffer = heap_alloc(pAuthInfo->max_token);
1173 out.BufferType = SECBUFFER_TOKEN;
1174 out.cbBuffer = pAuthInfo->max_token;
1175 out.pvBuffer = buffer;
1177 out_desc.ulVersion = 0;
1178 out_desc.cBuffers = 1;
1179 out_desc.pBuffers = &out;
1181 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
1182 first ? NULL : &pAuthInfo->ctx,
1183 first ? request->server->name : NULL,
1184 context_req, 0, SECURITY_NETWORK_DREP,
1185 in.pvBuffer ? &in_desc : NULL,
1186 0, &pAuthInfo->ctx, &out_desc,
1187 &pAuthInfo->attr, &pAuthInfo->exp);
1188 if (sec_status == SEC_E_OK)
1190 pAuthInfo->finished = TRUE;
1191 pAuthInfo->auth_data = out.pvBuffer;
1192 pAuthInfo->auth_data_len = out.cbBuffer;
1193 TRACE("sending last auth packet\n");
1195 else if (sec_status == SEC_I_CONTINUE_NEEDED)
1197 pAuthInfo->auth_data = out.pvBuffer;
1198 pAuthInfo->auth_data_len = out.cbBuffer;
1199 TRACE("sending next auth packet\n");
1201 else
1203 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
1204 heap_free(out.pvBuffer);
1205 destroy_authinfo(pAuthInfo);
1206 *ppAuthInfo = NULL;
1207 return FALSE;
1211 return TRUE;
1214 /***********************************************************************
1215 * HTTP_HttpAddRequestHeadersW (internal)
1217 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *request,
1218 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1220 LPWSTR lpszStart;
1221 LPWSTR lpszEnd;
1222 LPWSTR buffer;
1223 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
1225 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
1227 if( dwHeaderLength == ~0U )
1228 len = strlenW(lpszHeader);
1229 else
1230 len = dwHeaderLength;
1231 buffer = heap_alloc(sizeof(WCHAR)*(len+1));
1232 lstrcpynW( buffer, lpszHeader, len + 1);
1234 lpszStart = buffer;
1238 LPWSTR * pFieldAndValue;
1240 lpszEnd = lpszStart;
1242 while (*lpszEnd != '\0')
1244 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1245 break;
1246 lpszEnd++;
1249 if (*lpszStart == '\0')
1250 break;
1252 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1254 *lpszEnd = '\0';
1255 lpszEnd++; /* Jump over newline */
1257 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1258 if (*lpszStart == '\0')
1260 /* Skip 0-length headers */
1261 lpszStart = lpszEnd;
1262 res = ERROR_SUCCESS;
1263 continue;
1265 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1266 if (pFieldAndValue)
1268 res = HTTP_VerifyValidHeader(request, pFieldAndValue[0]);
1269 if (res == ERROR_SUCCESS)
1270 res = HTTP_ProcessHeader(request, pFieldAndValue[0],
1271 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1272 HTTP_FreeTokens(pFieldAndValue);
1275 lpszStart = lpszEnd;
1276 } while (res == ERROR_SUCCESS);
1278 heap_free(buffer);
1279 return res;
1282 /***********************************************************************
1283 * HttpAddRequestHeadersW (WININET.@)
1285 * Adds one or more HTTP header to the request handler
1287 * NOTE
1288 * On Windows if dwHeaderLength includes the trailing '\0', then
1289 * HttpAddRequestHeadersW() adds it too. However this results in an
1290 * invalid HTTP header which is rejected by some servers so we probably
1291 * don't need to match Windows on that point.
1293 * RETURNS
1294 * TRUE on success
1295 * FALSE on failure
1298 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1299 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1301 http_request_t *request;
1302 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1304 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1306 if (!lpszHeader)
1307 return TRUE;
1309 request = (http_request_t*) get_handle_object( hHttpRequest );
1310 if (request && request->hdr.htype == WH_HHTTPREQ)
1311 res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier );
1312 if( request )
1313 WININET_Release( &request->hdr );
1315 if(res != ERROR_SUCCESS)
1316 SetLastError(res);
1317 return res == ERROR_SUCCESS;
1320 /***********************************************************************
1321 * HttpAddRequestHeadersA (WININET.@)
1323 * Adds one or more HTTP header to the request handler
1325 * RETURNS
1326 * TRUE on success
1327 * FALSE on failure
1330 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1331 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1333 DWORD len;
1334 LPWSTR hdr;
1335 BOOL r;
1337 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1339 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1340 hdr = heap_alloc(len*sizeof(WCHAR));
1341 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1342 if( dwHeaderLength != ~0U )
1343 dwHeaderLength = len;
1345 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1347 heap_free( hdr );
1348 return r;
1351 static void free_accept_types( WCHAR **accept_types )
1353 WCHAR *ptr, **types = accept_types;
1355 if (!types) return;
1356 while ((ptr = *types))
1358 heap_free( ptr );
1359 types++;
1361 heap_free( accept_types );
1364 static WCHAR **convert_accept_types( const char **accept_types )
1366 unsigned int count;
1367 const char **types = accept_types;
1368 WCHAR **typesW;
1369 BOOL invalid_pointer = FALSE;
1371 if (!types) return NULL;
1372 count = 0;
1373 while (*types)
1375 __TRY
1377 /* find out how many there are */
1378 if (*types && **types)
1380 TRACE("accept type: %s\n", debugstr_a(*types));
1381 count++;
1384 __EXCEPT_PAGE_FAULT
1386 WARN("invalid accept type pointer\n");
1387 invalid_pointer = TRUE;
1389 __ENDTRY;
1390 types++;
1392 if (invalid_pointer) return NULL;
1393 if (!(typesW = heap_alloc( sizeof(WCHAR *) * (count + 1) ))) return NULL;
1394 count = 0;
1395 types = accept_types;
1396 while (*types)
1398 if (*types && **types) typesW[count++] = heap_strdupAtoW( *types );
1399 types++;
1401 typesW[count] = NULL;
1402 return typesW;
1405 /***********************************************************************
1406 * HttpOpenRequestA (WININET.@)
1408 * Open a HTTP request handle
1410 * RETURNS
1411 * HINTERNET a HTTP request handle on success
1412 * NULL on failure
1415 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1416 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1417 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1418 DWORD dwFlags, DWORD_PTR dwContext)
1420 LPWSTR szVerb = NULL, szObjectName = NULL;
1421 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1422 HINTERNET rc = FALSE;
1424 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1425 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1426 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1427 dwFlags, dwContext);
1429 if (lpszVerb)
1431 szVerb = heap_strdupAtoW(lpszVerb);
1432 if ( !szVerb )
1433 goto end;
1436 if (lpszObjectName)
1438 szObjectName = heap_strdupAtoW(lpszObjectName);
1439 if ( !szObjectName )
1440 goto end;
1443 if (lpszVersion)
1445 szVersion = heap_strdupAtoW(lpszVersion);
1446 if ( !szVersion )
1447 goto end;
1450 if (lpszReferrer)
1452 szReferrer = heap_strdupAtoW(lpszReferrer);
1453 if ( !szReferrer )
1454 goto end;
1457 szAcceptTypes = convert_accept_types( lpszAcceptTypes );
1458 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName, szVersion, szReferrer,
1459 (const WCHAR **)szAcceptTypes, dwFlags, dwContext);
1461 end:
1462 free_accept_types(szAcceptTypes);
1463 heap_free(szReferrer);
1464 heap_free(szVersion);
1465 heap_free(szObjectName);
1466 heap_free(szVerb);
1467 return rc;
1470 /***********************************************************************
1471 * HTTP_EncodeBase64
1473 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1475 UINT n = 0, x;
1476 static const CHAR HTTP_Base64Enc[] =
1477 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1479 while( len > 0 )
1481 /* first 6 bits, all from bin[0] */
1482 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1483 x = (bin[0] & 3) << 4;
1485 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1486 if( len == 1 )
1488 base64[n++] = HTTP_Base64Enc[x];
1489 base64[n++] = '=';
1490 base64[n++] = '=';
1491 break;
1493 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1494 x = ( bin[1] & 0x0f ) << 2;
1496 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1497 if( len == 2 )
1499 base64[n++] = HTTP_Base64Enc[x];
1500 base64[n++] = '=';
1501 break;
1503 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1505 /* last 6 bits, all from bin [2] */
1506 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1507 bin += 3;
1508 len -= 3;
1510 base64[n] = 0;
1511 return n;
1514 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1515 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1516 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1517 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1518 static const signed char HTTP_Base64Dec[256] =
1520 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1521 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1522 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1523 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1524 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1525 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1526 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1527 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1528 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1529 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1530 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1531 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1532 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1533 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1534 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1535 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1536 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1537 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1538 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1539 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1540 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1541 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1542 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1543 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1544 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1545 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1547 #undef CH
1549 /***********************************************************************
1550 * HTTP_DecodeBase64
1552 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1554 unsigned int n = 0;
1556 while(*base64)
1558 signed char in[4];
1560 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1561 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1562 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1563 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1565 WARN("invalid base64: %s\n", debugstr_w(base64));
1566 return 0;
1568 if (bin)
1569 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1570 n++;
1572 if ((base64[2] == '=') && (base64[3] == '='))
1573 break;
1574 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1575 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1577 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1578 return 0;
1580 if (bin)
1581 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1582 n++;
1584 if (base64[3] == '=')
1585 break;
1586 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1587 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1589 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1590 return 0;
1592 if (bin)
1593 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1594 n++;
1596 base64 += 4;
1599 return n;
1602 /***********************************************************************
1603 * HTTP_InsertAuthorization
1605 * Insert or delete the authorization field in the request header.
1607 static BOOL HTTP_InsertAuthorization( http_request_t *request, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1609 if (pAuthInfo)
1611 static const WCHAR wszSpace[] = {' ',0};
1612 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1613 unsigned int len;
1614 WCHAR *authorization = NULL;
1616 if (pAuthInfo->auth_data_len)
1618 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1619 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1620 authorization = heap_alloc((len+1)*sizeof(WCHAR));
1621 if (!authorization)
1622 return FALSE;
1624 strcpyW(authorization, pAuthInfo->scheme);
1625 strcatW(authorization, wszSpace);
1626 HTTP_EncodeBase64(pAuthInfo->auth_data,
1627 pAuthInfo->auth_data_len,
1628 authorization+strlenW(authorization));
1630 /* clear the data as it isn't valid now that it has been sent to the
1631 * server, unless it's Basic authentication which doesn't do
1632 * connection tracking */
1633 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1635 heap_free(pAuthInfo->auth_data);
1636 pAuthInfo->auth_data = NULL;
1637 pAuthInfo->auth_data_len = 0;
1641 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1643 HTTP_ProcessHeader(request, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1644 heap_free(authorization);
1646 return TRUE;
1649 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1651 static const WCHAR slash[] = { '/',0 };
1652 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','u',0 };
1653 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','u',0 };
1654 http_session_t *session = req->session;
1655 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1656 DWORD size;
1658 size = sizeof(new_location);
1659 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1661 URL_COMPONENTSW UrlComponents;
1663 if (!(url = heap_alloc(size + sizeof(WCHAR)))) return NULL;
1664 strcpyW( url, new_location );
1666 ZeroMemory(&UrlComponents,sizeof(URL_COMPONENTSW));
1667 if(InternetCrackUrlW(url, 0, 0, &UrlComponents)) goto done;
1668 heap_free(url);
1671 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1672 size += strlenW( session->hostName ) + strlenW( req->path );
1674 if (!(url = heap_alloc(size * sizeof(WCHAR)))) return NULL;
1676 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1677 sprintfW( url, formatSSL, session->hostName, session->hostPort );
1678 else
1679 sprintfW( url, format, session->hostName, session->hostPort );
1680 if (req->path[0] != '/') strcatW( url, slash );
1681 strcatW( url, req->path );
1683 done:
1684 TRACE("url=%s\n", debugstr_w(url));
1685 return url;
1688 /***********************************************************************
1689 * HTTP_DealWithProxy
1691 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_request_t *request)
1693 WCHAR buf[INTERNET_MAX_HOST_NAME_LENGTH];
1694 WCHAR protoProxy[INTERNET_MAX_URL_LENGTH];
1695 DWORD protoProxyLen = INTERNET_MAX_URL_LENGTH;
1696 WCHAR proxy[INTERNET_MAX_URL_LENGTH];
1697 static WCHAR szNul[] = { 0 };
1698 URL_COMPONENTSW UrlComponents;
1699 server_t *new_server;
1700 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1701 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1702 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1704 memset( &UrlComponents, 0, sizeof UrlComponents );
1705 UrlComponents.dwStructSize = sizeof UrlComponents;
1706 UrlComponents.lpszHostName = buf;
1707 UrlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
1709 if (!INTERNET_FindProxyForProtocol(hIC->proxy, protoHttp, protoProxy, &protoProxyLen))
1710 return FALSE;
1711 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1712 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1713 sprintfW(proxy, szFormat, protoProxy);
1714 else
1715 strcpyW(proxy, protoProxy);
1716 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1717 return FALSE;
1718 if( UrlComponents.dwHostNameLength == 0 )
1719 return FALSE;
1721 if( !request->path )
1722 request->path = szNul;
1724 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1725 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1727 new_server = get_server(UrlComponents.lpszHostName, UrlComponents.nPort);
1728 if(!new_server)
1729 return FALSE;
1731 server_release(request->server);
1732 request->server = new_server;
1734 TRACE("proxy server=%s port=%d\n", debugstr_w(new_server->name), new_server->port);
1735 return TRUE;
1738 static DWORD HTTP_ResolveName(http_request_t *request)
1740 server_t *server = request->server;
1741 socklen_t addr_len;
1742 const void *addr;
1744 if(server->addr_len)
1745 return ERROR_SUCCESS;
1747 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1748 INTERNET_STATUS_RESOLVING_NAME,
1749 server->name,
1750 (strlenW(server->name)+1) * sizeof(WCHAR));
1752 addr_len = sizeof(server->addr);
1753 if (!GetAddress(server->name, server->port, (struct sockaddr *)&server->addr, &addr_len))
1754 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1756 switch(server->addr.ss_family) {
1757 case AF_INET:
1758 addr = &((struct sockaddr_in *)&server->addr)->sin_addr;
1759 break;
1760 case AF_INET6:
1761 addr = &((struct sockaddr_in6 *)&server->addr)->sin6_addr;
1762 break;
1763 default:
1764 WARN("unsupported family %d\n", server->addr.ss_family);
1765 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1768 server->addr_len = addr_len;
1769 inet_ntop(server->addr.ss_family, addr, server->addr_str, sizeof(server->addr_str));
1770 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1771 INTERNET_STATUS_NAME_RESOLVED,
1772 server->addr_str, strlen(server->addr_str)+1);
1774 TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str);
1775 return ERROR_SUCCESS;
1778 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1780 static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
1781 static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
1782 static const WCHAR slash[] = { '/',0 };
1783 LPHTTPHEADERW host_header;
1784 LPCWSTR scheme;
1786 host_header = HTTP_GetHeader(req, hostW);
1787 if(!host_header)
1788 return FALSE;
1790 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1791 scheme = https;
1792 else
1793 scheme = http;
1794 strcpyW(buf, scheme);
1795 strcatW(buf, host_header->lpszValue);
1796 if (req->path[0] != '/')
1797 strcatW(buf, slash);
1798 strcatW(buf, req->path);
1799 return TRUE;
1803 /***********************************************************************
1804 * HTTPREQ_Destroy (internal)
1806 * Deallocate request handle
1809 static void HTTPREQ_Destroy(object_header_t *hdr)
1811 http_request_t *request = (http_request_t*) hdr;
1812 DWORD i;
1814 TRACE("\n");
1816 if(request->hCacheFile) {
1817 WCHAR url[INTERNET_MAX_URL_LENGTH];
1819 CloseHandle(request->hCacheFile);
1821 if(HTTP_GetRequestURL(request, url)) {
1822 DWORD headersLen;
1824 headersLen = request->rawHeaders ? strlenW(request->rawHeaders) : 0;
1825 CommitUrlCacheEntryW(url, request->cacheFile, request->expires,
1826 request->last_modified, NORMAL_CACHE_ENTRY,
1827 request->rawHeaders, headersLen, NULL, 0);
1830 heap_free(request->cacheFile);
1832 request->read_section.DebugInfo->Spare[0] = 0;
1833 DeleteCriticalSection( &request->read_section );
1834 WININET_Release(&request->session->hdr);
1836 destroy_authinfo(request->authInfo);
1837 destroy_authinfo(request->proxyAuthInfo);
1839 if(request->server)
1840 server_release(request->server);
1842 heap_free(request->path);
1843 heap_free(request->verb);
1844 heap_free(request->rawHeaders);
1845 heap_free(request->version);
1846 heap_free(request->statusText);
1848 for (i = 0; i < request->nCustHeaders; i++)
1850 heap_free(request->custHeaders[i].lpszField);
1851 heap_free(request->custHeaders[i].lpszValue);
1853 destroy_data_stream(request->data_stream);
1854 heap_free(request->custHeaders);
1857 static void http_release_netconn(http_request_t *req, BOOL reuse)
1859 TRACE("%p %p\n",req, req->netconn);
1861 if(!req->netconn)
1862 return;
1864 if(reuse && req->netconn->keep_alive) {
1865 BOOL run_collector;
1867 EnterCriticalSection(&connection_pool_cs);
1869 list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry);
1870 req->netconn->keep_until = GetTickCount64() + COLLECT_TIME;
1871 req->netconn = NULL;
1873 run_collector = !collector_running;
1874 collector_running = TRUE;
1876 LeaveCriticalSection(&connection_pool_cs);
1878 if(run_collector) {
1879 HANDLE thread = NULL;
1880 HMODULE module;
1882 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
1883 if(module)
1884 thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL);
1885 if(!thread) {
1886 EnterCriticalSection(&connection_pool_cs);
1887 collector_running = FALSE;
1888 LeaveCriticalSection(&connection_pool_cs);
1890 if(module)
1891 FreeLibrary(module);
1893 else
1894 CloseHandle(thread);
1896 return;
1899 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1900 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1902 free_netconn(req->netconn);
1903 req->netconn = NULL;
1905 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1906 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1909 static void drain_content(http_request_t *req)
1911 BOOL try_reuse;
1913 if (!req->netconn) return;
1915 if (req->contentLength == -1)
1916 try_reuse = FALSE;
1917 else if(!strcmpW(req->verb, szHEAD))
1918 try_reuse = TRUE;
1919 else
1920 try_reuse = req->data_stream->vtbl->drain_content(req->data_stream, req);
1922 http_release_netconn(req, try_reuse);
1925 static BOOL HTTP_KeepAlive(http_request_t *request)
1927 WCHAR szVersion[10];
1928 WCHAR szConnectionResponse[20];
1929 DWORD dwBufferSize = sizeof(szVersion);
1930 BOOL keepalive = FALSE;
1932 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1933 * the connection is keep-alive by default */
1934 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1935 && !strcmpiW(szVersion, g_szHttp1_1))
1937 keepalive = TRUE;
1940 dwBufferSize = sizeof(szConnectionResponse);
1941 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1942 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1944 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1947 return keepalive;
1950 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1952 http_request_t *req = (http_request_t*)hdr;
1954 drain_content(req);
1957 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1959 http_request_t *req = (http_request_t*)hdr;
1961 switch(option) {
1962 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1964 http_session_t *session = req->session;
1965 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1967 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1969 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1970 return ERROR_INSUFFICIENT_BUFFER;
1971 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1972 /* FIXME: can't get a SOCKET from our connection since we don't use
1973 * winsock
1975 info->Socket = 0;
1976 /* FIXME: get source port from req->netConnection */
1977 info->SourcePort = 0;
1978 info->DestPort = session->hostPort;
1979 info->Flags = 0;
1980 if (HTTP_KeepAlive(req))
1981 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1982 if (session->appInfo->proxy && session->appInfo->proxy[0] != 0)
1983 info->Flags |= IDSI_FLAG_PROXY;
1984 if (req->netconn->useSSL)
1985 info->Flags |= IDSI_FLAG_SECURE;
1987 return ERROR_SUCCESS;
1990 case INTERNET_OPTION_SECURITY_FLAGS:
1992 DWORD flags;
1994 if (*size < sizeof(ULONG))
1995 return ERROR_INSUFFICIENT_BUFFER;
1997 *size = sizeof(DWORD);
1998 flags = req->netconn ? req->netconn->security_flags : req->security_flags | req->server->security_flags;
1999 *(DWORD *)buffer = flags;
2001 TRACE("INTERNET_OPTION_SECURITY_FLAGS %x\n", flags);
2002 return ERROR_SUCCESS;
2005 case INTERNET_OPTION_HANDLE_TYPE:
2006 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2008 if (*size < sizeof(ULONG))
2009 return ERROR_INSUFFICIENT_BUFFER;
2011 *size = sizeof(DWORD);
2012 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
2013 return ERROR_SUCCESS;
2015 case INTERNET_OPTION_URL: {
2016 WCHAR url[INTERNET_MAX_URL_LENGTH];
2017 HTTPHEADERW *host;
2018 DWORD len;
2019 WCHAR *pch;
2021 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
2023 TRACE("INTERNET_OPTION_URL\n");
2025 host = HTTP_GetHeader(req, hostW);
2026 strcpyW(url, httpW);
2027 strcatW(url, host->lpszValue);
2028 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
2029 *pch = 0;
2030 strcatW(url, req->path);
2032 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
2034 if(unicode) {
2035 len = (strlenW(url)+1) * sizeof(WCHAR);
2036 if(*size < len)
2037 return ERROR_INSUFFICIENT_BUFFER;
2039 *size = len;
2040 strcpyW(buffer, url);
2041 return ERROR_SUCCESS;
2042 }else {
2043 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
2044 if(len > *size)
2045 return ERROR_INSUFFICIENT_BUFFER;
2047 *size = len;
2048 return ERROR_SUCCESS;
2052 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2053 INTERNET_CACHE_ENTRY_INFOW *info;
2054 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2055 WCHAR url[INTERNET_MAX_URL_LENGTH];
2056 DWORD nbytes, error;
2057 BOOL ret;
2059 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2061 if (*size < sizeof(*ts))
2063 *size = sizeof(*ts);
2064 return ERROR_INSUFFICIENT_BUFFER;
2066 nbytes = 0;
2067 HTTP_GetRequestURL(req, url);
2068 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
2069 error = GetLastError();
2070 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2072 if (!(info = heap_alloc(nbytes)))
2073 return ERROR_OUTOFMEMORY;
2075 GetUrlCacheEntryInfoW(url, info, &nbytes);
2077 ts->ftExpires = info->ExpireTime;
2078 ts->ftLastModified = info->LastModifiedTime;
2080 heap_free(info);
2081 *size = sizeof(*ts);
2082 return ERROR_SUCCESS;
2084 return error;
2087 case INTERNET_OPTION_DATAFILE_NAME: {
2088 DWORD req_size;
2090 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2092 if(!req->cacheFile) {
2093 *size = 0;
2094 return ERROR_INTERNET_ITEM_NOT_FOUND;
2097 if(unicode) {
2098 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
2099 if(*size < req_size)
2100 return ERROR_INSUFFICIENT_BUFFER;
2102 *size = req_size;
2103 memcpy(buffer, req->cacheFile, *size);
2104 return ERROR_SUCCESS;
2105 }else {
2106 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
2107 if (req_size > *size)
2108 return ERROR_INSUFFICIENT_BUFFER;
2110 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
2111 -1, buffer, *size, NULL, NULL);
2112 return ERROR_SUCCESS;
2116 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2117 PCCERT_CONTEXT context;
2119 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2120 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2121 return ERROR_INSUFFICIENT_BUFFER;
2124 context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2125 if(context) {
2126 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2127 DWORD len;
2129 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
2130 info->ftExpiry = context->pCertInfo->NotAfter;
2131 info->ftStart = context->pCertInfo->NotBefore;
2132 len = CertNameToStrA(context->dwCertEncodingType,
2133 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2134 info->lpszSubjectInfo = LocalAlloc(0, len);
2135 if(info->lpszSubjectInfo)
2136 CertNameToStrA(context->dwCertEncodingType,
2137 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2138 info->lpszSubjectInfo, len);
2139 len = CertNameToStrA(context->dwCertEncodingType,
2140 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2141 info->lpszIssuerInfo = LocalAlloc(0, len);
2142 if(info->lpszIssuerInfo)
2143 CertNameToStrA(context->dwCertEncodingType,
2144 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2145 info->lpszIssuerInfo, len);
2146 info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2147 CertFreeCertificateContext(context);
2148 return ERROR_SUCCESS;
2151 case INTERNET_OPTION_CONNECT_TIMEOUT:
2152 if (*size < sizeof(DWORD))
2153 return ERROR_INSUFFICIENT_BUFFER;
2155 *size = sizeof(DWORD);
2156 *(DWORD *)buffer = req->connect_timeout;
2157 return ERROR_SUCCESS;
2158 case INTERNET_OPTION_REQUEST_FLAGS: {
2159 DWORD flags = 0;
2161 if (*size < sizeof(DWORD))
2162 return ERROR_INSUFFICIENT_BUFFER;
2164 /* FIXME: Add support for:
2165 * INTERNET_REQFLAG_FROM_CACHE
2166 * INTERNET_REQFLAG_CACHE_WRITE_DISABLED
2169 if(req->session->appInfo->proxy)
2170 flags |= INTERNET_REQFLAG_VIA_PROXY;
2171 if(!req->rawHeaders)
2172 flags |= INTERNET_REQFLAG_NO_HEADERS;
2174 TRACE("INTERNET_OPTION_REQUEST_FLAGS returning %x\n", flags);
2176 *size = sizeof(DWORD);
2177 *(DWORD*)buffer = flags;
2178 return ERROR_SUCCESS;
2182 return INET_QueryOption(hdr, option, buffer, size, unicode);
2185 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2187 http_request_t *req = (http_request_t*)hdr;
2189 switch(option) {
2190 case INTERNET_OPTION_SECURITY_FLAGS:
2192 DWORD flags;
2194 if (!buffer || size != sizeof(DWORD))
2195 return ERROR_INVALID_PARAMETER;
2196 flags = *(DWORD *)buffer;
2197 TRACE("%08x\n", flags);
2198 req->security_flags = flags;
2199 if(req->netconn)
2200 req->netconn->security_flags = flags;
2201 return ERROR_SUCCESS;
2203 case INTERNET_OPTION_CONNECT_TIMEOUT:
2204 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2205 req->connect_timeout = *(DWORD *)buffer;
2206 return ERROR_SUCCESS;
2208 case INTERNET_OPTION_SEND_TIMEOUT:
2209 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2210 req->send_timeout = *(DWORD *)buffer;
2211 return ERROR_SUCCESS;
2213 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2214 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2215 req->receive_timeout = *(DWORD *)buffer;
2216 return ERROR_SUCCESS;
2218 case INTERNET_OPTION_USERNAME:
2219 heap_free(req->session->userName);
2220 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2221 return ERROR_SUCCESS;
2223 case INTERNET_OPTION_PASSWORD:
2224 heap_free(req->session->password);
2225 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2226 return ERROR_SUCCESS;
2227 case INTERNET_OPTION_HTTP_DECODING:
2228 if(size != sizeof(BOOL))
2229 return ERROR_INVALID_PARAMETER;
2230 req->decoding = *(BOOL*)buffer;
2231 return ERROR_SUCCESS;
2234 return INET_SetOption(hdr, option, buffer, size);
2237 /* read some more data into the read buffer (the read section must be held) */
2238 static DWORD read_more_data( http_request_t *req, int maxlen )
2240 DWORD res;
2241 int len;
2243 if (req->read_pos)
2245 /* move existing data to the start of the buffer */
2246 if(req->read_size)
2247 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2248 req->read_pos = 0;
2251 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2253 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2254 maxlen - req->read_size, 0, &len );
2255 if(res == ERROR_SUCCESS)
2256 req->read_size += len;
2258 return res;
2261 /* remove some amount of data from the read buffer (the read section must be held) */
2262 static void remove_data( http_request_t *req, int count )
2264 if (!(req->read_size -= count)) req->read_pos = 0;
2265 else req->read_pos += count;
2268 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2270 int count, bytes_read, pos = 0;
2271 DWORD res;
2273 EnterCriticalSection( &req->read_section );
2274 for (;;)
2276 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2278 if (eol)
2280 count = eol - (req->read_buf + req->read_pos);
2281 bytes_read = count + 1;
2283 else count = bytes_read = req->read_size;
2285 count = min( count, *len - pos );
2286 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2287 pos += count;
2288 remove_data( req, bytes_read );
2289 if (eol) break;
2291 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2293 *len = 0;
2294 TRACE( "returning empty string %u\n", res);
2295 LeaveCriticalSection( &req->read_section );
2296 INTERNET_SetLastError(res);
2297 return FALSE;
2300 LeaveCriticalSection( &req->read_section );
2302 if (pos < *len)
2304 if (pos && buffer[pos - 1] == '\r') pos--;
2305 *len = pos + 1;
2307 buffer[*len - 1] = 0;
2308 TRACE( "returning %s\n", debugstr_a(buffer));
2309 return TRUE;
2312 /* check if we have reached the end of the data to read (the read section must be held) */
2313 static BOOL end_of_read_data( http_request_t *req )
2315 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2318 /* fetch some more data into the read buffer (the read section must be held) */
2319 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2321 DWORD res, read=0;
2323 if(req->read_size == sizeof(req->read_buf))
2324 return ERROR_SUCCESS;
2326 if(req->read_pos) {
2327 if(req->read_size)
2328 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2329 req->read_pos = 0;
2332 res = req->data_stream->vtbl->read(req->data_stream, req, req->read_buf+req->read_size,
2333 sizeof(req->read_buf)-req->read_size, &read, read_mode);
2334 req->read_size += read;
2336 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2337 if(read_bytes)
2338 *read_bytes = read;
2339 return res;
2342 /* return the size of data available to be read immediately (the read section must be held) */
2343 static DWORD get_avail_data( http_request_t *req )
2345 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2348 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2350 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2351 DWORD avail = 0;
2353 if(req->netconn)
2354 NETCON_query_data_available(req->netconn, &avail);
2355 return netconn_stream->content_length == ~0u
2356 ? avail
2357 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2360 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2362 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2363 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2366 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2367 DWORD *read, read_mode_t read_mode)
2369 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2370 int len = 0;
2372 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2374 if(read_mode == READMODE_NOBLOCK)
2375 size = min(size, netconn_get_avail_data(stream, req));
2377 if(size && req->netconn) {
2378 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2379 len = 0;
2380 if(!len)
2381 netconn_stream->content_length = netconn_stream->content_read;
2384 netconn_stream->content_read += *read = len;
2385 TRACE("read %u bytes\n", len);
2386 return ERROR_SUCCESS;
2389 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2391 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2392 BYTE buf[1024];
2393 DWORD avail;
2394 int len;
2396 if(netconn_end_of_data(stream, req))
2397 return TRUE;
2399 do {
2400 avail = netconn_get_avail_data(stream, req);
2401 if(!avail)
2402 return FALSE;
2404 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2405 return FALSE;
2407 netconn_stream->content_read += len;
2408 }while(netconn_stream->content_read < netconn_stream->content_length);
2410 return TRUE;
2413 static void netconn_destroy(data_stream_t *stream)
2417 static const data_stream_vtbl_t netconn_stream_vtbl = {
2418 netconn_get_avail_data,
2419 netconn_end_of_data,
2420 netconn_read,
2421 netconn_drain_content,
2422 netconn_destroy
2425 /* read some more data into the read buffer (the read section must be held) */
2426 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2428 DWORD res;
2429 int len;
2431 if (stream->buf_pos)
2433 /* move existing data to the start of the buffer */
2434 if(stream->buf_size)
2435 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2436 stream->buf_pos = 0;
2439 if (maxlen == -1) maxlen = sizeof(stream->buf);
2441 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2442 maxlen - stream->buf_size, 0, &len );
2443 if(res == ERROR_SUCCESS)
2444 stream->buf_size += len;
2446 return res;
2449 /* remove some amount of data from the read buffer (the read section must be held) */
2450 static void remove_chunked_data(chunked_stream_t *stream, int count)
2452 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2453 else stream->buf_pos += count;
2456 /* discard data contents until we reach end of line (the read section must be held) */
2457 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2459 DWORD res;
2463 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2464 if (eol)
2466 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2467 break;
2469 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2470 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2471 } while (stream->buf_size);
2472 return ERROR_SUCCESS;
2475 /* read the size of the next chunk (the read section must be held) */
2476 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2478 /* TODOO */
2479 DWORD chunk_size = 0, res;
2481 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2482 return res;
2484 for (;;)
2486 while (stream->buf_size)
2488 char ch = stream->buf[stream->buf_pos];
2489 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2490 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2491 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2492 else if (ch == ';' || ch == '\r' || ch == '\n')
2494 TRACE( "reading %u byte chunk\n", chunk_size );
2495 stream->chunk_size = chunk_size;
2496 req->contentLength += chunk_size;
2497 return discard_chunked_eol(stream, req);
2499 remove_chunked_data(stream, 1);
2501 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2502 if (!stream->buf_size)
2504 stream->chunk_size = 0;
2505 return ERROR_SUCCESS;
2510 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2512 /* Allow reading only from read buffer */
2513 return 0;
2516 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2518 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2519 return !chunked_stream->chunk_size;
2522 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2523 DWORD *read, read_mode_t read_mode)
2525 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2526 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2528 if(chunked_stream->chunk_size == ~0u) {
2529 res = start_next_chunk(chunked_stream, req);
2530 if(res != ERROR_SUCCESS)
2531 return res;
2534 while(size && chunked_stream->chunk_size) {
2535 if(chunked_stream->buf_size) {
2536 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2538 /* this could block */
2539 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2540 break;
2542 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2543 remove_chunked_data(chunked_stream, read_bytes);
2544 }else {
2545 read_bytes = min(size, chunked_stream->chunk_size);
2547 if(read_mode == READMODE_NOBLOCK) {
2548 DWORD avail;
2550 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2551 break;
2552 if(read_bytes > avail)
2553 read_bytes = avail;
2555 /* this could block */
2556 if(read_bytes == chunked_stream->chunk_size)
2557 break;
2560 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2561 if(res != ERROR_SUCCESS)
2562 break;
2565 chunked_stream->chunk_size -= read_bytes;
2566 size -= read_bytes;
2567 ret_read += read_bytes;
2568 if(!chunked_stream->chunk_size) {
2569 assert(read_mode != READMODE_NOBLOCK);
2570 res = start_next_chunk(chunked_stream, req);
2571 if(res != ERROR_SUCCESS)
2572 break;
2575 if(read_mode == READMODE_ASYNC)
2576 read_mode = READMODE_NOBLOCK;
2579 TRACE("read %u bytes\n", ret_read);
2580 *read = ret_read;
2581 return res;
2584 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2586 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2588 /* FIXME: we can do better */
2589 return !chunked_stream->chunk_size;
2592 static void chunked_destroy(data_stream_t *stream)
2594 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2595 heap_free(chunked_stream);
2598 static const data_stream_vtbl_t chunked_stream_vtbl = {
2599 chunked_get_avail_data,
2600 chunked_end_of_data,
2601 chunked_read,
2602 chunked_drain_content,
2603 chunked_destroy
2606 /* set the request content length based on the headers */
2607 static DWORD set_content_length(http_request_t *request)
2609 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2610 WCHAR encoding[20];
2611 DWORD size;
2613 if(request->status_code == HTTP_STATUS_NO_CONTENT) {
2614 request->contentLength = request->netconn_stream.content_length = 0;
2615 return ERROR_SUCCESS;
2618 size = sizeof(request->contentLength);
2619 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2620 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2621 request->contentLength = ~0u;
2622 request->netconn_stream.content_length = request->contentLength;
2623 request->netconn_stream.content_read = request->read_size;
2625 size = sizeof(encoding);
2626 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2627 !strcmpiW(encoding, szChunked))
2629 chunked_stream_t *chunked_stream;
2631 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2632 if(!chunked_stream)
2633 return ERROR_OUTOFMEMORY;
2635 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2636 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2637 chunked_stream->chunk_size = ~0u;
2639 if(request->read_size) {
2640 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2641 chunked_stream->buf_size = request->read_size;
2642 request->read_size = request->read_pos = 0;
2645 request->data_stream = &chunked_stream->data_stream;
2646 request->contentLength = ~0u;
2647 request->read_chunked = TRUE;
2650 if(request->decoding) {
2651 int encoding_idx;
2653 static const WCHAR gzipW[] = {'g','z','i','p',0};
2655 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2656 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2657 return init_gzip_stream(request);
2660 return ERROR_SUCCESS;
2663 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2665 INTERNET_ASYNC_RESULT iar;
2667 iar.dwResult = result;
2668 iar.dwError = error;
2670 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2671 sizeof(INTERNET_ASYNC_RESULT));
2674 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2676 DWORD res, read = 0, avail = 0;
2677 read_mode_t mode;
2679 TRACE("%p\n", req);
2681 EnterCriticalSection( &req->read_section );
2683 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2684 res = refill_read_buffer(req, mode, &read);
2685 if(res == ERROR_SUCCESS && !first_notif)
2686 avail = get_avail_data(req);
2688 LeaveCriticalSection( &req->read_section );
2690 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2691 WARN("res %u read %u, closing connection\n", res, read);
2692 http_release_netconn(req, FALSE);
2695 if(res == ERROR_SUCCESS)
2696 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2697 else
2698 send_request_complete(req, 0, res);
2701 /* read data from the http connection (the read section must be held) */
2702 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2704 DWORD current_read = 0, ret_read = 0;
2705 read_mode_t read_mode;
2706 DWORD res = ERROR_SUCCESS;
2708 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2710 EnterCriticalSection( &req->read_section );
2712 if(req->read_size) {
2713 ret_read = min(size, req->read_size);
2714 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2715 req->read_size -= ret_read;
2716 req->read_pos += ret_read;
2717 if(read_mode == READMODE_ASYNC)
2718 read_mode = READMODE_NOBLOCK;
2721 if(ret_read < size) {
2722 res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, read_mode);
2723 ret_read += current_read;
2726 LeaveCriticalSection( &req->read_section );
2728 *read = ret_read;
2729 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2731 if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
2732 BOOL res;
2733 DWORD written;
2735 res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
2736 if(!res)
2737 WARN("WriteFile failed: %u\n", GetLastError());
2740 if(size && !ret_read)
2741 http_release_netconn(req, res == ERROR_SUCCESS);
2743 return res;
2747 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2749 http_request_t *req = (http_request_t*)hdr;
2750 DWORD res;
2752 EnterCriticalSection( &req->read_section );
2753 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2754 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2756 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2757 if(res == ERROR_SUCCESS)
2758 res = hdr->dwError;
2759 LeaveCriticalSection( &req->read_section );
2761 return res;
2764 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2766 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2767 http_request_t *req = (http_request_t*)workRequest->hdr;
2768 DWORD res;
2770 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2772 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2773 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2775 send_request_complete(req, res == ERROR_SUCCESS, res);
2778 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2779 DWORD flags, DWORD_PTR context)
2781 http_request_t *req = (http_request_t*)hdr;
2782 DWORD res, size, read, error = ERROR_SUCCESS;
2784 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2785 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2787 if (buffers->dwStructSize != sizeof(*buffers))
2788 return ERROR_INVALID_PARAMETER;
2790 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2792 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2794 WORKREQUEST workRequest;
2796 if (TryEnterCriticalSection( &req->read_section ))
2798 if (get_avail_data(req))
2800 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2801 &buffers->dwBufferLength, FALSE);
2802 size = buffers->dwBufferLength;
2803 LeaveCriticalSection( &req->read_section );
2804 goto done;
2806 LeaveCriticalSection( &req->read_section );
2809 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2810 workRequest.hdr = WININET_AddRef(&req->hdr);
2811 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2813 INTERNET_AsyncCall(&workRequest);
2815 return ERROR_IO_PENDING;
2818 read = 0;
2819 size = buffers->dwBufferLength;
2821 EnterCriticalSection( &req->read_section );
2822 if(hdr->dwError == ERROR_SUCCESS)
2823 hdr->dwError = INTERNET_HANDLE_IN_USE;
2824 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2825 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2827 while(1) {
2828 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2829 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2830 if(res != ERROR_SUCCESS)
2831 break;
2833 read += buffers->dwBufferLength;
2834 if(read == size || end_of_read_data(req))
2835 break;
2837 LeaveCriticalSection( &req->read_section );
2839 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2840 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2841 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2842 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2844 EnterCriticalSection( &req->read_section );
2847 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2848 hdr->dwError = ERROR_SUCCESS;
2849 else
2850 error = hdr->dwError;
2852 LeaveCriticalSection( &req->read_section );
2853 size = buffers->dwBufferLength;
2854 buffers->dwBufferLength = read;
2856 done:
2857 if (res == ERROR_SUCCESS) {
2858 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2859 &size, sizeof(size));
2862 return res==ERROR_SUCCESS ? error : res;
2865 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2867 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2868 http_request_t *req = (http_request_t*)workRequest->hdr;
2869 DWORD res;
2871 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2873 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2874 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2876 send_request_complete(req, res == ERROR_SUCCESS, res);
2879 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2880 DWORD flags, DWORD_PTR context)
2883 http_request_t *req = (http_request_t*)hdr;
2884 DWORD res, size, read, error = ERROR_SUCCESS;
2886 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2887 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2889 if (buffers->dwStructSize != sizeof(*buffers))
2890 return ERROR_INVALID_PARAMETER;
2892 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2894 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2896 WORKREQUEST workRequest;
2898 if (TryEnterCriticalSection( &req->read_section ))
2900 if (get_avail_data(req))
2902 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2903 &buffers->dwBufferLength, FALSE);
2904 size = buffers->dwBufferLength;
2905 LeaveCriticalSection( &req->read_section );
2906 goto done;
2908 LeaveCriticalSection( &req->read_section );
2911 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2912 workRequest.hdr = WININET_AddRef(&req->hdr);
2913 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2915 INTERNET_AsyncCall(&workRequest);
2917 return ERROR_IO_PENDING;
2920 read = 0;
2921 size = buffers->dwBufferLength;
2923 EnterCriticalSection( &req->read_section );
2924 if(hdr->dwError == ERROR_SUCCESS)
2925 hdr->dwError = INTERNET_HANDLE_IN_USE;
2926 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2927 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2929 while(1) {
2930 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2931 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2932 if(res != ERROR_SUCCESS)
2933 break;
2935 read += buffers->dwBufferLength;
2936 if(read == size || end_of_read_data(req))
2937 break;
2939 LeaveCriticalSection( &req->read_section );
2941 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2942 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2943 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2944 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2946 EnterCriticalSection( &req->read_section );
2949 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2950 hdr->dwError = ERROR_SUCCESS;
2951 else
2952 error = hdr->dwError;
2954 LeaveCriticalSection( &req->read_section );
2955 size = buffers->dwBufferLength;
2956 buffers->dwBufferLength = read;
2958 done:
2959 if (res == ERROR_SUCCESS) {
2960 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2961 &size, sizeof(size));
2964 return res==ERROR_SUCCESS ? error : res;
2967 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2969 DWORD res;
2970 http_request_t *request = (http_request_t*)hdr;
2972 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2974 *written = 0;
2975 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
2976 if (res == ERROR_SUCCESS)
2977 request->bytesWritten += *written;
2979 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2980 return res;
2983 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2985 http_request_t *req = (http_request_t*)workRequest->hdr;
2987 HTTP_ReceiveRequestData(req, FALSE);
2990 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2992 http_request_t *req = (http_request_t*)hdr;
2994 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2996 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2998 WORKREQUEST workRequest;
3000 /* never wait, if we can't enter the section we queue an async request right away */
3001 if (TryEnterCriticalSection( &req->read_section ))
3003 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
3004 if ((*available = get_avail_data( req ))) goto done;
3005 if (end_of_read_data( req )) goto done;
3006 LeaveCriticalSection( &req->read_section );
3009 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
3010 workRequest.hdr = WININET_AddRef( &req->hdr );
3012 INTERNET_AsyncCall(&workRequest);
3014 return ERROR_IO_PENDING;
3017 EnterCriticalSection( &req->read_section );
3019 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
3021 refill_read_buffer( req, READMODE_ASYNC, NULL );
3022 *available = get_avail_data( req );
3025 done:
3026 LeaveCriticalSection( &req->read_section );
3028 TRACE( "returning %u\n", *available );
3029 return ERROR_SUCCESS;
3032 static const object_vtbl_t HTTPREQVtbl = {
3033 HTTPREQ_Destroy,
3034 HTTPREQ_CloseConnection,
3035 HTTPREQ_QueryOption,
3036 HTTPREQ_SetOption,
3037 HTTPREQ_ReadFile,
3038 HTTPREQ_ReadFileExA,
3039 HTTPREQ_ReadFileExW,
3040 HTTPREQ_WriteFile,
3041 HTTPREQ_QueryDataAvailable,
3042 NULL
3045 /***********************************************************************
3046 * HTTP_HttpOpenRequestW (internal)
3048 * Open a HTTP request handle
3050 * RETURNS
3051 * HINTERNET a HTTP request handle on success
3052 * NULL on failure
3055 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3056 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3057 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3058 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3060 appinfo_t *hIC = session->appInfo;
3061 http_request_t *request;
3062 INTERNET_PORT port;
3063 DWORD len, res = ERROR_SUCCESS;
3065 TRACE("-->\n");
3067 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3068 if(!request)
3069 return ERROR_OUTOFMEMORY;
3071 request->hdr.htype = WH_HHTTPREQ;
3072 request->hdr.dwFlags = dwFlags;
3073 request->hdr.dwContext = dwContext;
3074 request->contentLength = ~0u;
3076 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3077 request->data_stream = &request->netconn_stream.data_stream;
3078 request->connect_timeout = session->connect_timeout;
3079 request->send_timeout = session->send_timeout;
3080 request->receive_timeout = session->receive_timeout;
3082 InitializeCriticalSection( &request->read_section );
3083 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3085 WININET_AddRef( &session->hdr );
3086 request->session = session;
3087 list_add_head( &session->hdr.children, &request->hdr.entry );
3089 port = session->hostPort;
3090 if(port == INTERNET_INVALID_PORT_NUMBER)
3091 port = dwFlags & INTERNET_FLAG_SECURE ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT;
3093 request->server = get_server(session->hostName, port);
3094 if(!request->server) {
3095 WININET_Release(&request->hdr);
3096 return ERROR_OUTOFMEMORY;
3099 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3100 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3101 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3102 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3104 if (lpszObjectName && *lpszObjectName) {
3105 HRESULT rc;
3107 len = 0;
3108 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3109 if (rc != E_POINTER)
3110 len = strlenW(lpszObjectName)+1;
3111 request->path = heap_alloc(len*sizeof(WCHAR));
3112 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3113 URL_ESCAPE_SPACES_ONLY);
3114 if (rc != S_OK)
3116 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3117 strcpyW(request->path,lpszObjectName);
3119 }else {
3120 static const WCHAR slashW[] = {'/',0};
3122 request->path = heap_strdupW(slashW);
3125 if (lpszReferrer && *lpszReferrer)
3126 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3128 if (lpszAcceptTypes)
3130 int i;
3131 for (i = 0; lpszAcceptTypes[i]; i++)
3133 if (!*lpszAcceptTypes[i]) continue;
3134 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3135 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3136 HTTP_ADDHDR_FLAG_REQ |
3137 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3141 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3142 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
3144 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
3145 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
3146 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
3148 WCHAR *host_name;
3150 static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
3152 host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
3153 if (!host_name) {
3154 res = ERROR_OUTOFMEMORY;
3155 goto lend;
3158 sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
3159 HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3160 heap_free(host_name);
3162 else
3163 HTTP_ProcessHeader(request, hostW, session->hostName,
3164 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3166 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3167 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3168 INTERNET_DEFAULT_HTTPS_PORT :
3169 INTERNET_DEFAULT_HTTP_PORT);
3171 if (hIC->proxy && hIC->proxy[0])
3172 HTTP_DealWithProxy( hIC, session, request );
3174 INTERNET_SendCallback(&session->hdr, dwContext,
3175 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3176 sizeof(HINTERNET));
3178 lend:
3179 TRACE("<-- %u (%p)\n", res, request);
3181 if(res != ERROR_SUCCESS) {
3182 WININET_Release( &request->hdr );
3183 *ret = NULL;
3184 return res;
3187 *ret = request->hdr.hInternet;
3188 return ERROR_SUCCESS;
3191 /***********************************************************************
3192 * HttpOpenRequestW (WININET.@)
3194 * Open a HTTP request handle
3196 * RETURNS
3197 * HINTERNET a HTTP request handle on success
3198 * NULL on failure
3201 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3202 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3203 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3204 DWORD dwFlags, DWORD_PTR dwContext)
3206 http_session_t *session;
3207 HINTERNET handle = NULL;
3208 DWORD res;
3210 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3211 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3212 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3213 dwFlags, dwContext);
3214 if(lpszAcceptTypes!=NULL)
3216 int i;
3217 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3218 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3221 session = (http_session_t*) get_handle_object( hHttpSession );
3222 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3224 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3225 goto lend;
3229 * My tests seem to show that the windows version does not
3230 * become asynchronous until after this point. And anyhow
3231 * if this call was asynchronous then how would you get the
3232 * necessary HINTERNET pointer returned by this function.
3235 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3236 lpszVersion, lpszReferrer, lpszAcceptTypes,
3237 dwFlags, dwContext, &handle);
3238 lend:
3239 if( session )
3240 WININET_Release( &session->hdr );
3241 TRACE("returning %p\n", handle);
3242 if(res != ERROR_SUCCESS)
3243 SetLastError(res);
3244 return handle;
3247 static const LPCWSTR header_lookup[] = {
3248 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3249 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3250 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3251 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3252 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3253 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3254 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3255 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3256 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3257 szDate, /* HTTP_QUERY_DATE = 9 */
3258 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3259 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3260 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3261 szURI, /* HTTP_QUERY_URI = 13 */
3262 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3263 NULL, /* HTTP_QUERY_COST = 15 */
3264 NULL, /* HTTP_QUERY_LINK = 16 */
3265 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3266 NULL, /* HTTP_QUERY_VERSION = 18 */
3267 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3268 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3269 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3270 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3271 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3272 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3273 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3274 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3275 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3276 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3277 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3278 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3279 NULL, /* HTTP_QUERY_FROM = 31 */
3280 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3281 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3282 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3283 szReferer, /* HTTP_QUERY_REFERER = 35 */
3284 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3285 szServer, /* HTTP_QUERY_SERVER = 37 */
3286 NULL, /* HTTP_TITLE = 38 */
3287 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3288 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3289 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3290 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3291 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3292 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3293 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3294 NULL, /* HTTP_QUERY_REFRESH = 46 */
3295 szContent_Disposition, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3296 szAge, /* HTTP_QUERY_AGE = 48 */
3297 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3298 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3299 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3300 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3301 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3302 szETag, /* HTTP_QUERY_ETAG = 54 */
3303 hostW, /* HTTP_QUERY_HOST = 55 */
3304 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3305 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3306 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3307 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3308 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3309 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3310 szRange, /* HTTP_QUERY_RANGE = 62 */
3311 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3312 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3313 szVary, /* HTTP_QUERY_VARY = 65 */
3314 szVia, /* HTTP_QUERY_VIA = 66 */
3315 szWarning, /* HTTP_QUERY_WARNING = 67 */
3316 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3317 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3318 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3321 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3323 /***********************************************************************
3324 * HTTP_HttpQueryInfoW (internal)
3326 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3327 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3329 LPHTTPHEADERW lphttpHdr = NULL;
3330 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3331 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3332 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3333 INT index = -1;
3335 /* Find requested header structure */
3336 switch (level)
3338 case HTTP_QUERY_CUSTOM:
3339 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3340 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3341 break;
3342 case HTTP_QUERY_RAW_HEADERS_CRLF:
3344 LPWSTR headers;
3345 DWORD len = 0;
3346 DWORD res = ERROR_INVALID_PARAMETER;
3348 if (request_only)
3349 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3350 else
3351 headers = request->rawHeaders;
3353 if (headers)
3354 len = strlenW(headers) * sizeof(WCHAR);
3356 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3358 len += sizeof(WCHAR);
3359 res = ERROR_INSUFFICIENT_BUFFER;
3361 else if (lpBuffer)
3363 if (headers)
3364 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3365 else
3367 len = strlenW(szCrLf) * sizeof(WCHAR);
3368 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3370 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3371 res = ERROR_SUCCESS;
3373 *lpdwBufferLength = len;
3375 if (request_only) heap_free(headers);
3376 return res;
3378 case HTTP_QUERY_RAW_HEADERS:
3380 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3381 DWORD i, size = 0;
3382 LPWSTR pszString = lpBuffer;
3384 for (i = 0; ppszRawHeaderLines[i]; i++)
3385 size += strlenW(ppszRawHeaderLines[i]) + 1;
3387 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3389 HTTP_FreeTokens(ppszRawHeaderLines);
3390 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3391 return ERROR_INSUFFICIENT_BUFFER;
3393 if (pszString)
3395 for (i = 0; ppszRawHeaderLines[i]; i++)
3397 DWORD len = strlenW(ppszRawHeaderLines[i]);
3398 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3399 pszString += len+1;
3401 *pszString = '\0';
3402 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3404 *lpdwBufferLength = size * sizeof(WCHAR);
3405 HTTP_FreeTokens(ppszRawHeaderLines);
3407 return ERROR_SUCCESS;
3409 case HTTP_QUERY_STATUS_TEXT:
3410 if (request->statusText)
3412 DWORD len = strlenW(request->statusText);
3413 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3415 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3416 return ERROR_INSUFFICIENT_BUFFER;
3418 if (lpBuffer)
3420 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3421 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3423 *lpdwBufferLength = len * sizeof(WCHAR);
3424 return ERROR_SUCCESS;
3426 break;
3427 case HTTP_QUERY_VERSION:
3428 if (request->version)
3430 DWORD len = strlenW(request->version);
3431 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3433 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3434 return ERROR_INSUFFICIENT_BUFFER;
3436 if (lpBuffer)
3438 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3439 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3441 *lpdwBufferLength = len * sizeof(WCHAR);
3442 return ERROR_SUCCESS;
3444 break;
3445 case HTTP_QUERY_CONTENT_ENCODING:
3446 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3447 requested_index,request_only);
3448 break;
3449 case HTTP_QUERY_STATUS_CODE: {
3450 DWORD res = ERROR_SUCCESS;
3452 if(request_only)
3453 return ERROR_HTTP_INVALID_QUERY_REQUEST;
3455 if(requested_index)
3456 break;
3458 if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3459 if(*lpdwBufferLength >= sizeof(DWORD))
3460 *(DWORD*)lpBuffer = request->status_code;
3461 else
3462 res = ERROR_INSUFFICIENT_BUFFER;
3463 *lpdwBufferLength = sizeof(DWORD);
3464 }else {
3465 WCHAR buf[12];
3466 DWORD size;
3467 static const WCHAR formatW[] = {'%','u',0};
3469 size = (sprintfW(buf, formatW, request->status_code)+1) * sizeof(WCHAR);
3471 if(size <= *lpdwBufferLength)
3472 memcpy(lpBuffer, buf, size);
3473 else
3474 res = ERROR_INSUFFICIENT_BUFFER;
3476 *lpdwBufferLength = size;
3478 return res;
3480 default:
3481 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3483 if (level < LAST_TABLE_HEADER && header_lookup[level])
3484 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3485 requested_index,request_only);
3488 if (index >= 0)
3489 lphttpHdr = &request->custHeaders[index];
3491 /* Ensure header satisfies requested attributes */
3492 if (!lphttpHdr ||
3493 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3494 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3496 return ERROR_HTTP_HEADER_NOT_FOUND;
3499 if (lpdwIndex) (*lpdwIndex)++;
3501 /* coalesce value to requested type */
3502 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3504 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3505 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3507 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3509 time_t tmpTime;
3510 struct tm tmpTM;
3511 SYSTEMTIME *STHook;
3513 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3515 tmpTM = *gmtime(&tmpTime);
3516 STHook = (SYSTEMTIME *)lpBuffer;
3517 STHook->wDay = tmpTM.tm_mday;
3518 STHook->wHour = tmpTM.tm_hour;
3519 STHook->wMilliseconds = 0;
3520 STHook->wMinute = tmpTM.tm_min;
3521 STHook->wDayOfWeek = tmpTM.tm_wday;
3522 STHook->wMonth = tmpTM.tm_mon + 1;
3523 STHook->wSecond = tmpTM.tm_sec;
3524 STHook->wYear = tmpTM.tm_year;
3526 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3527 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3528 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3530 else if (lphttpHdr->lpszValue)
3532 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3534 if (len > *lpdwBufferLength)
3536 *lpdwBufferLength = len;
3537 return ERROR_INSUFFICIENT_BUFFER;
3539 if (lpBuffer)
3541 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3542 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3544 *lpdwBufferLength = len - sizeof(WCHAR);
3546 return ERROR_SUCCESS;
3549 /***********************************************************************
3550 * HttpQueryInfoW (WININET.@)
3552 * Queries for information about an HTTP request
3554 * RETURNS
3555 * TRUE on success
3556 * FALSE on failure
3559 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3560 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3562 http_request_t *request;
3563 DWORD res;
3565 if (TRACE_ON(wininet)) {
3566 #define FE(x) { x, #x }
3567 static const wininet_flag_info query_flags[] = {
3568 FE(HTTP_QUERY_MIME_VERSION),
3569 FE(HTTP_QUERY_CONTENT_TYPE),
3570 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3571 FE(HTTP_QUERY_CONTENT_ID),
3572 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3573 FE(HTTP_QUERY_CONTENT_LENGTH),
3574 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3575 FE(HTTP_QUERY_ALLOW),
3576 FE(HTTP_QUERY_PUBLIC),
3577 FE(HTTP_QUERY_DATE),
3578 FE(HTTP_QUERY_EXPIRES),
3579 FE(HTTP_QUERY_LAST_MODIFIED),
3580 FE(HTTP_QUERY_MESSAGE_ID),
3581 FE(HTTP_QUERY_URI),
3582 FE(HTTP_QUERY_DERIVED_FROM),
3583 FE(HTTP_QUERY_COST),
3584 FE(HTTP_QUERY_LINK),
3585 FE(HTTP_QUERY_PRAGMA),
3586 FE(HTTP_QUERY_VERSION),
3587 FE(HTTP_QUERY_STATUS_CODE),
3588 FE(HTTP_QUERY_STATUS_TEXT),
3589 FE(HTTP_QUERY_RAW_HEADERS),
3590 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3591 FE(HTTP_QUERY_CONNECTION),
3592 FE(HTTP_QUERY_ACCEPT),
3593 FE(HTTP_QUERY_ACCEPT_CHARSET),
3594 FE(HTTP_QUERY_ACCEPT_ENCODING),
3595 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3596 FE(HTTP_QUERY_AUTHORIZATION),
3597 FE(HTTP_QUERY_CONTENT_ENCODING),
3598 FE(HTTP_QUERY_FORWARDED),
3599 FE(HTTP_QUERY_FROM),
3600 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3601 FE(HTTP_QUERY_LOCATION),
3602 FE(HTTP_QUERY_ORIG_URI),
3603 FE(HTTP_QUERY_REFERER),
3604 FE(HTTP_QUERY_RETRY_AFTER),
3605 FE(HTTP_QUERY_SERVER),
3606 FE(HTTP_QUERY_TITLE),
3607 FE(HTTP_QUERY_USER_AGENT),
3608 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3609 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3610 FE(HTTP_QUERY_ACCEPT_RANGES),
3611 FE(HTTP_QUERY_SET_COOKIE),
3612 FE(HTTP_QUERY_COOKIE),
3613 FE(HTTP_QUERY_REQUEST_METHOD),
3614 FE(HTTP_QUERY_REFRESH),
3615 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3616 FE(HTTP_QUERY_AGE),
3617 FE(HTTP_QUERY_CACHE_CONTROL),
3618 FE(HTTP_QUERY_CONTENT_BASE),
3619 FE(HTTP_QUERY_CONTENT_LOCATION),
3620 FE(HTTP_QUERY_CONTENT_MD5),
3621 FE(HTTP_QUERY_CONTENT_RANGE),
3622 FE(HTTP_QUERY_ETAG),
3623 FE(HTTP_QUERY_HOST),
3624 FE(HTTP_QUERY_IF_MATCH),
3625 FE(HTTP_QUERY_IF_NONE_MATCH),
3626 FE(HTTP_QUERY_IF_RANGE),
3627 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3628 FE(HTTP_QUERY_MAX_FORWARDS),
3629 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3630 FE(HTTP_QUERY_RANGE),
3631 FE(HTTP_QUERY_TRANSFER_ENCODING),
3632 FE(HTTP_QUERY_UPGRADE),
3633 FE(HTTP_QUERY_VARY),
3634 FE(HTTP_QUERY_VIA),
3635 FE(HTTP_QUERY_WARNING),
3636 FE(HTTP_QUERY_CUSTOM)
3638 static const wininet_flag_info modifier_flags[] = {
3639 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3640 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3641 FE(HTTP_QUERY_FLAG_NUMBER),
3642 FE(HTTP_QUERY_FLAG_COALESCE)
3644 #undef FE
3645 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3646 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3647 DWORD i;
3649 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3650 TRACE(" Attribute:");
3651 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3652 if (query_flags[i].val == info) {
3653 TRACE(" %s", query_flags[i].name);
3654 break;
3657 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3658 TRACE(" Unknown (%08x)", info);
3661 TRACE(" Modifier:");
3662 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3663 if (modifier_flags[i].val & info_mod) {
3664 TRACE(" %s", modifier_flags[i].name);
3665 info_mod &= ~ modifier_flags[i].val;
3669 if (info_mod) {
3670 TRACE(" Unknown (%08x)", info_mod);
3672 TRACE("\n");
3675 request = (http_request_t*) get_handle_object( hHttpRequest );
3676 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3678 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3679 goto lend;
3682 if (lpBuffer == NULL)
3683 *lpdwBufferLength = 0;
3684 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3685 lpBuffer, lpdwBufferLength, lpdwIndex);
3687 lend:
3688 if( request )
3689 WININET_Release( &request->hdr );
3691 TRACE("%u <--\n", res);
3692 if(res != ERROR_SUCCESS)
3693 SetLastError(res);
3694 return res == ERROR_SUCCESS;
3697 /***********************************************************************
3698 * HttpQueryInfoA (WININET.@)
3700 * Queries for information about an HTTP request
3702 * RETURNS
3703 * TRUE on success
3704 * FALSE on failure
3707 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3708 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3710 BOOL result;
3711 DWORD len;
3712 WCHAR* bufferW;
3714 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3715 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3717 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3718 lpdwBufferLength, lpdwIndex );
3721 if (lpBuffer)
3723 DWORD alloclen;
3724 len = (*lpdwBufferLength)*sizeof(WCHAR);
3725 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3727 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3728 if (alloclen < len)
3729 alloclen = len;
3731 else
3732 alloclen = len;
3733 bufferW = heap_alloc(alloclen);
3734 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3735 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3736 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3737 } else
3739 bufferW = NULL;
3740 len = 0;
3743 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3744 &len, lpdwIndex );
3745 if( result )
3747 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3748 lpBuffer, *lpdwBufferLength, NULL, NULL );
3749 *lpdwBufferLength = len - 1;
3751 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3753 else
3754 /* since the strings being returned from HttpQueryInfoW should be
3755 * only ASCII characters, it is reasonable to assume that all of
3756 * the Unicode characters can be reduced to a single byte */
3757 *lpdwBufferLength = len / sizeof(WCHAR);
3759 heap_free( bufferW );
3760 return result;
3763 /***********************************************************************
3764 * HTTP_GetRedirectURL (internal)
3766 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3768 static WCHAR szHttp[] = {'h','t','t','p',0};
3769 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3770 http_session_t *session = request->session;
3771 URL_COMPONENTSW urlComponents;
3772 DWORD url_length = 0;
3773 LPWSTR orig_url;
3774 LPWSTR combined_url;
3776 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3777 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3778 urlComponents.dwSchemeLength = 0;
3779 urlComponents.lpszHostName = session->hostName;
3780 urlComponents.dwHostNameLength = 0;
3781 urlComponents.nPort = session->hostPort;
3782 urlComponents.lpszUserName = session->userName;
3783 urlComponents.dwUserNameLength = 0;
3784 urlComponents.lpszPassword = NULL;
3785 urlComponents.dwPasswordLength = 0;
3786 urlComponents.lpszUrlPath = request->path;
3787 urlComponents.dwUrlPathLength = 0;
3788 urlComponents.lpszExtraInfo = NULL;
3789 urlComponents.dwExtraInfoLength = 0;
3791 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3792 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3793 return NULL;
3795 orig_url = heap_alloc(url_length);
3797 /* convert from bytes to characters */
3798 url_length = url_length / sizeof(WCHAR) - 1;
3799 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3801 heap_free(orig_url);
3802 return NULL;
3805 url_length = 0;
3806 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3807 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3809 heap_free(orig_url);
3810 return NULL;
3812 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3814 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3816 heap_free(orig_url);
3817 heap_free(combined_url);
3818 return NULL;
3820 heap_free(orig_url);
3821 return combined_url;
3825 /***********************************************************************
3826 * HTTP_HandleRedirect (internal)
3828 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3830 http_session_t *session = request->session;
3831 appinfo_t *hIC = session->appInfo;
3832 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3833 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3834 int index;
3836 if(lpszUrl[0]=='/')
3838 /* if it's an absolute path, keep the same session info */
3839 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3841 else
3843 URL_COMPONENTSW urlComponents;
3844 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3845 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3846 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3847 BOOL custom_port = FALSE;
3849 static WCHAR httpW[] = {'h','t','t','p',0};
3850 static WCHAR httpsW[] = {'h','t','t','p','s',0};
3852 userName[0] = 0;
3853 hostName[0] = 0;
3854 protocol[0] = 0;
3856 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3857 urlComponents.lpszScheme = protocol;
3858 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3859 urlComponents.lpszHostName = hostName;
3860 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3861 urlComponents.lpszUserName = userName;
3862 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3863 urlComponents.lpszPassword = NULL;
3864 urlComponents.dwPasswordLength = 0;
3865 urlComponents.lpszUrlPath = path;
3866 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3867 urlComponents.lpszExtraInfo = NULL;
3868 urlComponents.dwExtraInfoLength = 0;
3869 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3870 return INTERNET_GetLastError();
3872 if(!strcmpiW(protocol, httpW)) {
3873 if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
3874 TRACE("redirect from secure page to non-secure page\n");
3875 /* FIXME: warn about from secure redirect to non-secure page */
3876 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3879 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3880 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3881 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT)
3882 custom_port = TRUE;
3883 }else if(!strcmpiW(protocol, httpsW)) {
3884 if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
3885 TRACE("redirect from non-secure page to secure page\n");
3886 /* FIXME: notify about redirect to secure page */
3887 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3890 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3891 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3892 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3893 custom_port = TRUE;
3896 heap_free(session->hostName);
3898 if(custom_port) {
3899 int len;
3900 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3901 len = lstrlenW(hostName);
3902 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3903 session->hostName = heap_alloc(len*sizeof(WCHAR));
3904 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3906 else
3907 session->hostName = heap_strdupW(hostName);
3909 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3911 heap_free(session->userName);
3912 session->userName = NULL;
3913 if (userName[0])
3914 session->userName = heap_strdupW(userName);
3916 reset_data_stream(request);
3918 if(!using_proxy && (strcmpiW(request->server->name, hostName) || request->server->port != urlComponents.nPort)) {
3919 server_t *new_server;
3921 new_server = get_server(hostName, urlComponents.nPort);
3922 server_release(request->server);
3923 request->server = new_server;
3926 heap_free(request->path);
3927 request->path=NULL;
3928 if (*path)
3930 DWORD needed = 0;
3931 HRESULT rc;
3933 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3934 if (rc != E_POINTER)
3935 needed = strlenW(path)+1;
3936 request->path = heap_alloc(needed*sizeof(WCHAR));
3937 rc = UrlEscapeW(path, request->path, &needed,
3938 URL_ESCAPE_SPACES_ONLY);
3939 if (rc != S_OK)
3941 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3942 strcpyW(request->path,path);
3946 /* Remove custom content-type/length headers on redirects. */
3947 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3948 if (0 <= index)
3949 HTTP_DeleteCustomHeader(request, index);
3950 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3951 if (0 <= index)
3952 HTTP_DeleteCustomHeader(request, index);
3954 return ERROR_SUCCESS;
3957 /***********************************************************************
3958 * HTTP_build_req (internal)
3960 * concatenate all the strings in the request together
3962 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3964 LPCWSTR *t;
3965 LPWSTR str;
3967 for( t = list; *t ; t++ )
3968 len += strlenW( *t );
3969 len++;
3971 str = heap_alloc(len*sizeof(WCHAR));
3972 *str = 0;
3974 for( t = list; *t ; t++ )
3975 strcatW( str, *t );
3977 return str;
3980 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3982 LPWSTR lpszPath;
3983 LPWSTR requestString;
3984 INT len;
3985 INT cnt;
3986 INT responseLen;
3987 char *ascii_req;
3988 DWORD res;
3989 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3990 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
3991 http_session_t *session = request->session;
3993 TRACE("\n");
3995 lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
3996 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
3997 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
3998 heap_free( lpszPath );
4000 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4001 NULL, 0, NULL, NULL );
4002 len--; /* the nul terminator isn't needed */
4003 ascii_req = heap_alloc(len);
4004 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
4005 heap_free( requestString );
4007 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
4009 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4010 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
4011 heap_free( ascii_req );
4012 if (res != ERROR_SUCCESS)
4013 return res;
4015 responseLen = HTTP_GetResponseHeaders( request, TRUE );
4016 if (!responseLen)
4017 return ERROR_HTTP_INVALID_HEADER;
4019 return ERROR_SUCCESS;
4022 static void HTTP_InsertCookies(http_request_t *request)
4024 DWORD cookie_size, size, cnt = 0;
4025 HTTPHEADERW *host;
4026 WCHAR *cookies;
4028 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
4030 host = HTTP_GetHeader(request, hostW);
4031 if(!host)
4032 return;
4034 if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
4035 return;
4037 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
4038 if(!(cookies = heap_alloc(size)))
4039 return;
4041 cnt += sprintfW(cookies, cookieW);
4042 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
4043 strcatW(cookies, szCrLf);
4045 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4047 heap_free(cookies);
4050 static WORD HTTP_ParseWkday(LPCWSTR day)
4052 static const WCHAR days[7][4] = {{ 's','u','n',0 },
4053 { 'm','o','n',0 },
4054 { 't','u','e',0 },
4055 { 'w','e','d',0 },
4056 { 't','h','u',0 },
4057 { 'f','r','i',0 },
4058 { 's','a','t',0 }};
4059 int i;
4060 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4061 if (!strcmpiW(day, days[i]))
4062 return i;
4064 /* Invalid */
4065 return 7;
4068 static WORD HTTP_ParseMonth(LPCWSTR month)
4070 static const WCHAR jan[] = { 'j','a','n',0 };
4071 static const WCHAR feb[] = { 'f','e','b',0 };
4072 static const WCHAR mar[] = { 'm','a','r',0 };
4073 static const WCHAR apr[] = { 'a','p','r',0 };
4074 static const WCHAR may[] = { 'm','a','y',0 };
4075 static const WCHAR jun[] = { 'j','u','n',0 };
4076 static const WCHAR jul[] = { 'j','u','l',0 };
4077 static const WCHAR aug[] = { 'a','u','g',0 };
4078 static const WCHAR sep[] = { 's','e','p',0 };
4079 static const WCHAR oct[] = { 'o','c','t',0 };
4080 static const WCHAR nov[] = { 'n','o','v',0 };
4081 static const WCHAR dec[] = { 'd','e','c',0 };
4083 if (!strcmpiW(month, jan)) return 1;
4084 if (!strcmpiW(month, feb)) return 2;
4085 if (!strcmpiW(month, mar)) return 3;
4086 if (!strcmpiW(month, apr)) return 4;
4087 if (!strcmpiW(month, may)) return 5;
4088 if (!strcmpiW(month, jun)) return 6;
4089 if (!strcmpiW(month, jul)) return 7;
4090 if (!strcmpiW(month, aug)) return 8;
4091 if (!strcmpiW(month, sep)) return 9;
4092 if (!strcmpiW(month, oct)) return 10;
4093 if (!strcmpiW(month, nov)) return 11;
4094 if (!strcmpiW(month, dec)) return 12;
4095 /* Invalid */
4096 return 0;
4099 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4100 * optionally preceded by whitespace.
4101 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4102 * st, and sets *str to the first character after the time format.
4104 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4106 LPCWSTR ptr = *str;
4107 WCHAR *nextPtr;
4108 unsigned long num;
4110 while (isspaceW(*ptr))
4111 ptr++;
4113 num = strtoulW(ptr, &nextPtr, 10);
4114 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4116 ERR("unexpected time format %s\n", debugstr_w(ptr));
4117 return FALSE;
4119 if (num > 23)
4121 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4122 return FALSE;
4124 ptr = nextPtr + 1;
4125 st->wHour = (WORD)num;
4126 num = strtoulW(ptr, &nextPtr, 10);
4127 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4129 ERR("unexpected time format %s\n", debugstr_w(ptr));
4130 return FALSE;
4132 if (num > 59)
4134 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4135 return FALSE;
4137 ptr = nextPtr + 1;
4138 st->wMinute = (WORD)num;
4139 num = strtoulW(ptr, &nextPtr, 10);
4140 if (!nextPtr || nextPtr <= ptr)
4142 ERR("unexpected time format %s\n", debugstr_w(ptr));
4143 return FALSE;
4145 if (num > 59)
4147 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4148 return FALSE;
4150 ptr = nextPtr + 1;
4151 *str = ptr;
4152 st->wSecond = (WORD)num;
4153 return TRUE;
4156 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4158 static const WCHAR gmt[]= { 'G','M','T',0 };
4159 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4160 LPCWSTR ptr;
4161 SYSTEMTIME st = { 0 };
4162 unsigned long num;
4164 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4165 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4166 *dayPtr = *ptr;
4167 *dayPtr = 0;
4168 st.wDayOfWeek = HTTP_ParseWkday(day);
4169 if (st.wDayOfWeek >= 7)
4171 ERR("unexpected weekday %s\n", debugstr_w(day));
4172 return FALSE;
4175 while (isspaceW(*ptr))
4176 ptr++;
4178 for (monthPtr = month; !isspace(*ptr) &&
4179 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4180 monthPtr++, ptr++)
4181 *monthPtr = *ptr;
4182 *monthPtr = 0;
4183 st.wMonth = HTTP_ParseMonth(month);
4184 if (!st.wMonth || st.wMonth > 12)
4186 ERR("unexpected month %s\n", debugstr_w(month));
4187 return FALSE;
4190 while (isspaceW(*ptr))
4191 ptr++;
4193 num = strtoulW(ptr, &nextPtr, 10);
4194 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4196 ERR("unexpected day %s\n", debugstr_w(ptr));
4197 return FALSE;
4199 ptr = nextPtr;
4200 st.wDay = (WORD)num;
4202 while (isspaceW(*ptr))
4203 ptr++;
4205 if (!HTTP_ParseTime(&st, &ptr))
4206 return FALSE;
4208 while (isspaceW(*ptr))
4209 ptr++;
4211 num = strtoulW(ptr, &nextPtr, 10);
4212 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4214 ERR("unexpected year %s\n", debugstr_w(ptr));
4215 return FALSE;
4217 ptr = nextPtr;
4218 st.wYear = (WORD)num;
4220 while (isspaceW(*ptr))
4221 ptr++;
4223 /* asctime() doesn't report a timezone, but some web servers do, so accept
4224 * with or without GMT.
4226 if (*ptr && strcmpW(ptr, gmt))
4228 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4229 return FALSE;
4231 return SystemTimeToFileTime(&st, ft);
4234 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4236 static const WCHAR gmt[]= { 'G','M','T',0 };
4237 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4238 LPCWSTR ptr;
4239 unsigned long num;
4240 SYSTEMTIME st = { 0 };
4242 ptr = strchrW(value, ',');
4243 if (!ptr)
4244 return FALSE;
4245 if (ptr - value != 3)
4247 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4248 return FALSE;
4250 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4251 day[3] = 0;
4252 st.wDayOfWeek = HTTP_ParseWkday(day);
4253 if (st.wDayOfWeek > 6)
4255 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4256 return FALSE;
4258 ptr++;
4260 while (isspaceW(*ptr))
4261 ptr++;
4263 num = strtoulW(ptr, &nextPtr, 10);
4264 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4266 WARN("unexpected day %s\n", debugstr_w(value));
4267 return FALSE;
4269 ptr = nextPtr;
4270 st.wDay = (WORD)num;
4272 while (isspaceW(*ptr))
4273 ptr++;
4275 for (monthPtr = month; !isspace(*ptr) &&
4276 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4277 monthPtr++, ptr++)
4278 *monthPtr = *ptr;
4279 *monthPtr = 0;
4280 st.wMonth = HTTP_ParseMonth(month);
4281 if (!st.wMonth || st.wMonth > 12)
4283 WARN("unexpected month %s\n", debugstr_w(month));
4284 return FALSE;
4287 while (isspaceW(*ptr))
4288 ptr++;
4290 num = strtoulW(ptr, &nextPtr, 10);
4291 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4293 ERR("unexpected year %s\n", debugstr_w(value));
4294 return FALSE;
4296 ptr = nextPtr;
4297 st.wYear = (WORD)num;
4299 if (!HTTP_ParseTime(&st, &ptr))
4300 return FALSE;
4302 while (isspaceW(*ptr))
4303 ptr++;
4305 if (strcmpW(ptr, gmt))
4307 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4308 return FALSE;
4310 return SystemTimeToFileTime(&st, ft);
4313 static WORD HTTP_ParseWeekday(LPCWSTR day)
4315 static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4316 { 'm','o','n','d','a','y',0 },
4317 { 't','u','e','s','d','a','y',0 },
4318 { 'w','e','d','n','e','s','d','a','y',0 },
4319 { 't','h','u','r','s','d','a','y',0 },
4320 { 'f','r','i','d','a','y',0 },
4321 { 's','a','t','u','r','d','a','y',0 }};
4322 int i;
4323 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4324 if (!strcmpiW(day, days[i]))
4325 return i;
4327 /* Invalid */
4328 return 7;
4331 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4333 static const WCHAR gmt[]= { 'G','M','T',0 };
4334 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4335 LPCWSTR ptr;
4336 unsigned long num;
4337 SYSTEMTIME st = { 0 };
4339 ptr = strchrW(value, ',');
4340 if (!ptr)
4341 return FALSE;
4342 if (ptr - value == 3)
4344 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4345 day[3] = 0;
4346 st.wDayOfWeek = HTTP_ParseWkday(day);
4347 if (st.wDayOfWeek > 6)
4349 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4350 return FALSE;
4353 else if (ptr - value < sizeof(day) / sizeof(day[0]))
4355 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4356 day[ptr - value + 1] = 0;
4357 st.wDayOfWeek = HTTP_ParseWeekday(day);
4358 if (st.wDayOfWeek > 6)
4360 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4361 return FALSE;
4364 else
4366 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4367 return FALSE;
4369 ptr++;
4371 while (isspaceW(*ptr))
4372 ptr++;
4374 num = strtoulW(ptr, &nextPtr, 10);
4375 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4377 ERR("unexpected day %s\n", debugstr_w(value));
4378 return FALSE;
4380 ptr = nextPtr;
4381 st.wDay = (WORD)num;
4383 if (*ptr != '-')
4385 ERR("unexpected month format %s\n", debugstr_w(ptr));
4386 return FALSE;
4388 ptr++;
4390 for (monthPtr = month; *ptr != '-' &&
4391 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4392 monthPtr++, ptr++)
4393 *monthPtr = *ptr;
4394 *monthPtr = 0;
4395 st.wMonth = HTTP_ParseMonth(month);
4396 if (!st.wMonth || st.wMonth > 12)
4398 ERR("unexpected month %s\n", debugstr_w(month));
4399 return FALSE;
4402 if (*ptr != '-')
4404 ERR("unexpected year format %s\n", debugstr_w(ptr));
4405 return FALSE;
4407 ptr++;
4409 num = strtoulW(ptr, &nextPtr, 10);
4410 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4412 ERR("unexpected year %s\n", debugstr_w(value));
4413 return FALSE;
4415 ptr = nextPtr;
4416 st.wYear = (WORD)num;
4418 if (!HTTP_ParseTime(&st, &ptr))
4419 return FALSE;
4421 while (isspaceW(*ptr))
4422 ptr++;
4424 if (strcmpW(ptr, gmt))
4426 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4427 return FALSE;
4429 return SystemTimeToFileTime(&st, ft);
4432 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4434 static const WCHAR zero[] = { '0',0 };
4435 BOOL ret;
4437 if (!strcmpW(value, zero))
4439 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4440 ret = TRUE;
4442 else if (strchrW(value, ','))
4444 ret = HTTP_ParseRfc1123Date(value, ft);
4445 if (!ret)
4447 ret = HTTP_ParseRfc850Date(value, ft);
4448 if (!ret)
4449 ERR("unexpected date format %s\n", debugstr_w(value));
4452 else
4454 ret = HTTP_ParseDateAsAsctime(value, ft);
4455 if (!ret)
4456 ERR("unexpected date format %s\n", debugstr_w(value));
4458 return ret;
4461 static void HTTP_ProcessExpires(http_request_t *request)
4463 BOOL expirationFound = FALSE;
4464 int headerIndex;
4466 /* Look for a Cache-Control header with a max-age directive, as it takes
4467 * precedence over the Expires header.
4469 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4470 if (headerIndex != -1)
4472 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4473 LPWSTR ptr;
4475 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4477 LPWSTR comma = strchrW(ptr, ','), end, equal;
4479 if (comma)
4480 end = comma;
4481 else
4482 end = ptr + strlenW(ptr);
4483 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4485 if (*equal == '=')
4487 static const WCHAR max_age[] = {
4488 'm','a','x','-','a','g','e',0 };
4490 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4492 LPWSTR nextPtr;
4493 unsigned long age;
4495 age = strtoulW(equal + 1, &nextPtr, 10);
4496 if (nextPtr > equal + 1)
4498 LARGE_INTEGER ft;
4500 NtQuerySystemTime( &ft );
4501 /* Age is in seconds, FILETIME resolution is in
4502 * 100 nanosecond intervals.
4504 ft.QuadPart += age * (ULONGLONG)1000000;
4505 request->expires.dwLowDateTime = ft.u.LowPart;
4506 request->expires.dwHighDateTime = ft.u.HighPart;
4507 expirationFound = TRUE;
4511 if (comma)
4513 ptr = comma + 1;
4514 while (isspaceW(*ptr))
4515 ptr++;
4517 else
4518 ptr = NULL;
4521 if (!expirationFound)
4523 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4524 if (headerIndex != -1)
4526 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4527 FILETIME ft;
4529 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4531 expirationFound = TRUE;
4532 request->expires = ft;
4536 if (!expirationFound)
4538 LARGE_INTEGER t;
4540 /* With no known age, default to 10 minutes until expiration. */
4541 NtQuerySystemTime( &t );
4542 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4543 request->expires.dwLowDateTime = t.u.LowPart;
4544 request->expires.dwHighDateTime = t.u.HighPart;
4548 static void HTTP_ProcessLastModified(http_request_t *request)
4550 int headerIndex;
4552 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4553 if (headerIndex != -1)
4555 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4556 FILETIME ft;
4558 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4559 request->last_modified = ft;
4563 static void http_process_keep_alive(http_request_t *req)
4565 int index;
4567 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4568 if(index != -1)
4569 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4570 else
4571 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4574 static void HTTP_CacheRequest(http_request_t *request)
4576 WCHAR url[INTERNET_MAX_URL_LENGTH];
4577 WCHAR cacheFileName[MAX_PATH+1];
4578 BOOL b;
4580 b = HTTP_GetRequestURL(request, url);
4581 if(!b) {
4582 WARN("Could not get URL\n");
4583 return;
4586 b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4587 if(b) {
4588 heap_free(request->cacheFile);
4589 CloseHandle(request->hCacheFile);
4591 request->cacheFile = heap_strdupW(cacheFileName);
4592 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4593 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4594 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4595 WARN("Could not create file: %u\n", GetLastError());
4596 request->hCacheFile = NULL;
4598 }else {
4599 WARN("Could not create cache entry: %08x\n", GetLastError());
4603 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4605 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4606 netconn_t *netconn = NULL;
4607 DWORD res;
4609 assert(!request->netconn);
4610 reset_data_stream(request);
4612 res = HTTP_ResolveName(request);
4613 if(res != ERROR_SUCCESS)
4614 return res;
4616 EnterCriticalSection(&connection_pool_cs);
4618 while(!list_empty(&request->server->conn_pool)) {
4619 netconn = LIST_ENTRY(list_head(&request->server->conn_pool), netconn_t, pool_entry);
4620 list_remove(&netconn->pool_entry);
4622 if(NETCON_is_alive(netconn))
4623 break;
4625 TRACE("connection %p closed during idle\n", netconn);
4626 free_netconn(netconn);
4627 netconn = NULL;
4630 LeaveCriticalSection(&connection_pool_cs);
4632 if(netconn) {
4633 TRACE("<-- reusing %p netconn\n", netconn);
4634 request->netconn = netconn;
4635 *reusing = TRUE;
4636 return ERROR_SUCCESS;
4639 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4640 INTERNET_STATUS_CONNECTING_TO_SERVER,
4641 request->server->addr_str,
4642 strlen(request->server->addr_str)+1);
4644 res = create_netconn(is_https, request->server, request->security_flags,
4645 (request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0,
4646 request->connect_timeout, &netconn);
4647 if(res != ERROR_SUCCESS) {
4648 ERR("create_netconn failed: %u\n", res);
4649 return res;
4652 request->netconn = netconn;
4654 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4655 INTERNET_STATUS_CONNECTED_TO_SERVER,
4656 request->server->addr_str, strlen(request->server->addr_str)+1);
4658 if(is_https) {
4659 /* Note: we differ from Microsoft's WinINet here. they seem to have
4660 * a bug that causes no status callbacks to be sent when starting
4661 * a tunnel to a proxy server using the CONNECT verb. i believe our
4662 * behaviour to be more correct and to not cause any incompatibilities
4663 * because using a secure connection through a proxy server is a rare
4664 * case that would be hard for anyone to depend on */
4665 if(request->session->appInfo->proxy)
4666 res = HTTP_SecureProxyConnect(request);
4667 if(res == ERROR_SUCCESS)
4668 res = NETCON_secure_connect(request->netconn);
4671 if(res != ERROR_SUCCESS) {
4672 http_release_netconn(request, FALSE);
4673 return res;
4676 *reusing = FALSE;
4677 TRACE("Created connection to %s: %p\n", debugstr_w(request->server->name), netconn);
4678 return ERROR_SUCCESS;
4681 /***********************************************************************
4682 * HTTP_HttpSendRequestW (internal)
4684 * Sends the specified request to the HTTP server
4686 * RETURNS
4687 * ERROR_SUCCESS on success
4688 * win32 error code on failure
4691 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4692 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4693 DWORD dwContentLength, BOOL bEndRequest)
4695 INT cnt;
4696 BOOL redirected = FALSE;
4697 LPWSTR requestString = NULL;
4698 INT responseLen;
4699 BOOL loop_next;
4700 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4701 static const WCHAR szContentLength[] =
4702 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4703 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4704 DWORD res;
4706 TRACE("--> %p\n", request);
4708 assert(request->hdr.htype == WH_HHTTPREQ);
4710 /* if the verb is NULL default to GET */
4711 if (!request->verb)
4712 request->verb = heap_strdupW(szGET);
4714 if (dwContentLength || strcmpW(request->verb, szGET))
4716 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4717 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4718 request->bytesToWrite = dwContentLength;
4720 if (request->session->appInfo->agent)
4722 WCHAR *agent_header;
4723 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4724 int len;
4726 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4727 agent_header = heap_alloc(len * sizeof(WCHAR));
4728 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4730 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4731 heap_free(agent_header);
4733 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4735 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4736 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4738 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4740 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4741 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4742 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4745 /* add the headers the caller supplied */
4746 if( lpszHeaders && dwHeaderLength )
4747 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4751 DWORD len;
4752 BOOL reusing_connection;
4753 char *ascii_req;
4755 loop_next = FALSE;
4757 /* like native, just in case the caller forgot to call InternetReadFile
4758 * for all the data */
4759 drain_content(request);
4760 if(redirected) {
4761 request->contentLength = ~0u;
4762 request->bytesToWrite = 0;
4765 if (TRACE_ON(wininet))
4767 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4768 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4771 HTTP_FixURL(request);
4772 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4774 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4776 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4777 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4779 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4780 HTTP_InsertCookies(request);
4782 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4784 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4785 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4786 heap_free(url);
4788 else
4789 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4792 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4794 if ((res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4795 break;
4797 /* send the request as ASCII, tack on the optional data */
4798 if (!lpOptional || redirected)
4799 dwOptionalLength = 0;
4800 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4801 NULL, 0, NULL, NULL );
4802 ascii_req = heap_alloc(len + dwOptionalLength);
4803 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4804 ascii_req, len, NULL, NULL );
4805 if( lpOptional )
4806 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4807 len = (len + dwOptionalLength - 1);
4808 ascii_req[len] = 0;
4809 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4811 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4812 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4814 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4815 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4816 heap_free( ascii_req );
4817 if(res != ERROR_SUCCESS) {
4818 TRACE("send failed: %u\n", res);
4819 if(!reusing_connection)
4820 break;
4821 http_release_netconn(request, FALSE);
4822 loop_next = TRUE;
4823 continue;
4826 request->bytesWritten = dwOptionalLength;
4828 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4829 INTERNET_STATUS_REQUEST_SENT,
4830 &len, sizeof(DWORD));
4832 if (bEndRequest)
4834 DWORD dwBufferSize;
4836 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4837 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4839 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4840 /* FIXME: We should know that connection is closed before sending
4841 * headers. Otherwise wrong callbacks are executed */
4842 if(!responseLen && reusing_connection) {
4843 TRACE("Connection closed by server, reconnecting\n");
4844 http_release_netconn(request, FALSE);
4845 loop_next = TRUE;
4846 continue;
4849 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4850 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4851 sizeof(DWORD));
4853 http_process_keep_alive(request);
4854 HTTP_ProcessCookies(request);
4855 HTTP_ProcessExpires(request);
4856 HTTP_ProcessLastModified(request);
4858 res = set_content_length(request);
4859 if(res != ERROR_SUCCESS)
4860 goto lend;
4861 if(!request->contentLength)
4862 http_release_netconn(request, TRUE);
4864 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4866 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4867 dwBufferSize=sizeof(szNewLocation);
4868 switch(request->status_code) {
4869 case HTTP_STATUS_REDIRECT:
4870 case HTTP_STATUS_MOVED:
4871 case HTTP_STATUS_REDIRECT_KEEP_VERB:
4872 case HTTP_STATUS_REDIRECT_METHOD:
4873 if(HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) != ERROR_SUCCESS)
4874 break;
4876 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4877 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
4879 heap_free(request->verb);
4880 request->verb = heap_strdupW(szGET);
4882 drain_content(request);
4883 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4885 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4886 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4887 res = HTTP_HandleRedirect(request, new_url);
4888 if (res == ERROR_SUCCESS)
4890 heap_free(requestString);
4891 loop_next = TRUE;
4893 heap_free( new_url );
4895 redirected = TRUE;
4898 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4900 WCHAR szAuthValue[2048];
4901 dwBufferSize=2048;
4902 if (request->status_code == HTTP_STATUS_DENIED)
4904 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4905 DWORD dwIndex = 0;
4906 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4908 if (HTTP_DoAuthorization(request, szAuthValue,
4909 &request->authInfo,
4910 request->session->userName,
4911 request->session->password,
4912 Host->lpszValue))
4914 heap_free(requestString);
4915 loop_next = TRUE;
4916 break;
4920 if(!loop_next) {
4921 TRACE("Cleaning wrong authorization data\n");
4922 destroy_authinfo(request->authInfo);
4923 request->authInfo = NULL;
4926 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
4928 DWORD dwIndex = 0;
4929 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4931 if (HTTP_DoAuthorization(request, szAuthValue,
4932 &request->proxyAuthInfo,
4933 request->session->appInfo->proxyUsername,
4934 request->session->appInfo->proxyPassword,
4935 NULL))
4937 loop_next = TRUE;
4938 break;
4942 if(!loop_next) {
4943 TRACE("Cleaning wrong proxy authorization data\n");
4944 destroy_authinfo(request->proxyAuthInfo);
4945 request->proxyAuthInfo = NULL;
4950 else
4951 res = ERROR_SUCCESS;
4953 while (loop_next);
4955 if(res == ERROR_SUCCESS)
4956 HTTP_CacheRequest(request);
4958 lend:
4959 heap_free(requestString);
4961 /* TODO: send notification for P3P header */
4963 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4965 if (res == ERROR_SUCCESS) {
4966 if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
4967 HTTP_ReceiveRequestData(request, TRUE);
4968 else
4969 send_request_complete(request,
4970 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
4971 }else {
4972 send_request_complete(request, 0, res);
4976 TRACE("<--\n");
4977 return res;
4980 /***********************************************************************
4982 * Helper functions for the HttpSendRequest(Ex) functions
4985 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
4987 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4988 http_request_t *request = (http_request_t*) workRequest->hdr;
4990 TRACE("%p\n", request);
4992 HTTP_HttpSendRequestW(request, req->lpszHeader,
4993 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
4994 req->dwContentLength, req->bEndRequest);
4996 heap_free(req->lpszHeader);
5000 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
5002 DWORD dwBufferSize;
5003 INT responseLen;
5004 DWORD res = ERROR_SUCCESS;
5006 if(!request->netconn) {
5007 WARN("Not connected\n");
5008 send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
5009 return ERROR_INTERNET_OPERATION_CANCELLED;
5012 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5013 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5015 responseLen = HTTP_GetResponseHeaders(request, TRUE);
5016 if (!responseLen)
5017 res = ERROR_HTTP_HEADER_NOT_FOUND;
5019 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5020 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5022 /* process cookies here. Is this right? */
5023 http_process_keep_alive(request);
5024 HTTP_ProcessCookies(request);
5025 HTTP_ProcessExpires(request);
5026 HTTP_ProcessLastModified(request);
5028 if ((res = set_content_length(request)) == ERROR_SUCCESS) {
5029 if(!request->contentLength)
5030 http_release_netconn(request, TRUE);
5033 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5035 switch(request->status_code) {
5036 case HTTP_STATUS_REDIRECT:
5037 case HTTP_STATUS_MOVED:
5038 case HTTP_STATUS_REDIRECT_METHOD:
5039 case HTTP_STATUS_REDIRECT_KEEP_VERB: {
5040 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5041 dwBufferSize=sizeof(szNewLocation);
5042 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) != ERROR_SUCCESS)
5043 break;
5045 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5046 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5048 heap_free(request->verb);
5049 request->verb = heap_strdupW(szGET);
5051 drain_content(request);
5052 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5054 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5055 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5056 res = HTTP_HandleRedirect(request, new_url);
5057 if (res == ERROR_SUCCESS)
5058 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5059 heap_free( new_url );
5065 if (res == ERROR_SUCCESS && request->contentLength)
5066 HTTP_ReceiveRequestData(request, TRUE);
5067 else
5068 send_request_complete(request, res == ERROR_SUCCESS, res);
5070 return res;
5073 /***********************************************************************
5074 * HttpEndRequestA (WININET.@)
5076 * Ends an HTTP request that was started by HttpSendRequestEx
5078 * RETURNS
5079 * TRUE if successful
5080 * FALSE on failure
5083 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5084 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5086 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5088 if (lpBuffersOut)
5090 SetLastError(ERROR_INVALID_PARAMETER);
5091 return FALSE;
5094 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5097 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
5099 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
5100 http_request_t *request = (http_request_t*)work->hdr;
5102 TRACE("%p\n", request);
5104 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
5107 /***********************************************************************
5108 * HttpEndRequestW (WININET.@)
5110 * Ends an HTTP request that was started by HttpSendRequestEx
5112 * RETURNS
5113 * TRUE if successful
5114 * FALSE on failure
5117 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5118 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5120 http_request_t *request;
5121 DWORD res;
5123 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5125 if (lpBuffersOut)
5127 SetLastError(ERROR_INVALID_PARAMETER);
5128 return FALSE;
5131 request = (http_request_t*) get_handle_object( hRequest );
5133 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5135 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5136 if (request)
5137 WININET_Release( &request->hdr );
5138 return FALSE;
5140 request->hdr.dwFlags |= dwFlags;
5142 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5144 WORKREQUEST work;
5145 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5147 work.asyncproc = AsyncHttpEndRequestProc;
5148 work.hdr = WININET_AddRef( &request->hdr );
5150 work_endrequest = &work.u.HttpEndRequestW;
5151 work_endrequest->dwFlags = dwFlags;
5152 work_endrequest->dwContext = dwContext;
5154 INTERNET_AsyncCall(&work);
5155 res = ERROR_IO_PENDING;
5157 else
5158 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5160 WININET_Release( &request->hdr );
5161 TRACE("%u <--\n", res);
5162 if(res != ERROR_SUCCESS)
5163 SetLastError(res);
5164 return res == ERROR_SUCCESS;
5167 /***********************************************************************
5168 * HttpSendRequestExA (WININET.@)
5170 * Sends the specified request to the HTTP server and allows chunked
5171 * transfers.
5173 * RETURNS
5174 * Success: TRUE
5175 * Failure: FALSE, call GetLastError() for more information.
5177 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5178 LPINTERNET_BUFFERSA lpBuffersIn,
5179 LPINTERNET_BUFFERSA lpBuffersOut,
5180 DWORD dwFlags, DWORD_PTR dwContext)
5182 INTERNET_BUFFERSW BuffersInW;
5183 BOOL rc = FALSE;
5184 DWORD headerlen;
5185 LPWSTR header = NULL;
5187 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5188 lpBuffersOut, dwFlags, dwContext);
5190 if (lpBuffersIn)
5192 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5193 if (lpBuffersIn->lpcszHeader)
5195 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5196 lpBuffersIn->dwHeadersLength,0,0);
5197 header = heap_alloc(headerlen*sizeof(WCHAR));
5198 if (!(BuffersInW.lpcszHeader = header))
5200 SetLastError(ERROR_OUTOFMEMORY);
5201 return FALSE;
5203 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5204 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5205 header, headerlen);
5207 else
5208 BuffersInW.lpcszHeader = NULL;
5209 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5210 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5211 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5212 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5213 BuffersInW.Next = NULL;
5216 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5218 heap_free(header);
5219 return rc;
5222 /***********************************************************************
5223 * HttpSendRequestExW (WININET.@)
5225 * Sends the specified request to the HTTP server and allows chunked
5226 * transfers
5228 * RETURNS
5229 * Success: TRUE
5230 * Failure: FALSE, call GetLastError() for more information.
5232 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5233 LPINTERNET_BUFFERSW lpBuffersIn,
5234 LPINTERNET_BUFFERSW lpBuffersOut,
5235 DWORD dwFlags, DWORD_PTR dwContext)
5237 http_request_t *request;
5238 http_session_t *session;
5239 appinfo_t *hIC;
5240 DWORD res;
5242 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5243 lpBuffersOut, dwFlags, dwContext);
5245 request = (http_request_t*) get_handle_object( hRequest );
5247 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5249 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5250 goto lend;
5253 session = request->session;
5254 assert(session->hdr.htype == WH_HHTTPSESSION);
5255 hIC = session->appInfo;
5256 assert(hIC->hdr.htype == WH_HINIT);
5258 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5260 WORKREQUEST workRequest;
5261 struct WORKREQ_HTTPSENDREQUESTW *req;
5263 workRequest.asyncproc = AsyncHttpSendRequestProc;
5264 workRequest.hdr = WININET_AddRef( &request->hdr );
5265 req = &workRequest.u.HttpSendRequestW;
5266 if (lpBuffersIn)
5268 DWORD size = 0;
5270 if (lpBuffersIn->lpcszHeader)
5272 if (lpBuffersIn->dwHeadersLength == ~0u)
5273 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5274 else
5275 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5277 req->lpszHeader = heap_alloc(size);
5278 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5280 else req->lpszHeader = NULL;
5282 req->dwHeaderLength = size / sizeof(WCHAR);
5283 req->lpOptional = lpBuffersIn->lpvBuffer;
5284 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5285 req->dwContentLength = lpBuffersIn->dwBufferTotal;
5287 else
5289 req->lpszHeader = NULL;
5290 req->dwHeaderLength = 0;
5291 req->lpOptional = NULL;
5292 req->dwOptionalLength = 0;
5293 req->dwContentLength = 0;
5296 req->bEndRequest = FALSE;
5298 INTERNET_AsyncCall(&workRequest);
5300 * This is from windows.
5302 res = ERROR_IO_PENDING;
5304 else
5306 if (lpBuffersIn)
5307 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5308 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5309 lpBuffersIn->dwBufferTotal, FALSE);
5310 else
5311 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5314 lend:
5315 if ( request )
5316 WININET_Release( &request->hdr );
5318 TRACE("<---\n");
5319 SetLastError(res);
5320 return res == ERROR_SUCCESS;
5323 /***********************************************************************
5324 * HttpSendRequestW (WININET.@)
5326 * Sends the specified request to the HTTP server
5328 * RETURNS
5329 * TRUE on success
5330 * FALSE on failure
5333 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5334 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5336 http_request_t *request;
5337 http_session_t *session = NULL;
5338 appinfo_t *hIC = NULL;
5339 DWORD res = ERROR_SUCCESS;
5341 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5342 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5344 request = (http_request_t*) get_handle_object( hHttpRequest );
5345 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5347 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5348 goto lend;
5351 session = request->session;
5352 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5354 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5355 goto lend;
5358 hIC = session->appInfo;
5359 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5361 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5362 goto lend;
5365 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5367 WORKREQUEST workRequest;
5368 struct WORKREQ_HTTPSENDREQUESTW *req;
5370 workRequest.asyncproc = AsyncHttpSendRequestProc;
5371 workRequest.hdr = WININET_AddRef( &request->hdr );
5372 req = &workRequest.u.HttpSendRequestW;
5373 if (lpszHeaders)
5375 DWORD size;
5377 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5378 else size = dwHeaderLength * sizeof(WCHAR);
5380 req->lpszHeader = heap_alloc(size);
5381 memcpy(req->lpszHeader, lpszHeaders, size);
5383 else
5384 req->lpszHeader = 0;
5385 req->dwHeaderLength = dwHeaderLength;
5386 req->lpOptional = lpOptional;
5387 req->dwOptionalLength = dwOptionalLength;
5388 req->dwContentLength = dwOptionalLength;
5389 req->bEndRequest = TRUE;
5391 INTERNET_AsyncCall(&workRequest);
5393 * This is from windows.
5395 res = ERROR_IO_PENDING;
5397 else
5399 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5400 dwHeaderLength, lpOptional, dwOptionalLength,
5401 dwOptionalLength, TRUE);
5403 lend:
5404 if( request )
5405 WININET_Release( &request->hdr );
5407 SetLastError(res);
5408 return res == ERROR_SUCCESS;
5411 /***********************************************************************
5412 * HttpSendRequestA (WININET.@)
5414 * Sends the specified request to the HTTP server
5416 * RETURNS
5417 * TRUE on success
5418 * FALSE on failure
5421 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5422 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5424 BOOL result;
5425 LPWSTR szHeaders=NULL;
5426 DWORD nLen=dwHeaderLength;
5427 if(lpszHeaders!=NULL)
5429 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5430 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5431 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5433 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5434 heap_free(szHeaders);
5435 return result;
5438 /***********************************************************************
5439 * HTTPSESSION_Destroy (internal)
5441 * Deallocate session handle
5444 static void HTTPSESSION_Destroy(object_header_t *hdr)
5446 http_session_t *session = (http_session_t*) hdr;
5448 TRACE("%p\n", session);
5450 WININET_Release(&session->appInfo->hdr);
5452 heap_free(session->hostName);
5453 heap_free(session->password);
5454 heap_free(session->userName);
5457 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5459 http_session_t *ses = (http_session_t *)hdr;
5461 switch(option) {
5462 case INTERNET_OPTION_HANDLE_TYPE:
5463 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5465 if (*size < sizeof(ULONG))
5466 return ERROR_INSUFFICIENT_BUFFER;
5468 *size = sizeof(DWORD);
5469 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5470 return ERROR_SUCCESS;
5471 case INTERNET_OPTION_CONNECT_TIMEOUT:
5472 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5474 if (*size < sizeof(DWORD))
5475 return ERROR_INSUFFICIENT_BUFFER;
5477 *size = sizeof(DWORD);
5478 *(DWORD *)buffer = ses->connect_timeout;
5479 return ERROR_SUCCESS;
5481 case INTERNET_OPTION_SEND_TIMEOUT:
5482 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5484 if (*size < sizeof(DWORD))
5485 return ERROR_INSUFFICIENT_BUFFER;
5487 *size = sizeof(DWORD);
5488 *(DWORD *)buffer = ses->send_timeout;
5489 return ERROR_SUCCESS;
5491 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5492 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5494 if (*size < sizeof(DWORD))
5495 return ERROR_INSUFFICIENT_BUFFER;
5497 *size = sizeof(DWORD);
5498 *(DWORD *)buffer = ses->receive_timeout;
5499 return ERROR_SUCCESS;
5502 return INET_QueryOption(hdr, option, buffer, size, unicode);
5505 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5507 http_session_t *ses = (http_session_t*)hdr;
5509 switch(option) {
5510 case INTERNET_OPTION_USERNAME:
5512 heap_free(ses->userName);
5513 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5514 return ERROR_SUCCESS;
5516 case INTERNET_OPTION_PASSWORD:
5518 heap_free(ses->password);
5519 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5520 return ERROR_SUCCESS;
5522 case INTERNET_OPTION_CONNECT_TIMEOUT:
5524 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5525 ses->connect_timeout = *(DWORD *)buffer;
5526 return ERROR_SUCCESS;
5528 case INTERNET_OPTION_SEND_TIMEOUT:
5530 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5531 ses->send_timeout = *(DWORD *)buffer;
5532 return ERROR_SUCCESS;
5534 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5536 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5537 ses->receive_timeout = *(DWORD *)buffer;
5538 return ERROR_SUCCESS;
5540 default: break;
5543 return INET_SetOption(hdr, option, buffer, size);
5546 static const object_vtbl_t HTTPSESSIONVtbl = {
5547 HTTPSESSION_Destroy,
5548 NULL,
5549 HTTPSESSION_QueryOption,
5550 HTTPSESSION_SetOption,
5551 NULL,
5552 NULL,
5553 NULL,
5554 NULL,
5555 NULL
5559 /***********************************************************************
5560 * HTTP_Connect (internal)
5562 * Create http session handle
5564 * RETURNS
5565 * HINTERNET a session handle on success
5566 * NULL on failure
5569 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5570 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5571 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5572 DWORD dwInternalFlags, HINTERNET *ret)
5574 http_session_t *session = NULL;
5576 TRACE("-->\n");
5578 if (!lpszServerName || !lpszServerName[0])
5579 return ERROR_INVALID_PARAMETER;
5581 assert( hIC->hdr.htype == WH_HINIT );
5583 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5584 if (!session)
5585 return ERROR_OUTOFMEMORY;
5588 * According to my tests. The name is not resolved until a request is sent
5591 session->hdr.htype = WH_HHTTPSESSION;
5592 session->hdr.dwFlags = dwFlags;
5593 session->hdr.dwContext = dwContext;
5594 session->hdr.dwInternalFlags |= dwInternalFlags;
5596 WININET_AddRef( &hIC->hdr );
5597 session->appInfo = hIC;
5598 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5600 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5601 if(hIC->proxyBypass)
5602 FIXME("Proxy bypass is ignored.\n");
5604 session->hostName = heap_strdupW(lpszServerName);
5605 if (lpszUserName && lpszUserName[0])
5606 session->userName = heap_strdupW(lpszUserName);
5607 if (lpszPassword && lpszPassword[0])
5608 session->password = heap_strdupW(lpszPassword);
5609 session->hostPort = serverPort;
5610 session->connect_timeout = hIC->connect_timeout;
5611 session->send_timeout = INFINITE;
5612 session->receive_timeout = INFINITE;
5614 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5615 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5617 INTERNET_SendCallback(&hIC->hdr, dwContext,
5618 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5619 sizeof(HINTERNET));
5623 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5624 * windows
5627 TRACE("%p --> %p\n", hIC, session);
5629 *ret = session->hdr.hInternet;
5630 return ERROR_SUCCESS;
5633 /***********************************************************************
5634 * HTTP_clear_response_headers (internal)
5636 * clear out any old response headers
5638 static void HTTP_clear_response_headers( http_request_t *request )
5640 DWORD i;
5642 for( i=0; i<request->nCustHeaders; i++)
5644 if( !request->custHeaders[i].lpszField )
5645 continue;
5646 if( !request->custHeaders[i].lpszValue )
5647 continue;
5648 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5649 continue;
5650 HTTP_DeleteCustomHeader( request, i );
5651 i--;
5655 /***********************************************************************
5656 * HTTP_GetResponseHeaders (internal)
5658 * Read server response
5660 * RETURNS
5662 * TRUE on success
5663 * FALSE on error
5665 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5667 INT cbreaks = 0;
5668 WCHAR buffer[MAX_REPLY_LEN];
5669 DWORD buflen = MAX_REPLY_LEN;
5670 BOOL bSuccess = FALSE;
5671 INT rc = 0;
5672 char bufferA[MAX_REPLY_LEN];
5673 LPWSTR status_code = NULL, status_text = NULL;
5674 DWORD cchMaxRawHeaders = 1024;
5675 LPWSTR lpszRawHeaders = NULL;
5676 LPWSTR temp;
5677 DWORD cchRawHeaders = 0;
5678 BOOL codeHundred = FALSE;
5680 TRACE("-->\n");
5682 if(!request->netconn)
5683 goto lend;
5685 NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5686 do {
5687 static const WCHAR szHundred[] = {'1','0','0',0};
5689 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5691 buflen = MAX_REPLY_LEN;
5692 if (!read_line(request, bufferA, &buflen))
5693 goto lend;
5695 /* clear old response headers (eg. from a redirect response) */
5696 if (clear) {
5697 HTTP_clear_response_headers( request );
5698 clear = FALSE;
5701 rc += buflen;
5702 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5703 /* check is this a status code line? */
5704 if (!strncmpW(buffer, g_szHttp1_0, 4))
5706 /* split the version from the status code */
5707 status_code = strchrW( buffer, ' ' );
5708 if( !status_code )
5709 goto lend;
5710 *status_code++=0;
5712 /* split the status code from the status text */
5713 status_text = strchrW( status_code, ' ' );
5714 if( !status_text )
5715 goto lend;
5716 *status_text++=0;
5718 request->status_code = atoiW(status_code);
5720 TRACE("version [%s] status code [%s] status text [%s]\n",
5721 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5723 codeHundred = (!strcmpW(status_code, szHundred));
5725 else if (!codeHundred)
5727 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5729 heap_free(request->version);
5730 heap_free(request->statusText);
5732 request->status_code = HTTP_STATUS_OK;
5733 request->version = heap_strdupW(g_szHttp1_0);
5734 request->statusText = heap_strdupW(szOK);
5736 heap_free(request->rawHeaders);
5737 request->rawHeaders = heap_strdupW(szDefaultHeader);
5739 bSuccess = TRUE;
5740 goto lend;
5742 } while (codeHundred);
5744 /* Add status code */
5745 HTTP_ProcessHeader(request, szStatus, status_code,
5746 HTTP_ADDHDR_FLAG_REPLACE);
5748 heap_free(request->version);
5749 heap_free(request->statusText);
5751 request->version = heap_strdupW(buffer);
5752 request->statusText = heap_strdupW(status_text);
5754 /* Restore the spaces */
5755 *(status_code-1) = ' ';
5756 *(status_text-1) = ' ';
5758 /* regenerate raw headers */
5759 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5760 if (!lpszRawHeaders) goto lend;
5762 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5763 cchMaxRawHeaders *= 2;
5764 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5765 if (temp == NULL) goto lend;
5766 lpszRawHeaders = temp;
5767 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5768 cchRawHeaders += (buflen-1);
5769 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5770 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5771 lpszRawHeaders[cchRawHeaders] = '\0';
5773 /* Parse each response line */
5776 buflen = MAX_REPLY_LEN;
5777 if (read_line(request, bufferA, &buflen))
5779 LPWSTR * pFieldAndValue;
5781 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5783 if (!bufferA[0]) break;
5784 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5786 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5787 if (pFieldAndValue)
5789 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5790 cchMaxRawHeaders *= 2;
5791 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5792 if (temp == NULL) goto lend;
5793 lpszRawHeaders = temp;
5794 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5795 cchRawHeaders += (buflen-1);
5796 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5797 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5798 lpszRawHeaders[cchRawHeaders] = '\0';
5800 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5801 HTTP_ADDREQ_FLAG_ADD );
5803 HTTP_FreeTokens(pFieldAndValue);
5806 else
5808 cbreaks++;
5809 if (cbreaks >= 2)
5810 break;
5812 }while(1);
5814 /* make sure the response header is terminated with an empty line. Some apps really
5815 truly care about that empty line being there for some reason. Just add it to the
5816 header. */
5817 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5819 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5820 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5821 if (temp == NULL) goto lend;
5822 lpszRawHeaders = temp;
5825 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5827 heap_free(request->rawHeaders);
5828 request->rawHeaders = lpszRawHeaders;
5829 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5830 bSuccess = TRUE;
5832 lend:
5834 TRACE("<--\n");
5835 if (bSuccess)
5836 return rc;
5837 else
5839 heap_free(lpszRawHeaders);
5840 return 0;
5844 /***********************************************************************
5845 * HTTP_InterpretHttpHeader (internal)
5847 * Parse server response
5849 * RETURNS
5851 * Pointer to array of field, value, NULL on success.
5852 * NULL on error.
5854 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5856 LPWSTR * pTokenPair;
5857 LPWSTR pszColon;
5858 INT len;
5860 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5862 pszColon = strchrW(buffer, ':');
5863 /* must have two tokens */
5864 if (!pszColon)
5866 HTTP_FreeTokens(pTokenPair);
5867 if (buffer[0])
5868 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5869 return NULL;
5872 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5873 if (!pTokenPair[0])
5875 HTTP_FreeTokens(pTokenPair);
5876 return NULL;
5878 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5879 pTokenPair[0][pszColon - buffer] = '\0';
5881 /* skip colon */
5882 pszColon++;
5883 len = strlenW(pszColon);
5884 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5885 if (!pTokenPair[1])
5887 HTTP_FreeTokens(pTokenPair);
5888 return NULL;
5890 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5892 strip_spaces(pTokenPair[0]);
5893 strip_spaces(pTokenPair[1]);
5895 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5896 return pTokenPair;
5899 /***********************************************************************
5900 * HTTP_ProcessHeader (internal)
5902 * Stuff header into header tables according to <dwModifier>
5906 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5908 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5910 LPHTTPHEADERW lphttpHdr = NULL;
5911 INT index = -1;
5912 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5913 DWORD res = ERROR_HTTP_INVALID_HEADER;
5915 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5917 /* REPLACE wins out over ADD */
5918 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5919 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5921 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5922 index = -1;
5923 else
5924 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5926 if (index >= 0)
5928 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5929 return ERROR_HTTP_INVALID_HEADER;
5930 lphttpHdr = &request->custHeaders[index];
5932 else if (value)
5934 HTTPHEADERW hdr;
5936 hdr.lpszField = (LPWSTR)field;
5937 hdr.lpszValue = (LPWSTR)value;
5938 hdr.wFlags = hdr.wCount = 0;
5940 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5941 hdr.wFlags |= HDR_ISREQUEST;
5943 return HTTP_InsertCustomHeader(request, &hdr);
5945 /* no value to delete */
5946 else return ERROR_SUCCESS;
5948 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5949 lphttpHdr->wFlags |= HDR_ISREQUEST;
5950 else
5951 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5953 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5955 HTTP_DeleteCustomHeader( request, index );
5957 if (value)
5959 HTTPHEADERW hdr;
5961 hdr.lpszField = (LPWSTR)field;
5962 hdr.lpszValue = (LPWSTR)value;
5963 hdr.wFlags = hdr.wCount = 0;
5965 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5966 hdr.wFlags |= HDR_ISREQUEST;
5968 return HTTP_InsertCustomHeader(request, &hdr);
5971 return ERROR_SUCCESS;
5973 else if (dwModifier & COALESCEFLAGS)
5975 LPWSTR lpsztmp;
5976 WCHAR ch = 0;
5977 INT len = 0;
5978 INT origlen = strlenW(lphttpHdr->lpszValue);
5979 INT valuelen = strlenW(value);
5981 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5983 ch = ',';
5984 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5986 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5988 ch = ';';
5989 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5992 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5994 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5995 if (lpsztmp)
5997 lphttpHdr->lpszValue = lpsztmp;
5998 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5999 if (ch > 0)
6001 lphttpHdr->lpszValue[origlen] = ch;
6002 origlen++;
6003 lphttpHdr->lpszValue[origlen] = ' ';
6004 origlen++;
6007 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
6008 lphttpHdr->lpszValue[len] = '\0';
6009 res = ERROR_SUCCESS;
6011 else
6013 WARN("heap_realloc (%d bytes) failed\n",len+1);
6014 res = ERROR_OUTOFMEMORY;
6017 TRACE("<-- %d\n", res);
6018 return res;
6021 /***********************************************************************
6022 * HTTP_GetCustomHeaderIndex (internal)
6024 * Return index of custom header from header array
6027 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
6028 int requested_index, BOOL request_only)
6030 DWORD index;
6032 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6034 for (index = 0; index < request->nCustHeaders; index++)
6036 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
6037 continue;
6039 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6040 continue;
6042 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6043 continue;
6045 if (requested_index == 0)
6046 break;
6047 requested_index --;
6050 if (index >= request->nCustHeaders)
6051 index = -1;
6053 TRACE("Return: %d\n", index);
6054 return index;
6058 /***********************************************************************
6059 * HTTP_InsertCustomHeader (internal)
6061 * Insert header into array
6064 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6066 INT count;
6067 LPHTTPHEADERW lph = NULL;
6069 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6070 count = request->nCustHeaders + 1;
6071 if (count > 1)
6072 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6073 else
6074 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6076 if (!lph)
6077 return ERROR_OUTOFMEMORY;
6079 request->custHeaders = lph;
6080 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6081 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6082 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6083 request->custHeaders[count-1].wCount= lpHdr->wCount;
6084 request->nCustHeaders++;
6086 return ERROR_SUCCESS;
6090 /***********************************************************************
6091 * HTTP_DeleteCustomHeader (internal)
6093 * Delete header from array
6094 * If this function is called, the indexs may change.
6096 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6098 if( request->nCustHeaders <= 0 )
6099 return FALSE;
6100 if( index >= request->nCustHeaders )
6101 return FALSE;
6102 request->nCustHeaders--;
6104 heap_free(request->custHeaders[index].lpszField);
6105 heap_free(request->custHeaders[index].lpszValue);
6107 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6108 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6109 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6111 return TRUE;
6115 /***********************************************************************
6116 * HTTP_VerifyValidHeader (internal)
6118 * Verify the given header is not invalid for the given http request
6121 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6123 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6124 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6125 return ERROR_HTTP_INVALID_HEADER;
6127 return ERROR_SUCCESS;
6130 /***********************************************************************
6131 * IsHostInProxyBypassList (@)
6133 * Undocumented
6136 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6138 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6139 return FALSE;
6142 /***********************************************************************
6143 * InternetShowSecurityInfoByURLA (@)
6145 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
6147 FIXME("stub: %s %p\n", url, window);
6148 return FALSE;
6151 /***********************************************************************
6152 * InternetShowSecurityInfoByURLW (@)
6154 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
6156 FIXME("stub: %s %p\n", debugstr_w(url), window);
6157 return FALSE;
6160 /***********************************************************************
6161 * ShowX509EncodedCertificate (@)
6163 DWORD WINAPI ShowX509EncodedCertificate(HWND parent, LPBYTE cert, DWORD len)
6165 PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING,
6166 cert, len);
6167 DWORD ret;
6169 if (certContext)
6171 CRYPTUI_VIEWCERTIFICATE_STRUCTW view;
6173 memset(&view, 0, sizeof(view));
6174 view.hwndParent = parent;
6175 view.pCertContext = certContext;
6176 if (CryptUIDlgViewCertificateW(&view, NULL))
6177 ret = ERROR_SUCCESS;
6178 else
6179 ret = GetLastError();
6180 CertFreeCertificateContext(certContext);
6182 else
6183 ret = GetLastError();
6184 return ret;