wininet: Correctly fail INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT if libssl is...
[wine/multimedia.git] / dlls / wininet / http.c
blob0a3b8fe0290a4d2bbc22be116e97b96bc0f1f2f2
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 if(server->cert_chain)
248 CertFreeCertificateChain(server->cert_chain);
249 heap_free(server->name);
250 heap_free(server);
253 server_t *get_server(const WCHAR *name, INTERNET_PORT port, BOOL do_create)
255 server_t *iter, *server = NULL;
257 EnterCriticalSection(&connection_pool_cs);
259 LIST_FOR_EACH_ENTRY(iter, &connection_pool, server_t, entry) {
260 if(iter->port == port && !strcmpW(iter->name, name)) {
261 server = iter;
262 server_addref(server);
263 break;
267 if(!server && do_create) {
268 server = heap_alloc_zero(sizeof(*server));
269 if(server) {
270 server->ref = 2; /* list reference and return */
271 server->port = port;
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, TRUE);
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 98:
1991 TRACE("Queried undocumented option 98, forwarding to INTERNET_OPTION_SECURITY_FLAGS\n");
1992 /* fall through */
1993 case INTERNET_OPTION_SECURITY_FLAGS:
1995 DWORD flags;
1997 if (*size < sizeof(ULONG))
1998 return ERROR_INSUFFICIENT_BUFFER;
2000 *size = sizeof(DWORD);
2001 flags = req->netconn ? req->netconn->security_flags : req->security_flags | req->server->security_flags;
2002 *(DWORD *)buffer = flags;
2004 TRACE("INTERNET_OPTION_SECURITY_FLAGS %x\n", flags);
2005 return ERROR_SUCCESS;
2008 case INTERNET_OPTION_HANDLE_TYPE:
2009 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2011 if (*size < sizeof(ULONG))
2012 return ERROR_INSUFFICIENT_BUFFER;
2014 *size = sizeof(DWORD);
2015 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
2016 return ERROR_SUCCESS;
2018 case INTERNET_OPTION_URL: {
2019 WCHAR url[INTERNET_MAX_URL_LENGTH];
2020 HTTPHEADERW *host;
2021 DWORD len;
2022 WCHAR *pch;
2024 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
2026 TRACE("INTERNET_OPTION_URL\n");
2028 host = HTTP_GetHeader(req, hostW);
2029 strcpyW(url, httpW);
2030 strcatW(url, host->lpszValue);
2031 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
2032 *pch = 0;
2033 strcatW(url, req->path);
2035 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
2037 if(unicode) {
2038 len = (strlenW(url)+1) * sizeof(WCHAR);
2039 if(*size < len)
2040 return ERROR_INSUFFICIENT_BUFFER;
2042 *size = len;
2043 strcpyW(buffer, url);
2044 return ERROR_SUCCESS;
2045 }else {
2046 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
2047 if(len > *size)
2048 return ERROR_INSUFFICIENT_BUFFER;
2050 *size = len;
2051 return ERROR_SUCCESS;
2055 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2056 INTERNET_CACHE_ENTRY_INFOW *info;
2057 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2058 WCHAR url[INTERNET_MAX_URL_LENGTH];
2059 DWORD nbytes, error;
2060 BOOL ret;
2062 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2064 if (*size < sizeof(*ts))
2066 *size = sizeof(*ts);
2067 return ERROR_INSUFFICIENT_BUFFER;
2069 nbytes = 0;
2070 HTTP_GetRequestURL(req, url);
2071 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
2072 error = GetLastError();
2073 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2075 if (!(info = heap_alloc(nbytes)))
2076 return ERROR_OUTOFMEMORY;
2078 GetUrlCacheEntryInfoW(url, info, &nbytes);
2080 ts->ftExpires = info->ExpireTime;
2081 ts->ftLastModified = info->LastModifiedTime;
2083 heap_free(info);
2084 *size = sizeof(*ts);
2085 return ERROR_SUCCESS;
2087 return error;
2090 case INTERNET_OPTION_DATAFILE_NAME: {
2091 DWORD req_size;
2093 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2095 if(!req->cacheFile) {
2096 *size = 0;
2097 return ERROR_INTERNET_ITEM_NOT_FOUND;
2100 if(unicode) {
2101 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
2102 if(*size < req_size)
2103 return ERROR_INSUFFICIENT_BUFFER;
2105 *size = req_size;
2106 memcpy(buffer, req->cacheFile, *size);
2107 return ERROR_SUCCESS;
2108 }else {
2109 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
2110 if (req_size > *size)
2111 return ERROR_INSUFFICIENT_BUFFER;
2113 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
2114 -1, buffer, *size, NULL, NULL);
2115 return ERROR_SUCCESS;
2119 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2120 PCCERT_CONTEXT context;
2122 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2123 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2124 return ERROR_INSUFFICIENT_BUFFER;
2127 context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2128 if(context) {
2129 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2130 DWORD len;
2132 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
2133 info->ftExpiry = context->pCertInfo->NotAfter;
2134 info->ftStart = context->pCertInfo->NotBefore;
2135 len = CertNameToStrA(context->dwCertEncodingType,
2136 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2137 info->lpszSubjectInfo = LocalAlloc(0, len);
2138 if(info->lpszSubjectInfo)
2139 CertNameToStrA(context->dwCertEncodingType,
2140 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2141 info->lpszSubjectInfo, len);
2142 len = CertNameToStrA(context->dwCertEncodingType,
2143 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2144 info->lpszIssuerInfo = LocalAlloc(0, len);
2145 if(info->lpszIssuerInfo)
2146 CertNameToStrA(context->dwCertEncodingType,
2147 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2148 info->lpszIssuerInfo, len);
2149 info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2150 CertFreeCertificateContext(context);
2151 return ERROR_SUCCESS;
2153 return ERROR_NOT_SUPPORTED;
2155 case INTERNET_OPTION_CONNECT_TIMEOUT:
2156 if (*size < sizeof(DWORD))
2157 return ERROR_INSUFFICIENT_BUFFER;
2159 *size = sizeof(DWORD);
2160 *(DWORD *)buffer = req->connect_timeout;
2161 return ERROR_SUCCESS;
2162 case INTERNET_OPTION_REQUEST_FLAGS: {
2163 DWORD flags = 0;
2165 if (*size < sizeof(DWORD))
2166 return ERROR_INSUFFICIENT_BUFFER;
2168 /* FIXME: Add support for:
2169 * INTERNET_REQFLAG_FROM_CACHE
2170 * INTERNET_REQFLAG_CACHE_WRITE_DISABLED
2173 if(req->session->appInfo->proxy)
2174 flags |= INTERNET_REQFLAG_VIA_PROXY;
2175 if(!req->rawHeaders)
2176 flags |= INTERNET_REQFLAG_NO_HEADERS;
2178 TRACE("INTERNET_OPTION_REQUEST_FLAGS returning %x\n", flags);
2180 *size = sizeof(DWORD);
2181 *(DWORD*)buffer = flags;
2182 return ERROR_SUCCESS;
2186 return INET_QueryOption(hdr, option, buffer, size, unicode);
2189 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2191 http_request_t *req = (http_request_t*)hdr;
2193 switch(option) {
2194 case 99: /* Undocumented, seems to be INTERNET_OPTION_SECURITY_FLAGS with argument validation */
2195 TRACE("Undocumented option 99\n");
2197 if (!buffer || size != sizeof(DWORD))
2198 return ERROR_INVALID_PARAMETER;
2199 if(*(DWORD*)buffer & ~SECURITY_SET_MASK)
2200 return ERROR_INTERNET_OPTION_NOT_SETTABLE;
2202 /* fall through */
2203 case INTERNET_OPTION_SECURITY_FLAGS:
2205 DWORD flags;
2207 if (!buffer || size != sizeof(DWORD))
2208 return ERROR_INVALID_PARAMETER;
2209 flags = *(DWORD *)buffer;
2210 TRACE("INTERNET_OPTION_SECURITY_FLAGS %08x\n", flags);
2211 flags &= SECURITY_SET_MASK;
2212 req->security_flags |= flags;
2213 if(req->netconn)
2214 req->netconn->security_flags |= flags;
2215 return ERROR_SUCCESS;
2217 case INTERNET_OPTION_CONNECT_TIMEOUT:
2218 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2219 req->connect_timeout = *(DWORD *)buffer;
2220 return ERROR_SUCCESS;
2222 case INTERNET_OPTION_SEND_TIMEOUT:
2223 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2224 req->send_timeout = *(DWORD *)buffer;
2225 return ERROR_SUCCESS;
2227 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2228 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2229 req->receive_timeout = *(DWORD *)buffer;
2230 return ERROR_SUCCESS;
2232 case INTERNET_OPTION_USERNAME:
2233 heap_free(req->session->userName);
2234 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2235 return ERROR_SUCCESS;
2237 case INTERNET_OPTION_PASSWORD:
2238 heap_free(req->session->password);
2239 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2240 return ERROR_SUCCESS;
2241 case INTERNET_OPTION_HTTP_DECODING:
2242 if(size != sizeof(BOOL))
2243 return ERROR_INVALID_PARAMETER;
2244 req->decoding = *(BOOL*)buffer;
2245 return ERROR_SUCCESS;
2248 return INET_SetOption(hdr, option, buffer, size);
2251 /* read some more data into the read buffer (the read section must be held) */
2252 static DWORD read_more_data( http_request_t *req, int maxlen )
2254 DWORD res;
2255 int len;
2257 if (req->read_pos)
2259 /* move existing data to the start of the buffer */
2260 if(req->read_size)
2261 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2262 req->read_pos = 0;
2265 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2267 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2268 maxlen - req->read_size, 0, &len );
2269 if(res == ERROR_SUCCESS)
2270 req->read_size += len;
2272 return res;
2275 /* remove some amount of data from the read buffer (the read section must be held) */
2276 static void remove_data( http_request_t *req, int count )
2278 if (!(req->read_size -= count)) req->read_pos = 0;
2279 else req->read_pos += count;
2282 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2284 int count, bytes_read, pos = 0;
2285 DWORD res;
2287 EnterCriticalSection( &req->read_section );
2288 for (;;)
2290 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2292 if (eol)
2294 count = eol - (req->read_buf + req->read_pos);
2295 bytes_read = count + 1;
2297 else count = bytes_read = req->read_size;
2299 count = min( count, *len - pos );
2300 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2301 pos += count;
2302 remove_data( req, bytes_read );
2303 if (eol) break;
2305 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2307 *len = 0;
2308 TRACE( "returning empty string %u\n", res);
2309 LeaveCriticalSection( &req->read_section );
2310 INTERNET_SetLastError(res);
2311 return FALSE;
2314 LeaveCriticalSection( &req->read_section );
2316 if (pos < *len)
2318 if (pos && buffer[pos - 1] == '\r') pos--;
2319 *len = pos + 1;
2321 buffer[*len - 1] = 0;
2322 TRACE( "returning %s\n", debugstr_a(buffer));
2323 return TRUE;
2326 /* check if we have reached the end of the data to read (the read section must be held) */
2327 static BOOL end_of_read_data( http_request_t *req )
2329 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2332 /* fetch some more data into the read buffer (the read section must be held) */
2333 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2335 DWORD res, read=0;
2337 if(req->read_size == sizeof(req->read_buf))
2338 return ERROR_SUCCESS;
2340 if(req->read_pos) {
2341 if(req->read_size)
2342 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2343 req->read_pos = 0;
2346 res = req->data_stream->vtbl->read(req->data_stream, req, req->read_buf+req->read_size,
2347 sizeof(req->read_buf)-req->read_size, &read, read_mode);
2348 req->read_size += read;
2350 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2351 if(read_bytes)
2352 *read_bytes = read;
2353 return res;
2356 /* return the size of data available to be read immediately (the read section must be held) */
2357 static DWORD get_avail_data( http_request_t *req )
2359 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2362 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2364 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2365 DWORD avail = 0;
2367 if(req->netconn)
2368 NETCON_query_data_available(req->netconn, &avail);
2369 return netconn_stream->content_length == ~0u
2370 ? avail
2371 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2374 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2376 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2377 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2380 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2381 DWORD *read, read_mode_t read_mode)
2383 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2384 int len = 0;
2386 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2388 if(read_mode == READMODE_NOBLOCK)
2389 size = min(size, netconn_get_avail_data(stream, req));
2391 if(size && req->netconn) {
2392 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2393 len = 0;
2394 if(!len)
2395 netconn_stream->content_length = netconn_stream->content_read;
2398 netconn_stream->content_read += *read = len;
2399 TRACE("read %u bytes\n", len);
2400 return ERROR_SUCCESS;
2403 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2405 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2406 BYTE buf[1024];
2407 DWORD avail;
2408 int len;
2410 if(netconn_end_of_data(stream, req))
2411 return TRUE;
2413 do {
2414 avail = netconn_get_avail_data(stream, req);
2415 if(!avail)
2416 return FALSE;
2418 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2419 return FALSE;
2421 netconn_stream->content_read += len;
2422 }while(netconn_stream->content_read < netconn_stream->content_length);
2424 return TRUE;
2427 static void netconn_destroy(data_stream_t *stream)
2431 static const data_stream_vtbl_t netconn_stream_vtbl = {
2432 netconn_get_avail_data,
2433 netconn_end_of_data,
2434 netconn_read,
2435 netconn_drain_content,
2436 netconn_destroy
2439 /* read some more data into the read buffer (the read section must be held) */
2440 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2442 DWORD res;
2443 int len;
2445 if (stream->buf_pos)
2447 /* move existing data to the start of the buffer */
2448 if(stream->buf_size)
2449 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2450 stream->buf_pos = 0;
2453 if (maxlen == -1) maxlen = sizeof(stream->buf);
2455 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2456 maxlen - stream->buf_size, 0, &len );
2457 if(res == ERROR_SUCCESS)
2458 stream->buf_size += len;
2460 return res;
2463 /* remove some amount of data from the read buffer (the read section must be held) */
2464 static void remove_chunked_data(chunked_stream_t *stream, int count)
2466 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2467 else stream->buf_pos += count;
2470 /* discard data contents until we reach end of line (the read section must be held) */
2471 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2473 DWORD res;
2477 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2478 if (eol)
2480 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2481 break;
2483 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2484 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2485 } while (stream->buf_size);
2486 return ERROR_SUCCESS;
2489 /* read the size of the next chunk (the read section must be held) */
2490 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2492 /* TODOO */
2493 DWORD chunk_size = 0, res;
2495 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2496 return res;
2498 for (;;)
2500 while (stream->buf_size)
2502 char ch = stream->buf[stream->buf_pos];
2503 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2504 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2505 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2506 else if (ch == ';' || ch == '\r' || ch == '\n')
2508 TRACE( "reading %u byte chunk\n", chunk_size );
2509 stream->chunk_size = chunk_size;
2510 req->contentLength += chunk_size;
2511 return discard_chunked_eol(stream, req);
2513 remove_chunked_data(stream, 1);
2515 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2516 if (!stream->buf_size)
2518 stream->chunk_size = 0;
2519 return ERROR_SUCCESS;
2524 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2526 /* Allow reading only from read buffer */
2527 return 0;
2530 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2532 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2533 return !chunked_stream->chunk_size;
2536 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2537 DWORD *read, read_mode_t read_mode)
2539 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2540 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2542 if(chunked_stream->chunk_size == ~0u) {
2543 res = start_next_chunk(chunked_stream, req);
2544 if(res != ERROR_SUCCESS)
2545 return res;
2548 while(size && chunked_stream->chunk_size) {
2549 if(chunked_stream->buf_size) {
2550 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2552 /* this could block */
2553 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2554 break;
2556 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2557 remove_chunked_data(chunked_stream, read_bytes);
2558 }else {
2559 read_bytes = min(size, chunked_stream->chunk_size);
2561 if(read_mode == READMODE_NOBLOCK) {
2562 DWORD avail;
2564 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2565 break;
2566 if(read_bytes > avail)
2567 read_bytes = avail;
2569 /* this could block */
2570 if(read_bytes == chunked_stream->chunk_size)
2571 break;
2574 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2575 if(res != ERROR_SUCCESS)
2576 break;
2579 chunked_stream->chunk_size -= read_bytes;
2580 size -= read_bytes;
2581 ret_read += read_bytes;
2582 if(!chunked_stream->chunk_size) {
2583 assert(read_mode != READMODE_NOBLOCK);
2584 res = start_next_chunk(chunked_stream, req);
2585 if(res != ERROR_SUCCESS)
2586 break;
2589 if(read_mode == READMODE_ASYNC)
2590 read_mode = READMODE_NOBLOCK;
2593 TRACE("read %u bytes\n", ret_read);
2594 *read = ret_read;
2595 return res;
2598 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2600 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2602 /* FIXME: we can do better */
2603 return !chunked_stream->chunk_size;
2606 static void chunked_destroy(data_stream_t *stream)
2608 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2609 heap_free(chunked_stream);
2612 static const data_stream_vtbl_t chunked_stream_vtbl = {
2613 chunked_get_avail_data,
2614 chunked_end_of_data,
2615 chunked_read,
2616 chunked_drain_content,
2617 chunked_destroy
2620 /* set the request content length based on the headers */
2621 static DWORD set_content_length(http_request_t *request)
2623 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2624 WCHAR encoding[20];
2625 DWORD size;
2627 if(request->status_code == HTTP_STATUS_NO_CONTENT) {
2628 request->contentLength = request->netconn_stream.content_length = 0;
2629 return ERROR_SUCCESS;
2632 size = sizeof(request->contentLength);
2633 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2634 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2635 request->contentLength = ~0u;
2636 request->netconn_stream.content_length = request->contentLength;
2637 request->netconn_stream.content_read = request->read_size;
2639 size = sizeof(encoding);
2640 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2641 !strcmpiW(encoding, szChunked))
2643 chunked_stream_t *chunked_stream;
2645 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2646 if(!chunked_stream)
2647 return ERROR_OUTOFMEMORY;
2649 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2650 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2651 chunked_stream->chunk_size = ~0u;
2653 if(request->read_size) {
2654 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2655 chunked_stream->buf_size = request->read_size;
2656 request->read_size = request->read_pos = 0;
2659 request->data_stream = &chunked_stream->data_stream;
2660 request->contentLength = ~0u;
2661 request->read_chunked = TRUE;
2664 if(request->decoding) {
2665 int encoding_idx;
2667 static const WCHAR gzipW[] = {'g','z','i','p',0};
2669 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2670 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2671 return init_gzip_stream(request);
2674 return ERROR_SUCCESS;
2677 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2679 INTERNET_ASYNC_RESULT iar;
2681 iar.dwResult = result;
2682 iar.dwError = error;
2684 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2685 sizeof(INTERNET_ASYNC_RESULT));
2688 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2690 DWORD res, read = 0, avail = 0;
2691 read_mode_t mode;
2693 TRACE("%p\n", req);
2695 EnterCriticalSection( &req->read_section );
2697 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2698 res = refill_read_buffer(req, mode, &read);
2699 if(res == ERROR_SUCCESS && !first_notif)
2700 avail = get_avail_data(req);
2702 LeaveCriticalSection( &req->read_section );
2704 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2705 WARN("res %u read %u, closing connection\n", res, read);
2706 http_release_netconn(req, FALSE);
2709 if(res == ERROR_SUCCESS)
2710 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2711 else
2712 send_request_complete(req, 0, res);
2715 /* read data from the http connection (the read section must be held) */
2716 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2718 DWORD current_read = 0, ret_read = 0;
2719 read_mode_t read_mode;
2720 DWORD res = ERROR_SUCCESS;
2722 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2724 EnterCriticalSection( &req->read_section );
2726 if(req->read_size) {
2727 ret_read = min(size, req->read_size);
2728 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2729 req->read_size -= ret_read;
2730 req->read_pos += ret_read;
2731 if(read_mode == READMODE_ASYNC)
2732 read_mode = READMODE_NOBLOCK;
2735 if(ret_read < size) {
2736 res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, read_mode);
2737 ret_read += current_read;
2740 LeaveCriticalSection( &req->read_section );
2742 *read = ret_read;
2743 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2745 if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
2746 BOOL res;
2747 DWORD written;
2749 res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
2750 if(!res)
2751 WARN("WriteFile failed: %u\n", GetLastError());
2754 if(size && !ret_read)
2755 http_release_netconn(req, res == ERROR_SUCCESS);
2757 return res;
2761 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2763 http_request_t *req = (http_request_t*)hdr;
2764 DWORD res;
2766 EnterCriticalSection( &req->read_section );
2767 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2768 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2770 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2771 if(res == ERROR_SUCCESS)
2772 res = hdr->dwError;
2773 LeaveCriticalSection( &req->read_section );
2775 return res;
2778 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2780 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2781 http_request_t *req = (http_request_t*)workRequest->hdr;
2782 DWORD res;
2784 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2786 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2787 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2789 send_request_complete(req, res == ERROR_SUCCESS, res);
2792 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2793 DWORD flags, DWORD_PTR context)
2795 http_request_t *req = (http_request_t*)hdr;
2796 DWORD res, size, read, error = ERROR_SUCCESS;
2798 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2799 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2801 if (buffers->dwStructSize != sizeof(*buffers))
2802 return ERROR_INVALID_PARAMETER;
2804 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2806 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2808 WORKREQUEST workRequest;
2810 if (TryEnterCriticalSection( &req->read_section ))
2812 if (get_avail_data(req))
2814 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2815 &buffers->dwBufferLength, FALSE);
2816 size = buffers->dwBufferLength;
2817 LeaveCriticalSection( &req->read_section );
2818 goto done;
2820 LeaveCriticalSection( &req->read_section );
2823 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2824 workRequest.hdr = WININET_AddRef(&req->hdr);
2825 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2827 INTERNET_AsyncCall(&workRequest);
2829 return ERROR_IO_PENDING;
2832 read = 0;
2833 size = buffers->dwBufferLength;
2835 EnterCriticalSection( &req->read_section );
2836 if(hdr->dwError == ERROR_SUCCESS)
2837 hdr->dwError = INTERNET_HANDLE_IN_USE;
2838 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2839 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2841 while(1) {
2842 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2843 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2844 if(res != ERROR_SUCCESS)
2845 break;
2847 read += buffers->dwBufferLength;
2848 if(read == size || end_of_read_data(req))
2849 break;
2851 LeaveCriticalSection( &req->read_section );
2853 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2854 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2855 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2856 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2858 EnterCriticalSection( &req->read_section );
2861 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2862 hdr->dwError = ERROR_SUCCESS;
2863 else
2864 error = hdr->dwError;
2866 LeaveCriticalSection( &req->read_section );
2867 size = buffers->dwBufferLength;
2868 buffers->dwBufferLength = read;
2870 done:
2871 if (res == ERROR_SUCCESS) {
2872 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2873 &size, sizeof(size));
2876 return res==ERROR_SUCCESS ? error : res;
2879 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2881 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2882 http_request_t *req = (http_request_t*)workRequest->hdr;
2883 DWORD res;
2885 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2887 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2888 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2890 send_request_complete(req, res == ERROR_SUCCESS, res);
2893 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2894 DWORD flags, DWORD_PTR context)
2897 http_request_t *req = (http_request_t*)hdr;
2898 DWORD res, size, read, error = ERROR_SUCCESS;
2900 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2901 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2903 if (buffers->dwStructSize != sizeof(*buffers))
2904 return ERROR_INVALID_PARAMETER;
2906 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2908 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2910 WORKREQUEST workRequest;
2912 if (TryEnterCriticalSection( &req->read_section ))
2914 if (get_avail_data(req))
2916 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2917 &buffers->dwBufferLength, FALSE);
2918 size = buffers->dwBufferLength;
2919 LeaveCriticalSection( &req->read_section );
2920 goto done;
2922 LeaveCriticalSection( &req->read_section );
2925 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2926 workRequest.hdr = WININET_AddRef(&req->hdr);
2927 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2929 INTERNET_AsyncCall(&workRequest);
2931 return ERROR_IO_PENDING;
2934 read = 0;
2935 size = buffers->dwBufferLength;
2937 EnterCriticalSection( &req->read_section );
2938 if(hdr->dwError == ERROR_SUCCESS)
2939 hdr->dwError = INTERNET_HANDLE_IN_USE;
2940 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2941 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2943 while(1) {
2944 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2945 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2946 if(res != ERROR_SUCCESS)
2947 break;
2949 read += buffers->dwBufferLength;
2950 if(read == size || end_of_read_data(req))
2951 break;
2953 LeaveCriticalSection( &req->read_section );
2955 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2956 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2957 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2958 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2960 EnterCriticalSection( &req->read_section );
2963 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2964 hdr->dwError = ERROR_SUCCESS;
2965 else
2966 error = hdr->dwError;
2968 LeaveCriticalSection( &req->read_section );
2969 size = buffers->dwBufferLength;
2970 buffers->dwBufferLength = read;
2972 done:
2973 if (res == ERROR_SUCCESS) {
2974 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2975 &size, sizeof(size));
2978 return res==ERROR_SUCCESS ? error : res;
2981 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2983 DWORD res;
2984 http_request_t *request = (http_request_t*)hdr;
2986 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2988 *written = 0;
2989 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
2990 if (res == ERROR_SUCCESS)
2991 request->bytesWritten += *written;
2993 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2994 return res;
2997 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2999 http_request_t *req = (http_request_t*)workRequest->hdr;
3001 HTTP_ReceiveRequestData(req, FALSE);
3004 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
3006 http_request_t *req = (http_request_t*)hdr;
3008 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
3010 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3012 WORKREQUEST workRequest;
3014 /* never wait, if we can't enter the section we queue an async request right away */
3015 if (TryEnterCriticalSection( &req->read_section ))
3017 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
3018 if ((*available = get_avail_data( req ))) goto done;
3019 if (end_of_read_data( req )) goto done;
3020 LeaveCriticalSection( &req->read_section );
3023 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
3024 workRequest.hdr = WININET_AddRef( &req->hdr );
3026 INTERNET_AsyncCall(&workRequest);
3028 return ERROR_IO_PENDING;
3031 EnterCriticalSection( &req->read_section );
3033 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
3035 refill_read_buffer( req, READMODE_ASYNC, NULL );
3036 *available = get_avail_data( req );
3039 done:
3040 LeaveCriticalSection( &req->read_section );
3042 TRACE( "returning %u\n", *available );
3043 return ERROR_SUCCESS;
3046 static const object_vtbl_t HTTPREQVtbl = {
3047 HTTPREQ_Destroy,
3048 HTTPREQ_CloseConnection,
3049 HTTPREQ_QueryOption,
3050 HTTPREQ_SetOption,
3051 HTTPREQ_ReadFile,
3052 HTTPREQ_ReadFileExA,
3053 HTTPREQ_ReadFileExW,
3054 HTTPREQ_WriteFile,
3055 HTTPREQ_QueryDataAvailable,
3056 NULL
3059 /***********************************************************************
3060 * HTTP_HttpOpenRequestW (internal)
3062 * Open a HTTP request handle
3064 * RETURNS
3065 * HINTERNET a HTTP request handle on success
3066 * NULL on failure
3069 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3070 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3071 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3072 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3074 appinfo_t *hIC = session->appInfo;
3075 http_request_t *request;
3076 INTERNET_PORT port;
3077 DWORD len, res = ERROR_SUCCESS;
3079 TRACE("-->\n");
3081 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3082 if(!request)
3083 return ERROR_OUTOFMEMORY;
3085 request->hdr.htype = WH_HHTTPREQ;
3086 request->hdr.dwFlags = dwFlags;
3087 request->hdr.dwContext = dwContext;
3088 request->contentLength = ~0u;
3090 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3091 request->data_stream = &request->netconn_stream.data_stream;
3092 request->connect_timeout = session->connect_timeout;
3093 request->send_timeout = session->send_timeout;
3094 request->receive_timeout = session->receive_timeout;
3096 InitializeCriticalSection( &request->read_section );
3097 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3099 WININET_AddRef( &session->hdr );
3100 request->session = session;
3101 list_add_head( &session->hdr.children, &request->hdr.entry );
3103 port = session->hostPort;
3104 if(port == INTERNET_INVALID_PORT_NUMBER)
3105 port = dwFlags & INTERNET_FLAG_SECURE ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT;
3107 request->server = get_server(session->hostName, port, TRUE);
3108 if(!request->server) {
3109 WININET_Release(&request->hdr);
3110 return ERROR_OUTOFMEMORY;
3113 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3114 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3115 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3116 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3118 if (lpszObjectName && *lpszObjectName) {
3119 HRESULT rc;
3121 len = 0;
3122 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3123 if (rc != E_POINTER)
3124 len = strlenW(lpszObjectName)+1;
3125 request->path = heap_alloc(len*sizeof(WCHAR));
3126 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3127 URL_ESCAPE_SPACES_ONLY);
3128 if (rc != S_OK)
3130 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3131 strcpyW(request->path,lpszObjectName);
3133 }else {
3134 static const WCHAR slashW[] = {'/',0};
3136 request->path = heap_strdupW(slashW);
3139 if (lpszReferrer && *lpszReferrer)
3140 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3142 if (lpszAcceptTypes)
3144 int i;
3145 for (i = 0; lpszAcceptTypes[i]; i++)
3147 if (!*lpszAcceptTypes[i]) continue;
3148 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3149 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3150 HTTP_ADDHDR_FLAG_REQ |
3151 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3155 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3156 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
3158 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
3159 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
3160 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
3162 WCHAR *host_name;
3164 static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
3166 host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
3167 if (!host_name) {
3168 res = ERROR_OUTOFMEMORY;
3169 goto lend;
3172 sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
3173 HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3174 heap_free(host_name);
3176 else
3177 HTTP_ProcessHeader(request, hostW, session->hostName,
3178 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3180 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3181 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3182 INTERNET_DEFAULT_HTTPS_PORT :
3183 INTERNET_DEFAULT_HTTP_PORT);
3185 if (hIC->proxy && hIC->proxy[0])
3186 HTTP_DealWithProxy( hIC, session, request );
3188 INTERNET_SendCallback(&session->hdr, dwContext,
3189 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3190 sizeof(HINTERNET));
3192 lend:
3193 TRACE("<-- %u (%p)\n", res, request);
3195 if(res != ERROR_SUCCESS) {
3196 WININET_Release( &request->hdr );
3197 *ret = NULL;
3198 return res;
3201 *ret = request->hdr.hInternet;
3202 return ERROR_SUCCESS;
3205 /***********************************************************************
3206 * HttpOpenRequestW (WININET.@)
3208 * Open a HTTP request handle
3210 * RETURNS
3211 * HINTERNET a HTTP request handle on success
3212 * NULL on failure
3215 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3216 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3217 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3218 DWORD dwFlags, DWORD_PTR dwContext)
3220 http_session_t *session;
3221 HINTERNET handle = NULL;
3222 DWORD res;
3224 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3225 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3226 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3227 dwFlags, dwContext);
3228 if(lpszAcceptTypes!=NULL)
3230 int i;
3231 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3232 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3235 session = (http_session_t*) get_handle_object( hHttpSession );
3236 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3238 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3239 goto lend;
3243 * My tests seem to show that the windows version does not
3244 * become asynchronous until after this point. And anyhow
3245 * if this call was asynchronous then how would you get the
3246 * necessary HINTERNET pointer returned by this function.
3249 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3250 lpszVersion, lpszReferrer, lpszAcceptTypes,
3251 dwFlags, dwContext, &handle);
3252 lend:
3253 if( session )
3254 WININET_Release( &session->hdr );
3255 TRACE("returning %p\n", handle);
3256 if(res != ERROR_SUCCESS)
3257 SetLastError(res);
3258 return handle;
3261 static const LPCWSTR header_lookup[] = {
3262 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3263 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3264 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3265 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3266 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3267 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3268 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3269 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3270 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3271 szDate, /* HTTP_QUERY_DATE = 9 */
3272 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3273 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3274 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3275 szURI, /* HTTP_QUERY_URI = 13 */
3276 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3277 NULL, /* HTTP_QUERY_COST = 15 */
3278 NULL, /* HTTP_QUERY_LINK = 16 */
3279 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3280 NULL, /* HTTP_QUERY_VERSION = 18 */
3281 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3282 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3283 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3284 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3285 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3286 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3287 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3288 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3289 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3290 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3291 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3292 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3293 NULL, /* HTTP_QUERY_FROM = 31 */
3294 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3295 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3296 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3297 szReferer, /* HTTP_QUERY_REFERER = 35 */
3298 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3299 szServer, /* HTTP_QUERY_SERVER = 37 */
3300 NULL, /* HTTP_TITLE = 38 */
3301 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3302 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3303 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3304 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3305 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3306 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3307 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3308 NULL, /* HTTP_QUERY_REFRESH = 46 */
3309 szContent_Disposition, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3310 szAge, /* HTTP_QUERY_AGE = 48 */
3311 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3312 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3313 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3314 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3315 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3316 szETag, /* HTTP_QUERY_ETAG = 54 */
3317 hostW, /* HTTP_QUERY_HOST = 55 */
3318 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3319 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3320 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3321 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3322 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3323 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3324 szRange, /* HTTP_QUERY_RANGE = 62 */
3325 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3326 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3327 szVary, /* HTTP_QUERY_VARY = 65 */
3328 szVia, /* HTTP_QUERY_VIA = 66 */
3329 szWarning, /* HTTP_QUERY_WARNING = 67 */
3330 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3331 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3332 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3335 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3337 /***********************************************************************
3338 * HTTP_HttpQueryInfoW (internal)
3340 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3341 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3343 LPHTTPHEADERW lphttpHdr = NULL;
3344 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3345 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3346 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3347 INT index = -1;
3349 /* Find requested header structure */
3350 switch (level)
3352 case HTTP_QUERY_CUSTOM:
3353 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3354 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3355 break;
3356 case HTTP_QUERY_RAW_HEADERS_CRLF:
3358 LPWSTR headers;
3359 DWORD len = 0;
3360 DWORD res = ERROR_INVALID_PARAMETER;
3362 if (request_only)
3363 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3364 else
3365 headers = request->rawHeaders;
3367 if (headers)
3368 len = strlenW(headers) * sizeof(WCHAR);
3370 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3372 len += sizeof(WCHAR);
3373 res = ERROR_INSUFFICIENT_BUFFER;
3375 else if (lpBuffer)
3377 if (headers)
3378 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3379 else
3381 len = strlenW(szCrLf) * sizeof(WCHAR);
3382 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3384 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3385 res = ERROR_SUCCESS;
3387 *lpdwBufferLength = len;
3389 if (request_only) heap_free(headers);
3390 return res;
3392 case HTTP_QUERY_RAW_HEADERS:
3394 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3395 DWORD i, size = 0;
3396 LPWSTR pszString = lpBuffer;
3398 for (i = 0; ppszRawHeaderLines[i]; i++)
3399 size += strlenW(ppszRawHeaderLines[i]) + 1;
3401 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3403 HTTP_FreeTokens(ppszRawHeaderLines);
3404 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3405 return ERROR_INSUFFICIENT_BUFFER;
3407 if (pszString)
3409 for (i = 0; ppszRawHeaderLines[i]; i++)
3411 DWORD len = strlenW(ppszRawHeaderLines[i]);
3412 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3413 pszString += len+1;
3415 *pszString = '\0';
3416 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3418 *lpdwBufferLength = size * sizeof(WCHAR);
3419 HTTP_FreeTokens(ppszRawHeaderLines);
3421 return ERROR_SUCCESS;
3423 case HTTP_QUERY_STATUS_TEXT:
3424 if (request->statusText)
3426 DWORD len = strlenW(request->statusText);
3427 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3429 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3430 return ERROR_INSUFFICIENT_BUFFER;
3432 if (lpBuffer)
3434 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3435 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3437 *lpdwBufferLength = len * sizeof(WCHAR);
3438 return ERROR_SUCCESS;
3440 break;
3441 case HTTP_QUERY_VERSION:
3442 if (request->version)
3444 DWORD len = strlenW(request->version);
3445 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3447 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3448 return ERROR_INSUFFICIENT_BUFFER;
3450 if (lpBuffer)
3452 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3453 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3455 *lpdwBufferLength = len * sizeof(WCHAR);
3456 return ERROR_SUCCESS;
3458 break;
3459 case HTTP_QUERY_CONTENT_ENCODING:
3460 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3461 requested_index,request_only);
3462 break;
3463 case HTTP_QUERY_STATUS_CODE: {
3464 DWORD res = ERROR_SUCCESS;
3466 if(request_only)
3467 return ERROR_HTTP_INVALID_QUERY_REQUEST;
3469 if(requested_index)
3470 break;
3472 if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3473 if(*lpdwBufferLength >= sizeof(DWORD))
3474 *(DWORD*)lpBuffer = request->status_code;
3475 else
3476 res = ERROR_INSUFFICIENT_BUFFER;
3477 *lpdwBufferLength = sizeof(DWORD);
3478 }else {
3479 WCHAR buf[12];
3480 DWORD size;
3481 static const WCHAR formatW[] = {'%','u',0};
3483 size = (sprintfW(buf, formatW, request->status_code)+1) * sizeof(WCHAR);
3485 if(size <= *lpdwBufferLength)
3486 memcpy(lpBuffer, buf, size);
3487 else
3488 res = ERROR_INSUFFICIENT_BUFFER;
3490 *lpdwBufferLength = size;
3492 return res;
3494 default:
3495 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3497 if (level < LAST_TABLE_HEADER && header_lookup[level])
3498 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3499 requested_index,request_only);
3502 if (index >= 0)
3503 lphttpHdr = &request->custHeaders[index];
3505 /* Ensure header satisfies requested attributes */
3506 if (!lphttpHdr ||
3507 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3508 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3510 return ERROR_HTTP_HEADER_NOT_FOUND;
3513 if (lpdwIndex) (*lpdwIndex)++;
3515 /* coalesce value to requested type */
3516 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3518 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3519 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3521 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3523 time_t tmpTime;
3524 struct tm tmpTM;
3525 SYSTEMTIME *STHook;
3527 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3529 tmpTM = *gmtime(&tmpTime);
3530 STHook = (SYSTEMTIME *)lpBuffer;
3531 STHook->wDay = tmpTM.tm_mday;
3532 STHook->wHour = tmpTM.tm_hour;
3533 STHook->wMilliseconds = 0;
3534 STHook->wMinute = tmpTM.tm_min;
3535 STHook->wDayOfWeek = tmpTM.tm_wday;
3536 STHook->wMonth = tmpTM.tm_mon + 1;
3537 STHook->wSecond = tmpTM.tm_sec;
3538 STHook->wYear = tmpTM.tm_year;
3540 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3541 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3542 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3544 else if (lphttpHdr->lpszValue)
3546 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3548 if (len > *lpdwBufferLength)
3550 *lpdwBufferLength = len;
3551 return ERROR_INSUFFICIENT_BUFFER;
3553 if (lpBuffer)
3555 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3556 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3558 *lpdwBufferLength = len - sizeof(WCHAR);
3560 return ERROR_SUCCESS;
3563 /***********************************************************************
3564 * HttpQueryInfoW (WININET.@)
3566 * Queries for information about an HTTP request
3568 * RETURNS
3569 * TRUE on success
3570 * FALSE on failure
3573 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3574 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3576 http_request_t *request;
3577 DWORD res;
3579 if (TRACE_ON(wininet)) {
3580 #define FE(x) { x, #x }
3581 static const wininet_flag_info query_flags[] = {
3582 FE(HTTP_QUERY_MIME_VERSION),
3583 FE(HTTP_QUERY_CONTENT_TYPE),
3584 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3585 FE(HTTP_QUERY_CONTENT_ID),
3586 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3587 FE(HTTP_QUERY_CONTENT_LENGTH),
3588 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3589 FE(HTTP_QUERY_ALLOW),
3590 FE(HTTP_QUERY_PUBLIC),
3591 FE(HTTP_QUERY_DATE),
3592 FE(HTTP_QUERY_EXPIRES),
3593 FE(HTTP_QUERY_LAST_MODIFIED),
3594 FE(HTTP_QUERY_MESSAGE_ID),
3595 FE(HTTP_QUERY_URI),
3596 FE(HTTP_QUERY_DERIVED_FROM),
3597 FE(HTTP_QUERY_COST),
3598 FE(HTTP_QUERY_LINK),
3599 FE(HTTP_QUERY_PRAGMA),
3600 FE(HTTP_QUERY_VERSION),
3601 FE(HTTP_QUERY_STATUS_CODE),
3602 FE(HTTP_QUERY_STATUS_TEXT),
3603 FE(HTTP_QUERY_RAW_HEADERS),
3604 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3605 FE(HTTP_QUERY_CONNECTION),
3606 FE(HTTP_QUERY_ACCEPT),
3607 FE(HTTP_QUERY_ACCEPT_CHARSET),
3608 FE(HTTP_QUERY_ACCEPT_ENCODING),
3609 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3610 FE(HTTP_QUERY_AUTHORIZATION),
3611 FE(HTTP_QUERY_CONTENT_ENCODING),
3612 FE(HTTP_QUERY_FORWARDED),
3613 FE(HTTP_QUERY_FROM),
3614 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3615 FE(HTTP_QUERY_LOCATION),
3616 FE(HTTP_QUERY_ORIG_URI),
3617 FE(HTTP_QUERY_REFERER),
3618 FE(HTTP_QUERY_RETRY_AFTER),
3619 FE(HTTP_QUERY_SERVER),
3620 FE(HTTP_QUERY_TITLE),
3621 FE(HTTP_QUERY_USER_AGENT),
3622 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3623 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3624 FE(HTTP_QUERY_ACCEPT_RANGES),
3625 FE(HTTP_QUERY_SET_COOKIE),
3626 FE(HTTP_QUERY_COOKIE),
3627 FE(HTTP_QUERY_REQUEST_METHOD),
3628 FE(HTTP_QUERY_REFRESH),
3629 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3630 FE(HTTP_QUERY_AGE),
3631 FE(HTTP_QUERY_CACHE_CONTROL),
3632 FE(HTTP_QUERY_CONTENT_BASE),
3633 FE(HTTP_QUERY_CONTENT_LOCATION),
3634 FE(HTTP_QUERY_CONTENT_MD5),
3635 FE(HTTP_QUERY_CONTENT_RANGE),
3636 FE(HTTP_QUERY_ETAG),
3637 FE(HTTP_QUERY_HOST),
3638 FE(HTTP_QUERY_IF_MATCH),
3639 FE(HTTP_QUERY_IF_NONE_MATCH),
3640 FE(HTTP_QUERY_IF_RANGE),
3641 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3642 FE(HTTP_QUERY_MAX_FORWARDS),
3643 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3644 FE(HTTP_QUERY_RANGE),
3645 FE(HTTP_QUERY_TRANSFER_ENCODING),
3646 FE(HTTP_QUERY_UPGRADE),
3647 FE(HTTP_QUERY_VARY),
3648 FE(HTTP_QUERY_VIA),
3649 FE(HTTP_QUERY_WARNING),
3650 FE(HTTP_QUERY_CUSTOM)
3652 static const wininet_flag_info modifier_flags[] = {
3653 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3654 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3655 FE(HTTP_QUERY_FLAG_NUMBER),
3656 FE(HTTP_QUERY_FLAG_COALESCE)
3658 #undef FE
3659 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3660 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3661 DWORD i;
3663 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3664 TRACE(" Attribute:");
3665 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3666 if (query_flags[i].val == info) {
3667 TRACE(" %s", query_flags[i].name);
3668 break;
3671 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3672 TRACE(" Unknown (%08x)", info);
3675 TRACE(" Modifier:");
3676 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3677 if (modifier_flags[i].val & info_mod) {
3678 TRACE(" %s", modifier_flags[i].name);
3679 info_mod &= ~ modifier_flags[i].val;
3683 if (info_mod) {
3684 TRACE(" Unknown (%08x)", info_mod);
3686 TRACE("\n");
3689 request = (http_request_t*) get_handle_object( hHttpRequest );
3690 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3692 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3693 goto lend;
3696 if (lpBuffer == NULL)
3697 *lpdwBufferLength = 0;
3698 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3699 lpBuffer, lpdwBufferLength, lpdwIndex);
3701 lend:
3702 if( request )
3703 WININET_Release( &request->hdr );
3705 TRACE("%u <--\n", res);
3706 if(res != ERROR_SUCCESS)
3707 SetLastError(res);
3708 return res == ERROR_SUCCESS;
3711 /***********************************************************************
3712 * HttpQueryInfoA (WININET.@)
3714 * Queries for information about an HTTP request
3716 * RETURNS
3717 * TRUE on success
3718 * FALSE on failure
3721 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3722 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3724 BOOL result;
3725 DWORD len;
3726 WCHAR* bufferW;
3728 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3729 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3731 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3732 lpdwBufferLength, lpdwIndex );
3735 if (lpBuffer)
3737 DWORD alloclen;
3738 len = (*lpdwBufferLength)*sizeof(WCHAR);
3739 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3741 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3742 if (alloclen < len)
3743 alloclen = len;
3745 else
3746 alloclen = len;
3747 bufferW = heap_alloc(alloclen);
3748 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3749 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3750 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3751 } else
3753 bufferW = NULL;
3754 len = 0;
3757 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3758 &len, lpdwIndex );
3759 if( result )
3761 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3762 lpBuffer, *lpdwBufferLength, NULL, NULL );
3763 *lpdwBufferLength = len - 1;
3765 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3767 else
3768 /* since the strings being returned from HttpQueryInfoW should be
3769 * only ASCII characters, it is reasonable to assume that all of
3770 * the Unicode characters can be reduced to a single byte */
3771 *lpdwBufferLength = len / sizeof(WCHAR);
3773 heap_free( bufferW );
3774 return result;
3777 /***********************************************************************
3778 * HTTP_GetRedirectURL (internal)
3780 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3782 static WCHAR szHttp[] = {'h','t','t','p',0};
3783 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3784 http_session_t *session = request->session;
3785 URL_COMPONENTSW urlComponents;
3786 DWORD url_length = 0;
3787 LPWSTR orig_url;
3788 LPWSTR combined_url;
3790 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3791 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3792 urlComponents.dwSchemeLength = 0;
3793 urlComponents.lpszHostName = session->hostName;
3794 urlComponents.dwHostNameLength = 0;
3795 urlComponents.nPort = session->hostPort;
3796 urlComponents.lpszUserName = session->userName;
3797 urlComponents.dwUserNameLength = 0;
3798 urlComponents.lpszPassword = NULL;
3799 urlComponents.dwPasswordLength = 0;
3800 urlComponents.lpszUrlPath = request->path;
3801 urlComponents.dwUrlPathLength = 0;
3802 urlComponents.lpszExtraInfo = NULL;
3803 urlComponents.dwExtraInfoLength = 0;
3805 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3806 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3807 return NULL;
3809 orig_url = heap_alloc(url_length);
3811 /* convert from bytes to characters */
3812 url_length = url_length / sizeof(WCHAR) - 1;
3813 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3815 heap_free(orig_url);
3816 return NULL;
3819 url_length = 0;
3820 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3821 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3823 heap_free(orig_url);
3824 return NULL;
3826 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3828 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3830 heap_free(orig_url);
3831 heap_free(combined_url);
3832 return NULL;
3834 heap_free(orig_url);
3835 return combined_url;
3839 /***********************************************************************
3840 * HTTP_HandleRedirect (internal)
3842 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3844 http_session_t *session = request->session;
3845 appinfo_t *hIC = session->appInfo;
3846 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3847 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3848 int index;
3850 if(lpszUrl[0]=='/')
3852 /* if it's an absolute path, keep the same session info */
3853 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3855 else
3857 URL_COMPONENTSW urlComponents;
3858 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3859 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3860 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3861 BOOL custom_port = FALSE;
3863 static WCHAR httpW[] = {'h','t','t','p',0};
3864 static WCHAR httpsW[] = {'h','t','t','p','s',0};
3866 userName[0] = 0;
3867 hostName[0] = 0;
3868 protocol[0] = 0;
3870 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3871 urlComponents.lpszScheme = protocol;
3872 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3873 urlComponents.lpszHostName = hostName;
3874 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3875 urlComponents.lpszUserName = userName;
3876 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3877 urlComponents.lpszPassword = NULL;
3878 urlComponents.dwPasswordLength = 0;
3879 urlComponents.lpszUrlPath = path;
3880 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3881 urlComponents.lpszExtraInfo = NULL;
3882 urlComponents.dwExtraInfoLength = 0;
3883 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3884 return INTERNET_GetLastError();
3886 if(!strcmpiW(protocol, httpW)) {
3887 if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
3888 TRACE("redirect from secure page to non-secure page\n");
3889 /* FIXME: warn about from secure redirect to non-secure page */
3890 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3893 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3894 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3895 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT)
3896 custom_port = TRUE;
3897 }else if(!strcmpiW(protocol, httpsW)) {
3898 if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
3899 TRACE("redirect from non-secure page to secure page\n");
3900 /* FIXME: notify about redirect to secure page */
3901 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3904 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3905 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3906 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3907 custom_port = TRUE;
3910 heap_free(session->hostName);
3912 if(custom_port) {
3913 int len;
3914 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3915 len = lstrlenW(hostName);
3916 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3917 session->hostName = heap_alloc(len*sizeof(WCHAR));
3918 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3920 else
3921 session->hostName = heap_strdupW(hostName);
3923 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3925 heap_free(session->userName);
3926 session->userName = NULL;
3927 if (userName[0])
3928 session->userName = heap_strdupW(userName);
3930 reset_data_stream(request);
3932 if(!using_proxy && (strcmpiW(request->server->name, hostName) || request->server->port != urlComponents.nPort)) {
3933 server_t *new_server;
3935 new_server = get_server(hostName, urlComponents.nPort, TRUE);
3936 server_release(request->server);
3937 request->server = new_server;
3940 heap_free(request->path);
3941 request->path=NULL;
3942 if (*path)
3944 DWORD needed = 0;
3945 HRESULT rc;
3947 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3948 if (rc != E_POINTER)
3949 needed = strlenW(path)+1;
3950 request->path = heap_alloc(needed*sizeof(WCHAR));
3951 rc = UrlEscapeW(path, request->path, &needed,
3952 URL_ESCAPE_SPACES_ONLY);
3953 if (rc != S_OK)
3955 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3956 strcpyW(request->path,path);
3960 /* Remove custom content-type/length headers on redirects. */
3961 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3962 if (0 <= index)
3963 HTTP_DeleteCustomHeader(request, index);
3964 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3965 if (0 <= index)
3966 HTTP_DeleteCustomHeader(request, index);
3968 return ERROR_SUCCESS;
3971 /***********************************************************************
3972 * HTTP_build_req (internal)
3974 * concatenate all the strings in the request together
3976 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3978 LPCWSTR *t;
3979 LPWSTR str;
3981 for( t = list; *t ; t++ )
3982 len += strlenW( *t );
3983 len++;
3985 str = heap_alloc(len*sizeof(WCHAR));
3986 *str = 0;
3988 for( t = list; *t ; t++ )
3989 strcatW( str, *t );
3991 return str;
3994 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3996 LPWSTR lpszPath;
3997 LPWSTR requestString;
3998 INT len;
3999 INT cnt;
4000 INT responseLen;
4001 char *ascii_req;
4002 DWORD res;
4003 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
4004 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
4005 http_session_t *session = request->session;
4007 TRACE("\n");
4009 lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
4010 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
4011 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
4012 heap_free( lpszPath );
4014 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4015 NULL, 0, NULL, NULL );
4016 len--; /* the nul terminator isn't needed */
4017 ascii_req = heap_alloc(len);
4018 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
4019 heap_free( requestString );
4021 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
4023 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4024 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
4025 heap_free( ascii_req );
4026 if (res != ERROR_SUCCESS)
4027 return res;
4029 responseLen = HTTP_GetResponseHeaders( request, TRUE );
4030 if (!responseLen)
4031 return ERROR_HTTP_INVALID_HEADER;
4033 return ERROR_SUCCESS;
4036 static void HTTP_InsertCookies(http_request_t *request)
4038 DWORD cookie_size, size, cnt = 0;
4039 HTTPHEADERW *host;
4040 WCHAR *cookies;
4042 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
4044 host = HTTP_GetHeader(request, hostW);
4045 if(!host)
4046 return;
4048 if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
4049 return;
4051 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
4052 if(!(cookies = heap_alloc(size)))
4053 return;
4055 cnt += sprintfW(cookies, cookieW);
4056 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
4057 strcatW(cookies, szCrLf);
4059 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4061 heap_free(cookies);
4064 static WORD HTTP_ParseWkday(LPCWSTR day)
4066 static const WCHAR days[7][4] = {{ 's','u','n',0 },
4067 { 'm','o','n',0 },
4068 { 't','u','e',0 },
4069 { 'w','e','d',0 },
4070 { 't','h','u',0 },
4071 { 'f','r','i',0 },
4072 { 's','a','t',0 }};
4073 int i;
4074 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4075 if (!strcmpiW(day, days[i]))
4076 return i;
4078 /* Invalid */
4079 return 7;
4082 static WORD HTTP_ParseMonth(LPCWSTR month)
4084 static const WCHAR jan[] = { 'j','a','n',0 };
4085 static const WCHAR feb[] = { 'f','e','b',0 };
4086 static const WCHAR mar[] = { 'm','a','r',0 };
4087 static const WCHAR apr[] = { 'a','p','r',0 };
4088 static const WCHAR may[] = { 'm','a','y',0 };
4089 static const WCHAR jun[] = { 'j','u','n',0 };
4090 static const WCHAR jul[] = { 'j','u','l',0 };
4091 static const WCHAR aug[] = { 'a','u','g',0 };
4092 static const WCHAR sep[] = { 's','e','p',0 };
4093 static const WCHAR oct[] = { 'o','c','t',0 };
4094 static const WCHAR nov[] = { 'n','o','v',0 };
4095 static const WCHAR dec[] = { 'd','e','c',0 };
4097 if (!strcmpiW(month, jan)) return 1;
4098 if (!strcmpiW(month, feb)) return 2;
4099 if (!strcmpiW(month, mar)) return 3;
4100 if (!strcmpiW(month, apr)) return 4;
4101 if (!strcmpiW(month, may)) return 5;
4102 if (!strcmpiW(month, jun)) return 6;
4103 if (!strcmpiW(month, jul)) return 7;
4104 if (!strcmpiW(month, aug)) return 8;
4105 if (!strcmpiW(month, sep)) return 9;
4106 if (!strcmpiW(month, oct)) return 10;
4107 if (!strcmpiW(month, nov)) return 11;
4108 if (!strcmpiW(month, dec)) return 12;
4109 /* Invalid */
4110 return 0;
4113 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4114 * optionally preceded by whitespace.
4115 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4116 * st, and sets *str to the first character after the time format.
4118 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4120 LPCWSTR ptr = *str;
4121 WCHAR *nextPtr;
4122 unsigned long num;
4124 while (isspaceW(*ptr))
4125 ptr++;
4127 num = strtoulW(ptr, &nextPtr, 10);
4128 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4130 ERR("unexpected time format %s\n", debugstr_w(ptr));
4131 return FALSE;
4133 if (num > 23)
4135 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4136 return FALSE;
4138 ptr = nextPtr + 1;
4139 st->wHour = (WORD)num;
4140 num = strtoulW(ptr, &nextPtr, 10);
4141 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4143 ERR("unexpected time format %s\n", debugstr_w(ptr));
4144 return FALSE;
4146 if (num > 59)
4148 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4149 return FALSE;
4151 ptr = nextPtr + 1;
4152 st->wMinute = (WORD)num;
4153 num = strtoulW(ptr, &nextPtr, 10);
4154 if (!nextPtr || nextPtr <= ptr)
4156 ERR("unexpected time format %s\n", debugstr_w(ptr));
4157 return FALSE;
4159 if (num > 59)
4161 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4162 return FALSE;
4164 ptr = nextPtr + 1;
4165 *str = ptr;
4166 st->wSecond = (WORD)num;
4167 return TRUE;
4170 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4172 static const WCHAR gmt[]= { 'G','M','T',0 };
4173 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4174 LPCWSTR ptr;
4175 SYSTEMTIME st = { 0 };
4176 unsigned long num;
4178 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4179 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4180 *dayPtr = *ptr;
4181 *dayPtr = 0;
4182 st.wDayOfWeek = HTTP_ParseWkday(day);
4183 if (st.wDayOfWeek >= 7)
4185 ERR("unexpected weekday %s\n", debugstr_w(day));
4186 return FALSE;
4189 while (isspaceW(*ptr))
4190 ptr++;
4192 for (monthPtr = month; !isspace(*ptr) &&
4193 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4194 monthPtr++, ptr++)
4195 *monthPtr = *ptr;
4196 *monthPtr = 0;
4197 st.wMonth = HTTP_ParseMonth(month);
4198 if (!st.wMonth || st.wMonth > 12)
4200 ERR("unexpected month %s\n", debugstr_w(month));
4201 return FALSE;
4204 while (isspaceW(*ptr))
4205 ptr++;
4207 num = strtoulW(ptr, &nextPtr, 10);
4208 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4210 ERR("unexpected day %s\n", debugstr_w(ptr));
4211 return FALSE;
4213 ptr = nextPtr;
4214 st.wDay = (WORD)num;
4216 while (isspaceW(*ptr))
4217 ptr++;
4219 if (!HTTP_ParseTime(&st, &ptr))
4220 return FALSE;
4222 while (isspaceW(*ptr))
4223 ptr++;
4225 num = strtoulW(ptr, &nextPtr, 10);
4226 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4228 ERR("unexpected year %s\n", debugstr_w(ptr));
4229 return FALSE;
4231 ptr = nextPtr;
4232 st.wYear = (WORD)num;
4234 while (isspaceW(*ptr))
4235 ptr++;
4237 /* asctime() doesn't report a timezone, but some web servers do, so accept
4238 * with or without GMT.
4240 if (*ptr && strcmpW(ptr, gmt))
4242 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4243 return FALSE;
4245 return SystemTimeToFileTime(&st, ft);
4248 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4250 static const WCHAR gmt[]= { 'G','M','T',0 };
4251 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4252 LPCWSTR ptr;
4253 unsigned long num;
4254 SYSTEMTIME st = { 0 };
4256 ptr = strchrW(value, ',');
4257 if (!ptr)
4258 return FALSE;
4259 if (ptr - value != 3)
4261 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4262 return FALSE;
4264 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4265 day[3] = 0;
4266 st.wDayOfWeek = HTTP_ParseWkday(day);
4267 if (st.wDayOfWeek > 6)
4269 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4270 return FALSE;
4272 ptr++;
4274 while (isspaceW(*ptr))
4275 ptr++;
4277 num = strtoulW(ptr, &nextPtr, 10);
4278 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4280 WARN("unexpected day %s\n", debugstr_w(value));
4281 return FALSE;
4283 ptr = nextPtr;
4284 st.wDay = (WORD)num;
4286 while (isspaceW(*ptr))
4287 ptr++;
4289 for (monthPtr = month; !isspace(*ptr) &&
4290 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4291 monthPtr++, ptr++)
4292 *monthPtr = *ptr;
4293 *monthPtr = 0;
4294 st.wMonth = HTTP_ParseMonth(month);
4295 if (!st.wMonth || st.wMonth > 12)
4297 WARN("unexpected month %s\n", debugstr_w(month));
4298 return FALSE;
4301 while (isspaceW(*ptr))
4302 ptr++;
4304 num = strtoulW(ptr, &nextPtr, 10);
4305 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4307 ERR("unexpected year %s\n", debugstr_w(value));
4308 return FALSE;
4310 ptr = nextPtr;
4311 st.wYear = (WORD)num;
4313 if (!HTTP_ParseTime(&st, &ptr))
4314 return FALSE;
4316 while (isspaceW(*ptr))
4317 ptr++;
4319 if (strcmpW(ptr, gmt))
4321 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4322 return FALSE;
4324 return SystemTimeToFileTime(&st, ft);
4327 static WORD HTTP_ParseWeekday(LPCWSTR day)
4329 static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4330 { 'm','o','n','d','a','y',0 },
4331 { 't','u','e','s','d','a','y',0 },
4332 { 'w','e','d','n','e','s','d','a','y',0 },
4333 { 't','h','u','r','s','d','a','y',0 },
4334 { 'f','r','i','d','a','y',0 },
4335 { 's','a','t','u','r','d','a','y',0 }};
4336 int i;
4337 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4338 if (!strcmpiW(day, days[i]))
4339 return i;
4341 /* Invalid */
4342 return 7;
4345 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4347 static const WCHAR gmt[]= { 'G','M','T',0 };
4348 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4349 LPCWSTR ptr;
4350 unsigned long num;
4351 SYSTEMTIME st = { 0 };
4353 ptr = strchrW(value, ',');
4354 if (!ptr)
4355 return FALSE;
4356 if (ptr - value == 3)
4358 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4359 day[3] = 0;
4360 st.wDayOfWeek = HTTP_ParseWkday(day);
4361 if (st.wDayOfWeek > 6)
4363 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4364 return FALSE;
4367 else if (ptr - value < sizeof(day) / sizeof(day[0]))
4369 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4370 day[ptr - value + 1] = 0;
4371 st.wDayOfWeek = HTTP_ParseWeekday(day);
4372 if (st.wDayOfWeek > 6)
4374 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4375 return FALSE;
4378 else
4380 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4381 return FALSE;
4383 ptr++;
4385 while (isspaceW(*ptr))
4386 ptr++;
4388 num = strtoulW(ptr, &nextPtr, 10);
4389 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4391 ERR("unexpected day %s\n", debugstr_w(value));
4392 return FALSE;
4394 ptr = nextPtr;
4395 st.wDay = (WORD)num;
4397 if (*ptr != '-')
4399 ERR("unexpected month format %s\n", debugstr_w(ptr));
4400 return FALSE;
4402 ptr++;
4404 for (monthPtr = month; *ptr != '-' &&
4405 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4406 monthPtr++, ptr++)
4407 *monthPtr = *ptr;
4408 *monthPtr = 0;
4409 st.wMonth = HTTP_ParseMonth(month);
4410 if (!st.wMonth || st.wMonth > 12)
4412 ERR("unexpected month %s\n", debugstr_w(month));
4413 return FALSE;
4416 if (*ptr != '-')
4418 ERR("unexpected year format %s\n", debugstr_w(ptr));
4419 return FALSE;
4421 ptr++;
4423 num = strtoulW(ptr, &nextPtr, 10);
4424 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4426 ERR("unexpected year %s\n", debugstr_w(value));
4427 return FALSE;
4429 ptr = nextPtr;
4430 st.wYear = (WORD)num;
4432 if (!HTTP_ParseTime(&st, &ptr))
4433 return FALSE;
4435 while (isspaceW(*ptr))
4436 ptr++;
4438 if (strcmpW(ptr, gmt))
4440 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4441 return FALSE;
4443 return SystemTimeToFileTime(&st, ft);
4446 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4448 static const WCHAR zero[] = { '0',0 };
4449 BOOL ret;
4451 if (!strcmpW(value, zero))
4453 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4454 ret = TRUE;
4456 else if (strchrW(value, ','))
4458 ret = HTTP_ParseRfc1123Date(value, ft);
4459 if (!ret)
4461 ret = HTTP_ParseRfc850Date(value, ft);
4462 if (!ret)
4463 ERR("unexpected date format %s\n", debugstr_w(value));
4466 else
4468 ret = HTTP_ParseDateAsAsctime(value, ft);
4469 if (!ret)
4470 ERR("unexpected date format %s\n", debugstr_w(value));
4472 return ret;
4475 static void HTTP_ProcessExpires(http_request_t *request)
4477 BOOL expirationFound = FALSE;
4478 int headerIndex;
4480 /* Look for a Cache-Control header with a max-age directive, as it takes
4481 * precedence over the Expires header.
4483 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4484 if (headerIndex != -1)
4486 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4487 LPWSTR ptr;
4489 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4491 LPWSTR comma = strchrW(ptr, ','), end, equal;
4493 if (comma)
4494 end = comma;
4495 else
4496 end = ptr + strlenW(ptr);
4497 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4499 if (*equal == '=')
4501 static const WCHAR max_age[] = {
4502 'm','a','x','-','a','g','e',0 };
4504 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4506 LPWSTR nextPtr;
4507 unsigned long age;
4509 age = strtoulW(equal + 1, &nextPtr, 10);
4510 if (nextPtr > equal + 1)
4512 LARGE_INTEGER ft;
4514 NtQuerySystemTime( &ft );
4515 /* Age is in seconds, FILETIME resolution is in
4516 * 100 nanosecond intervals.
4518 ft.QuadPart += age * (ULONGLONG)1000000;
4519 request->expires.dwLowDateTime = ft.u.LowPart;
4520 request->expires.dwHighDateTime = ft.u.HighPart;
4521 expirationFound = TRUE;
4525 if (comma)
4527 ptr = comma + 1;
4528 while (isspaceW(*ptr))
4529 ptr++;
4531 else
4532 ptr = NULL;
4535 if (!expirationFound)
4537 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4538 if (headerIndex != -1)
4540 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4541 FILETIME ft;
4543 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4545 expirationFound = TRUE;
4546 request->expires = ft;
4550 if (!expirationFound)
4552 LARGE_INTEGER t;
4554 /* With no known age, default to 10 minutes until expiration. */
4555 NtQuerySystemTime( &t );
4556 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4557 request->expires.dwLowDateTime = t.u.LowPart;
4558 request->expires.dwHighDateTime = t.u.HighPart;
4562 static void HTTP_ProcessLastModified(http_request_t *request)
4564 int headerIndex;
4566 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4567 if (headerIndex != -1)
4569 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4570 FILETIME ft;
4572 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4573 request->last_modified = ft;
4577 static void http_process_keep_alive(http_request_t *req)
4579 int index;
4581 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4582 if(index != -1)
4583 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4584 else
4585 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4588 static void HTTP_CacheRequest(http_request_t *request)
4590 WCHAR url[INTERNET_MAX_URL_LENGTH];
4591 WCHAR cacheFileName[MAX_PATH+1];
4592 BOOL b;
4594 b = HTTP_GetRequestURL(request, url);
4595 if(!b) {
4596 WARN("Could not get URL\n");
4597 return;
4600 b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4601 if(b) {
4602 heap_free(request->cacheFile);
4603 CloseHandle(request->hCacheFile);
4605 request->cacheFile = heap_strdupW(cacheFileName);
4606 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4607 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4608 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4609 WARN("Could not create file: %u\n", GetLastError());
4610 request->hCacheFile = NULL;
4612 }else {
4613 WARN("Could not create cache entry: %08x\n", GetLastError());
4617 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4619 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4620 netconn_t *netconn = NULL;
4621 DWORD res;
4623 assert(!request->netconn);
4624 reset_data_stream(request);
4626 res = HTTP_ResolveName(request);
4627 if(res != ERROR_SUCCESS)
4628 return res;
4630 EnterCriticalSection(&connection_pool_cs);
4632 while(!list_empty(&request->server->conn_pool)) {
4633 netconn = LIST_ENTRY(list_head(&request->server->conn_pool), netconn_t, pool_entry);
4634 list_remove(&netconn->pool_entry);
4636 if(NETCON_is_alive(netconn))
4637 break;
4639 TRACE("connection %p closed during idle\n", netconn);
4640 free_netconn(netconn);
4641 netconn = NULL;
4644 LeaveCriticalSection(&connection_pool_cs);
4646 if(netconn) {
4647 TRACE("<-- reusing %p netconn\n", netconn);
4648 request->netconn = netconn;
4649 *reusing = TRUE;
4650 return ERROR_SUCCESS;
4653 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4654 INTERNET_STATUS_CONNECTING_TO_SERVER,
4655 request->server->addr_str,
4656 strlen(request->server->addr_str)+1);
4658 res = create_netconn(is_https, request->server, request->security_flags,
4659 (request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0,
4660 request->connect_timeout, &netconn);
4661 if(res != ERROR_SUCCESS) {
4662 ERR("create_netconn failed: %u\n", res);
4663 return res;
4666 request->netconn = netconn;
4668 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4669 INTERNET_STATUS_CONNECTED_TO_SERVER,
4670 request->server->addr_str, strlen(request->server->addr_str)+1);
4672 if(is_https) {
4673 /* Note: we differ from Microsoft's WinINet here. they seem to have
4674 * a bug that causes no status callbacks to be sent when starting
4675 * a tunnel to a proxy server using the CONNECT verb. i believe our
4676 * behaviour to be more correct and to not cause any incompatibilities
4677 * because using a secure connection through a proxy server is a rare
4678 * case that would be hard for anyone to depend on */
4679 if(request->session->appInfo->proxy)
4680 res = HTTP_SecureProxyConnect(request);
4681 if(res == ERROR_SUCCESS)
4682 res = NETCON_secure_connect(request->netconn);
4685 if(res != ERROR_SUCCESS) {
4686 http_release_netconn(request, FALSE);
4687 return res;
4690 *reusing = FALSE;
4691 TRACE("Created connection to %s: %p\n", debugstr_w(request->server->name), netconn);
4692 return ERROR_SUCCESS;
4695 /***********************************************************************
4696 * HTTP_HttpSendRequestW (internal)
4698 * Sends the specified request to the HTTP server
4700 * RETURNS
4701 * ERROR_SUCCESS on success
4702 * win32 error code on failure
4705 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4706 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4707 DWORD dwContentLength, BOOL bEndRequest)
4709 INT cnt;
4710 BOOL redirected = FALSE;
4711 LPWSTR requestString = NULL;
4712 INT responseLen;
4713 BOOL loop_next;
4714 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4715 static const WCHAR szContentLength[] =
4716 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4717 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4718 DWORD res;
4720 TRACE("--> %p\n", request);
4722 assert(request->hdr.htype == WH_HHTTPREQ);
4724 /* if the verb is NULL default to GET */
4725 if (!request->verb)
4726 request->verb = heap_strdupW(szGET);
4728 if (dwContentLength || strcmpW(request->verb, szGET))
4730 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4731 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4732 request->bytesToWrite = dwContentLength;
4734 if (request->session->appInfo->agent)
4736 WCHAR *agent_header;
4737 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4738 int len;
4740 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4741 agent_header = heap_alloc(len * sizeof(WCHAR));
4742 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4744 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4745 heap_free(agent_header);
4747 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4749 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4750 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4752 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4754 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4755 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4756 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4759 /* add the headers the caller supplied */
4760 if( lpszHeaders && dwHeaderLength )
4761 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4765 DWORD len;
4766 BOOL reusing_connection;
4767 char *ascii_req;
4769 loop_next = FALSE;
4771 /* like native, just in case the caller forgot to call InternetReadFile
4772 * for all the data */
4773 drain_content(request);
4774 if(redirected) {
4775 request->contentLength = ~0u;
4776 request->bytesToWrite = 0;
4779 if (TRACE_ON(wininet))
4781 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4782 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4785 HTTP_FixURL(request);
4786 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4788 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4790 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4791 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4793 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4794 HTTP_InsertCookies(request);
4796 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4798 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4799 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4800 heap_free(url);
4802 else
4803 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4806 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4808 if ((res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4809 break;
4811 /* send the request as ASCII, tack on the optional data */
4812 if (!lpOptional || redirected)
4813 dwOptionalLength = 0;
4814 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4815 NULL, 0, NULL, NULL );
4816 ascii_req = heap_alloc(len + dwOptionalLength);
4817 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4818 ascii_req, len, NULL, NULL );
4819 if( lpOptional )
4820 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4821 len = (len + dwOptionalLength - 1);
4822 ascii_req[len] = 0;
4823 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4825 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4826 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4828 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4829 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4830 heap_free( ascii_req );
4831 if(res != ERROR_SUCCESS) {
4832 TRACE("send failed: %u\n", res);
4833 if(!reusing_connection)
4834 break;
4835 http_release_netconn(request, FALSE);
4836 loop_next = TRUE;
4837 continue;
4840 request->bytesWritten = dwOptionalLength;
4842 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4843 INTERNET_STATUS_REQUEST_SENT,
4844 &len, sizeof(DWORD));
4846 if (bEndRequest)
4848 DWORD dwBufferSize;
4850 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4851 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4853 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4854 /* FIXME: We should know that connection is closed before sending
4855 * headers. Otherwise wrong callbacks are executed */
4856 if(!responseLen && reusing_connection) {
4857 TRACE("Connection closed by server, reconnecting\n");
4858 http_release_netconn(request, FALSE);
4859 loop_next = TRUE;
4860 continue;
4863 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4864 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4865 sizeof(DWORD));
4867 http_process_keep_alive(request);
4868 HTTP_ProcessCookies(request);
4869 HTTP_ProcessExpires(request);
4870 HTTP_ProcessLastModified(request);
4872 res = set_content_length(request);
4873 if(res != ERROR_SUCCESS)
4874 goto lend;
4875 if(!request->contentLength)
4876 http_release_netconn(request, TRUE);
4878 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4880 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4881 dwBufferSize=sizeof(szNewLocation);
4882 switch(request->status_code) {
4883 case HTTP_STATUS_REDIRECT:
4884 case HTTP_STATUS_MOVED:
4885 case HTTP_STATUS_REDIRECT_KEEP_VERB:
4886 case HTTP_STATUS_REDIRECT_METHOD:
4887 if(HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) != ERROR_SUCCESS)
4888 break;
4890 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4891 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
4893 heap_free(request->verb);
4894 request->verb = heap_strdupW(szGET);
4896 drain_content(request);
4897 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4899 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4900 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4901 res = HTTP_HandleRedirect(request, new_url);
4902 if (res == ERROR_SUCCESS)
4904 heap_free(requestString);
4905 loop_next = TRUE;
4907 heap_free( new_url );
4909 redirected = TRUE;
4912 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4914 WCHAR szAuthValue[2048];
4915 dwBufferSize=2048;
4916 if (request->status_code == HTTP_STATUS_DENIED)
4918 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4919 DWORD dwIndex = 0;
4920 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4922 if (HTTP_DoAuthorization(request, szAuthValue,
4923 &request->authInfo,
4924 request->session->userName,
4925 request->session->password,
4926 Host->lpszValue))
4928 heap_free(requestString);
4929 loop_next = TRUE;
4930 break;
4934 if(!loop_next) {
4935 TRACE("Cleaning wrong authorization data\n");
4936 destroy_authinfo(request->authInfo);
4937 request->authInfo = NULL;
4940 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
4942 DWORD dwIndex = 0;
4943 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4945 if (HTTP_DoAuthorization(request, szAuthValue,
4946 &request->proxyAuthInfo,
4947 request->session->appInfo->proxyUsername,
4948 request->session->appInfo->proxyPassword,
4949 NULL))
4951 loop_next = TRUE;
4952 break;
4956 if(!loop_next) {
4957 TRACE("Cleaning wrong proxy authorization data\n");
4958 destroy_authinfo(request->proxyAuthInfo);
4959 request->proxyAuthInfo = NULL;
4964 else
4965 res = ERROR_SUCCESS;
4967 while (loop_next);
4969 if(res == ERROR_SUCCESS)
4970 HTTP_CacheRequest(request);
4972 lend:
4973 heap_free(requestString);
4975 /* TODO: send notification for P3P header */
4977 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4979 if (res == ERROR_SUCCESS) {
4980 if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
4981 HTTP_ReceiveRequestData(request, TRUE);
4982 else
4983 send_request_complete(request,
4984 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
4985 }else {
4986 send_request_complete(request, 0, res);
4990 TRACE("<--\n");
4991 return res;
4994 /***********************************************************************
4996 * Helper functions for the HttpSendRequest(Ex) functions
4999 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
5001 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
5002 http_request_t *request = (http_request_t*) workRequest->hdr;
5004 TRACE("%p\n", request);
5006 HTTP_HttpSendRequestW(request, req->lpszHeader,
5007 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
5008 req->dwContentLength, req->bEndRequest);
5010 heap_free(req->lpszHeader);
5014 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
5016 DWORD dwBufferSize;
5017 INT responseLen;
5018 DWORD res = ERROR_SUCCESS;
5020 if(!request->netconn) {
5021 WARN("Not connected\n");
5022 send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
5023 return ERROR_INTERNET_OPERATION_CANCELLED;
5026 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5027 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5029 responseLen = HTTP_GetResponseHeaders(request, TRUE);
5030 if (!responseLen)
5031 res = ERROR_HTTP_HEADER_NOT_FOUND;
5033 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5034 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5036 /* process cookies here. Is this right? */
5037 http_process_keep_alive(request);
5038 HTTP_ProcessCookies(request);
5039 HTTP_ProcessExpires(request);
5040 HTTP_ProcessLastModified(request);
5042 if ((res = set_content_length(request)) == ERROR_SUCCESS) {
5043 if(!request->contentLength)
5044 http_release_netconn(request, TRUE);
5047 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5049 switch(request->status_code) {
5050 case HTTP_STATUS_REDIRECT:
5051 case HTTP_STATUS_MOVED:
5052 case HTTP_STATUS_REDIRECT_METHOD:
5053 case HTTP_STATUS_REDIRECT_KEEP_VERB: {
5054 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5055 dwBufferSize=sizeof(szNewLocation);
5056 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) != ERROR_SUCCESS)
5057 break;
5059 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5060 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5062 heap_free(request->verb);
5063 request->verb = heap_strdupW(szGET);
5065 drain_content(request);
5066 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5068 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5069 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5070 res = HTTP_HandleRedirect(request, new_url);
5071 if (res == ERROR_SUCCESS)
5072 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5073 heap_free( new_url );
5079 if (res == ERROR_SUCCESS && request->contentLength)
5080 HTTP_ReceiveRequestData(request, TRUE);
5081 else
5082 send_request_complete(request, res == ERROR_SUCCESS, res);
5084 return res;
5087 /***********************************************************************
5088 * HttpEndRequestA (WININET.@)
5090 * Ends an HTTP request that was started by HttpSendRequestEx
5092 * RETURNS
5093 * TRUE if successful
5094 * FALSE on failure
5097 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5098 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5100 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5102 if (lpBuffersOut)
5104 SetLastError(ERROR_INVALID_PARAMETER);
5105 return FALSE;
5108 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5111 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
5113 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
5114 http_request_t *request = (http_request_t*)work->hdr;
5116 TRACE("%p\n", request);
5118 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
5121 /***********************************************************************
5122 * HttpEndRequestW (WININET.@)
5124 * Ends an HTTP request that was started by HttpSendRequestEx
5126 * RETURNS
5127 * TRUE if successful
5128 * FALSE on failure
5131 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5132 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5134 http_request_t *request;
5135 DWORD res;
5137 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5139 if (lpBuffersOut)
5141 SetLastError(ERROR_INVALID_PARAMETER);
5142 return FALSE;
5145 request = (http_request_t*) get_handle_object( hRequest );
5147 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5149 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5150 if (request)
5151 WININET_Release( &request->hdr );
5152 return FALSE;
5154 request->hdr.dwFlags |= dwFlags;
5156 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5158 WORKREQUEST work;
5159 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5161 work.asyncproc = AsyncHttpEndRequestProc;
5162 work.hdr = WININET_AddRef( &request->hdr );
5164 work_endrequest = &work.u.HttpEndRequestW;
5165 work_endrequest->dwFlags = dwFlags;
5166 work_endrequest->dwContext = dwContext;
5168 INTERNET_AsyncCall(&work);
5169 res = ERROR_IO_PENDING;
5171 else
5172 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5174 WININET_Release( &request->hdr );
5175 TRACE("%u <--\n", res);
5176 if(res != ERROR_SUCCESS)
5177 SetLastError(res);
5178 return res == ERROR_SUCCESS;
5181 /***********************************************************************
5182 * HttpSendRequestExA (WININET.@)
5184 * Sends the specified request to the HTTP server and allows chunked
5185 * transfers.
5187 * RETURNS
5188 * Success: TRUE
5189 * Failure: FALSE, call GetLastError() for more information.
5191 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5192 LPINTERNET_BUFFERSA lpBuffersIn,
5193 LPINTERNET_BUFFERSA lpBuffersOut,
5194 DWORD dwFlags, DWORD_PTR dwContext)
5196 INTERNET_BUFFERSW BuffersInW;
5197 BOOL rc = FALSE;
5198 DWORD headerlen;
5199 LPWSTR header = NULL;
5201 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5202 lpBuffersOut, dwFlags, dwContext);
5204 if (lpBuffersIn)
5206 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5207 if (lpBuffersIn->lpcszHeader)
5209 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5210 lpBuffersIn->dwHeadersLength,0,0);
5211 header = heap_alloc(headerlen*sizeof(WCHAR));
5212 if (!(BuffersInW.lpcszHeader = header))
5214 SetLastError(ERROR_OUTOFMEMORY);
5215 return FALSE;
5217 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5218 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5219 header, headerlen);
5221 else
5222 BuffersInW.lpcszHeader = NULL;
5223 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5224 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5225 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5226 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5227 BuffersInW.Next = NULL;
5230 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5232 heap_free(header);
5233 return rc;
5236 /***********************************************************************
5237 * HttpSendRequestExW (WININET.@)
5239 * Sends the specified request to the HTTP server and allows chunked
5240 * transfers
5242 * RETURNS
5243 * Success: TRUE
5244 * Failure: FALSE, call GetLastError() for more information.
5246 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5247 LPINTERNET_BUFFERSW lpBuffersIn,
5248 LPINTERNET_BUFFERSW lpBuffersOut,
5249 DWORD dwFlags, DWORD_PTR dwContext)
5251 http_request_t *request;
5252 http_session_t *session;
5253 appinfo_t *hIC;
5254 DWORD res;
5256 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5257 lpBuffersOut, dwFlags, dwContext);
5259 request = (http_request_t*) get_handle_object( hRequest );
5261 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5263 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5264 goto lend;
5267 session = request->session;
5268 assert(session->hdr.htype == WH_HHTTPSESSION);
5269 hIC = session->appInfo;
5270 assert(hIC->hdr.htype == WH_HINIT);
5272 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5274 WORKREQUEST workRequest;
5275 struct WORKREQ_HTTPSENDREQUESTW *req;
5277 workRequest.asyncproc = AsyncHttpSendRequestProc;
5278 workRequest.hdr = WININET_AddRef( &request->hdr );
5279 req = &workRequest.u.HttpSendRequestW;
5280 if (lpBuffersIn)
5282 DWORD size = 0;
5284 if (lpBuffersIn->lpcszHeader)
5286 if (lpBuffersIn->dwHeadersLength == ~0u)
5287 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5288 else
5289 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5291 req->lpszHeader = heap_alloc(size);
5292 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5294 else req->lpszHeader = NULL;
5296 req->dwHeaderLength = size / sizeof(WCHAR);
5297 req->lpOptional = lpBuffersIn->lpvBuffer;
5298 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5299 req->dwContentLength = lpBuffersIn->dwBufferTotal;
5301 else
5303 req->lpszHeader = NULL;
5304 req->dwHeaderLength = 0;
5305 req->lpOptional = NULL;
5306 req->dwOptionalLength = 0;
5307 req->dwContentLength = 0;
5310 req->bEndRequest = FALSE;
5312 INTERNET_AsyncCall(&workRequest);
5314 * This is from windows.
5316 res = ERROR_IO_PENDING;
5318 else
5320 if (lpBuffersIn)
5321 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5322 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5323 lpBuffersIn->dwBufferTotal, FALSE);
5324 else
5325 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5328 lend:
5329 if ( request )
5330 WININET_Release( &request->hdr );
5332 TRACE("<---\n");
5333 SetLastError(res);
5334 return res == ERROR_SUCCESS;
5337 /***********************************************************************
5338 * HttpSendRequestW (WININET.@)
5340 * Sends the specified request to the HTTP server
5342 * RETURNS
5343 * TRUE on success
5344 * FALSE on failure
5347 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5348 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5350 http_request_t *request;
5351 http_session_t *session = NULL;
5352 appinfo_t *hIC = NULL;
5353 DWORD res = ERROR_SUCCESS;
5355 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5356 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5358 request = (http_request_t*) get_handle_object( hHttpRequest );
5359 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5361 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5362 goto lend;
5365 session = request->session;
5366 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5368 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5369 goto lend;
5372 hIC = session->appInfo;
5373 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5375 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5376 goto lend;
5379 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5381 WORKREQUEST workRequest;
5382 struct WORKREQ_HTTPSENDREQUESTW *req;
5384 workRequest.asyncproc = AsyncHttpSendRequestProc;
5385 workRequest.hdr = WININET_AddRef( &request->hdr );
5386 req = &workRequest.u.HttpSendRequestW;
5387 if (lpszHeaders)
5389 DWORD size;
5391 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5392 else size = dwHeaderLength * sizeof(WCHAR);
5394 req->lpszHeader = heap_alloc(size);
5395 memcpy(req->lpszHeader, lpszHeaders, size);
5397 else
5398 req->lpszHeader = 0;
5399 req->dwHeaderLength = dwHeaderLength;
5400 req->lpOptional = lpOptional;
5401 req->dwOptionalLength = dwOptionalLength;
5402 req->dwContentLength = dwOptionalLength;
5403 req->bEndRequest = TRUE;
5405 INTERNET_AsyncCall(&workRequest);
5407 * This is from windows.
5409 res = ERROR_IO_PENDING;
5411 else
5413 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5414 dwHeaderLength, lpOptional, dwOptionalLength,
5415 dwOptionalLength, TRUE);
5417 lend:
5418 if( request )
5419 WININET_Release( &request->hdr );
5421 SetLastError(res);
5422 return res == ERROR_SUCCESS;
5425 /***********************************************************************
5426 * HttpSendRequestA (WININET.@)
5428 * Sends the specified request to the HTTP server
5430 * RETURNS
5431 * TRUE on success
5432 * FALSE on failure
5435 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5436 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5438 BOOL result;
5439 LPWSTR szHeaders=NULL;
5440 DWORD nLen=dwHeaderLength;
5441 if(lpszHeaders!=NULL)
5443 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5444 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5445 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5447 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5448 heap_free(szHeaders);
5449 return result;
5452 /***********************************************************************
5453 * HTTPSESSION_Destroy (internal)
5455 * Deallocate session handle
5458 static void HTTPSESSION_Destroy(object_header_t *hdr)
5460 http_session_t *session = (http_session_t*) hdr;
5462 TRACE("%p\n", session);
5464 WININET_Release(&session->appInfo->hdr);
5466 heap_free(session->hostName);
5467 heap_free(session->password);
5468 heap_free(session->userName);
5471 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5473 http_session_t *ses = (http_session_t *)hdr;
5475 switch(option) {
5476 case INTERNET_OPTION_HANDLE_TYPE:
5477 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5479 if (*size < sizeof(ULONG))
5480 return ERROR_INSUFFICIENT_BUFFER;
5482 *size = sizeof(DWORD);
5483 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5484 return ERROR_SUCCESS;
5485 case INTERNET_OPTION_CONNECT_TIMEOUT:
5486 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5488 if (*size < sizeof(DWORD))
5489 return ERROR_INSUFFICIENT_BUFFER;
5491 *size = sizeof(DWORD);
5492 *(DWORD *)buffer = ses->connect_timeout;
5493 return ERROR_SUCCESS;
5495 case INTERNET_OPTION_SEND_TIMEOUT:
5496 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5498 if (*size < sizeof(DWORD))
5499 return ERROR_INSUFFICIENT_BUFFER;
5501 *size = sizeof(DWORD);
5502 *(DWORD *)buffer = ses->send_timeout;
5503 return ERROR_SUCCESS;
5505 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5506 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5508 if (*size < sizeof(DWORD))
5509 return ERROR_INSUFFICIENT_BUFFER;
5511 *size = sizeof(DWORD);
5512 *(DWORD *)buffer = ses->receive_timeout;
5513 return ERROR_SUCCESS;
5516 return INET_QueryOption(hdr, option, buffer, size, unicode);
5519 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5521 http_session_t *ses = (http_session_t*)hdr;
5523 switch(option) {
5524 case INTERNET_OPTION_USERNAME:
5526 heap_free(ses->userName);
5527 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5528 return ERROR_SUCCESS;
5530 case INTERNET_OPTION_PASSWORD:
5532 heap_free(ses->password);
5533 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5534 return ERROR_SUCCESS;
5536 case INTERNET_OPTION_CONNECT_TIMEOUT:
5538 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5539 ses->connect_timeout = *(DWORD *)buffer;
5540 return ERROR_SUCCESS;
5542 case INTERNET_OPTION_SEND_TIMEOUT:
5544 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5545 ses->send_timeout = *(DWORD *)buffer;
5546 return ERROR_SUCCESS;
5548 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5550 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5551 ses->receive_timeout = *(DWORD *)buffer;
5552 return ERROR_SUCCESS;
5554 default: break;
5557 return INET_SetOption(hdr, option, buffer, size);
5560 static const object_vtbl_t HTTPSESSIONVtbl = {
5561 HTTPSESSION_Destroy,
5562 NULL,
5563 HTTPSESSION_QueryOption,
5564 HTTPSESSION_SetOption,
5565 NULL,
5566 NULL,
5567 NULL,
5568 NULL,
5569 NULL
5573 /***********************************************************************
5574 * HTTP_Connect (internal)
5576 * Create http session handle
5578 * RETURNS
5579 * HINTERNET a session handle on success
5580 * NULL on failure
5583 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5584 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5585 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5586 DWORD dwInternalFlags, HINTERNET *ret)
5588 http_session_t *session = NULL;
5590 TRACE("-->\n");
5592 if (!lpszServerName || !lpszServerName[0])
5593 return ERROR_INVALID_PARAMETER;
5595 assert( hIC->hdr.htype == WH_HINIT );
5597 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5598 if (!session)
5599 return ERROR_OUTOFMEMORY;
5602 * According to my tests. The name is not resolved until a request is sent
5605 session->hdr.htype = WH_HHTTPSESSION;
5606 session->hdr.dwFlags = dwFlags;
5607 session->hdr.dwContext = dwContext;
5608 session->hdr.dwInternalFlags |= dwInternalFlags;
5610 WININET_AddRef( &hIC->hdr );
5611 session->appInfo = hIC;
5612 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5614 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5615 if(hIC->proxyBypass)
5616 FIXME("Proxy bypass is ignored.\n");
5618 session->hostName = heap_strdupW(lpszServerName);
5619 if (lpszUserName && lpszUserName[0])
5620 session->userName = heap_strdupW(lpszUserName);
5621 if (lpszPassword && lpszPassword[0])
5622 session->password = heap_strdupW(lpszPassword);
5623 session->hostPort = serverPort;
5624 session->connect_timeout = hIC->connect_timeout;
5625 session->send_timeout = INFINITE;
5626 session->receive_timeout = INFINITE;
5628 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5629 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5631 INTERNET_SendCallback(&hIC->hdr, dwContext,
5632 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5633 sizeof(HINTERNET));
5637 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5638 * windows
5641 TRACE("%p --> %p\n", hIC, session);
5643 *ret = session->hdr.hInternet;
5644 return ERROR_SUCCESS;
5647 /***********************************************************************
5648 * HTTP_clear_response_headers (internal)
5650 * clear out any old response headers
5652 static void HTTP_clear_response_headers( http_request_t *request )
5654 DWORD i;
5656 for( i=0; i<request->nCustHeaders; i++)
5658 if( !request->custHeaders[i].lpszField )
5659 continue;
5660 if( !request->custHeaders[i].lpszValue )
5661 continue;
5662 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5663 continue;
5664 HTTP_DeleteCustomHeader( request, i );
5665 i--;
5669 /***********************************************************************
5670 * HTTP_GetResponseHeaders (internal)
5672 * Read server response
5674 * RETURNS
5676 * TRUE on success
5677 * FALSE on error
5679 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5681 INT cbreaks = 0;
5682 WCHAR buffer[MAX_REPLY_LEN];
5683 DWORD buflen = MAX_REPLY_LEN;
5684 BOOL bSuccess = FALSE;
5685 INT rc = 0;
5686 char bufferA[MAX_REPLY_LEN];
5687 LPWSTR status_code = NULL, status_text = NULL;
5688 DWORD cchMaxRawHeaders = 1024;
5689 LPWSTR lpszRawHeaders = NULL;
5690 LPWSTR temp;
5691 DWORD cchRawHeaders = 0;
5692 BOOL codeHundred = FALSE;
5694 TRACE("-->\n");
5696 if(!request->netconn)
5697 goto lend;
5699 NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5700 do {
5701 static const WCHAR szHundred[] = {'1','0','0',0};
5703 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5705 buflen = MAX_REPLY_LEN;
5706 if (!read_line(request, bufferA, &buflen))
5707 goto lend;
5709 /* clear old response headers (eg. from a redirect response) */
5710 if (clear) {
5711 HTTP_clear_response_headers( request );
5712 clear = FALSE;
5715 rc += buflen;
5716 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5717 /* check is this a status code line? */
5718 if (!strncmpW(buffer, g_szHttp1_0, 4))
5720 /* split the version from the status code */
5721 status_code = strchrW( buffer, ' ' );
5722 if( !status_code )
5723 goto lend;
5724 *status_code++=0;
5726 /* split the status code from the status text */
5727 status_text = strchrW( status_code, ' ' );
5728 if( !status_text )
5729 goto lend;
5730 *status_text++=0;
5732 request->status_code = atoiW(status_code);
5734 TRACE("version [%s] status code [%s] status text [%s]\n",
5735 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5737 codeHundred = (!strcmpW(status_code, szHundred));
5739 else if (!codeHundred)
5741 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5743 heap_free(request->version);
5744 heap_free(request->statusText);
5746 request->status_code = HTTP_STATUS_OK;
5747 request->version = heap_strdupW(g_szHttp1_0);
5748 request->statusText = heap_strdupW(szOK);
5750 heap_free(request->rawHeaders);
5751 request->rawHeaders = heap_strdupW(szDefaultHeader);
5753 bSuccess = TRUE;
5754 goto lend;
5756 } while (codeHundred);
5758 /* Add status code */
5759 HTTP_ProcessHeader(request, szStatus, status_code,
5760 HTTP_ADDHDR_FLAG_REPLACE);
5762 heap_free(request->version);
5763 heap_free(request->statusText);
5765 request->version = heap_strdupW(buffer);
5766 request->statusText = heap_strdupW(status_text);
5768 /* Restore the spaces */
5769 *(status_code-1) = ' ';
5770 *(status_text-1) = ' ';
5772 /* regenerate raw headers */
5773 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5774 if (!lpszRawHeaders) goto lend;
5776 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5777 cchMaxRawHeaders *= 2;
5778 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5779 if (temp == NULL) goto lend;
5780 lpszRawHeaders = temp;
5781 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5782 cchRawHeaders += (buflen-1);
5783 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5784 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5785 lpszRawHeaders[cchRawHeaders] = '\0';
5787 /* Parse each response line */
5790 buflen = MAX_REPLY_LEN;
5791 if (read_line(request, bufferA, &buflen))
5793 LPWSTR * pFieldAndValue;
5795 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5797 if (!bufferA[0]) break;
5798 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5800 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5801 if (pFieldAndValue)
5803 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5804 cchMaxRawHeaders *= 2;
5805 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5806 if (temp == NULL) goto lend;
5807 lpszRawHeaders = temp;
5808 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5809 cchRawHeaders += (buflen-1);
5810 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5811 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5812 lpszRawHeaders[cchRawHeaders] = '\0';
5814 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5815 HTTP_ADDREQ_FLAG_ADD );
5817 HTTP_FreeTokens(pFieldAndValue);
5820 else
5822 cbreaks++;
5823 if (cbreaks >= 2)
5824 break;
5826 }while(1);
5828 /* make sure the response header is terminated with an empty line. Some apps really
5829 truly care about that empty line being there for some reason. Just add it to the
5830 header. */
5831 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5833 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5834 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5835 if (temp == NULL) goto lend;
5836 lpszRawHeaders = temp;
5839 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5841 heap_free(request->rawHeaders);
5842 request->rawHeaders = lpszRawHeaders;
5843 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5844 bSuccess = TRUE;
5846 lend:
5848 TRACE("<--\n");
5849 if (bSuccess)
5850 return rc;
5851 else
5853 heap_free(lpszRawHeaders);
5854 return 0;
5858 /***********************************************************************
5859 * HTTP_InterpretHttpHeader (internal)
5861 * Parse server response
5863 * RETURNS
5865 * Pointer to array of field, value, NULL on success.
5866 * NULL on error.
5868 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5870 LPWSTR * pTokenPair;
5871 LPWSTR pszColon;
5872 INT len;
5874 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5876 pszColon = strchrW(buffer, ':');
5877 /* must have two tokens */
5878 if (!pszColon)
5880 HTTP_FreeTokens(pTokenPair);
5881 if (buffer[0])
5882 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5883 return NULL;
5886 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5887 if (!pTokenPair[0])
5889 HTTP_FreeTokens(pTokenPair);
5890 return NULL;
5892 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5893 pTokenPair[0][pszColon - buffer] = '\0';
5895 /* skip colon */
5896 pszColon++;
5897 len = strlenW(pszColon);
5898 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5899 if (!pTokenPair[1])
5901 HTTP_FreeTokens(pTokenPair);
5902 return NULL;
5904 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5906 strip_spaces(pTokenPair[0]);
5907 strip_spaces(pTokenPair[1]);
5909 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5910 return pTokenPair;
5913 /***********************************************************************
5914 * HTTP_ProcessHeader (internal)
5916 * Stuff header into header tables according to <dwModifier>
5920 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5922 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5924 LPHTTPHEADERW lphttpHdr = NULL;
5925 INT index = -1;
5926 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5927 DWORD res = ERROR_HTTP_INVALID_HEADER;
5929 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5931 /* REPLACE wins out over ADD */
5932 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5933 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5935 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5936 index = -1;
5937 else
5938 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5940 if (index >= 0)
5942 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5943 return ERROR_HTTP_INVALID_HEADER;
5944 lphttpHdr = &request->custHeaders[index];
5946 else if (value)
5948 HTTPHEADERW hdr;
5950 hdr.lpszField = (LPWSTR)field;
5951 hdr.lpszValue = (LPWSTR)value;
5952 hdr.wFlags = hdr.wCount = 0;
5954 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5955 hdr.wFlags |= HDR_ISREQUEST;
5957 return HTTP_InsertCustomHeader(request, &hdr);
5959 /* no value to delete */
5960 else return ERROR_SUCCESS;
5962 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5963 lphttpHdr->wFlags |= HDR_ISREQUEST;
5964 else
5965 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5967 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5969 HTTP_DeleteCustomHeader( request, index );
5971 if (value)
5973 HTTPHEADERW hdr;
5975 hdr.lpszField = (LPWSTR)field;
5976 hdr.lpszValue = (LPWSTR)value;
5977 hdr.wFlags = hdr.wCount = 0;
5979 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5980 hdr.wFlags |= HDR_ISREQUEST;
5982 return HTTP_InsertCustomHeader(request, &hdr);
5985 return ERROR_SUCCESS;
5987 else if (dwModifier & COALESCEFLAGS)
5989 LPWSTR lpsztmp;
5990 WCHAR ch = 0;
5991 INT len = 0;
5992 INT origlen = strlenW(lphttpHdr->lpszValue);
5993 INT valuelen = strlenW(value);
5995 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5997 ch = ',';
5998 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6000 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
6002 ch = ';';
6003 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6006 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
6008 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
6009 if (lpsztmp)
6011 lphttpHdr->lpszValue = lpsztmp;
6012 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
6013 if (ch > 0)
6015 lphttpHdr->lpszValue[origlen] = ch;
6016 origlen++;
6017 lphttpHdr->lpszValue[origlen] = ' ';
6018 origlen++;
6021 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
6022 lphttpHdr->lpszValue[len] = '\0';
6023 res = ERROR_SUCCESS;
6025 else
6027 WARN("heap_realloc (%d bytes) failed\n",len+1);
6028 res = ERROR_OUTOFMEMORY;
6031 TRACE("<-- %d\n", res);
6032 return res;
6035 /***********************************************************************
6036 * HTTP_GetCustomHeaderIndex (internal)
6038 * Return index of custom header from header array
6041 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
6042 int requested_index, BOOL request_only)
6044 DWORD index;
6046 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6048 for (index = 0; index < request->nCustHeaders; index++)
6050 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
6051 continue;
6053 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6054 continue;
6056 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6057 continue;
6059 if (requested_index == 0)
6060 break;
6061 requested_index --;
6064 if (index >= request->nCustHeaders)
6065 index = -1;
6067 TRACE("Return: %d\n", index);
6068 return index;
6072 /***********************************************************************
6073 * HTTP_InsertCustomHeader (internal)
6075 * Insert header into array
6078 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6080 INT count;
6081 LPHTTPHEADERW lph = NULL;
6083 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6084 count = request->nCustHeaders + 1;
6085 if (count > 1)
6086 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6087 else
6088 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6090 if (!lph)
6091 return ERROR_OUTOFMEMORY;
6093 request->custHeaders = lph;
6094 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6095 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6096 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6097 request->custHeaders[count-1].wCount= lpHdr->wCount;
6098 request->nCustHeaders++;
6100 return ERROR_SUCCESS;
6104 /***********************************************************************
6105 * HTTP_DeleteCustomHeader (internal)
6107 * Delete header from array
6108 * If this function is called, the indexs may change.
6110 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6112 if( request->nCustHeaders <= 0 )
6113 return FALSE;
6114 if( index >= request->nCustHeaders )
6115 return FALSE;
6116 request->nCustHeaders--;
6118 heap_free(request->custHeaders[index].lpszField);
6119 heap_free(request->custHeaders[index].lpszValue);
6121 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6122 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6123 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6125 return TRUE;
6129 /***********************************************************************
6130 * HTTP_VerifyValidHeader (internal)
6132 * Verify the given header is not invalid for the given http request
6135 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6137 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6138 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6139 return ERROR_HTTP_INVALID_HEADER;
6141 return ERROR_SUCCESS;
6144 /***********************************************************************
6145 * IsHostInProxyBypassList (@)
6147 * Undocumented
6150 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6152 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6153 return FALSE;
6156 /***********************************************************************
6157 * InternetShowSecurityInfoByURLA (@)
6159 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
6161 FIXME("stub: %s %p\n", url, window);
6162 return FALSE;
6165 /***********************************************************************
6166 * InternetShowSecurityInfoByURLW (@)
6168 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
6170 FIXME("stub: %s %p\n", debugstr_w(url), window);
6171 return FALSE;
6174 /***********************************************************************
6175 * ShowX509EncodedCertificate (@)
6177 DWORD WINAPI ShowX509EncodedCertificate(HWND parent, LPBYTE cert, DWORD len)
6179 PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING,
6180 cert, len);
6181 DWORD ret;
6183 if (certContext)
6185 CRYPTUI_VIEWCERTIFICATE_STRUCTW view;
6187 memset(&view, 0, sizeof(view));
6188 view.hwndParent = parent;
6189 view.pCertContext = certContext;
6190 if (CryptUIDlgViewCertificateW(&view, NULL))
6191 ret = ERROR_SUCCESS;
6192 else
6193 ret = GetLastError();
6194 CertFreeCertificateContext(certContext);
6196 else
6197 ret = GetLastError();
6198 return ret;