Sync with trunk r58740.
[reactos.git] / dll / win32 / wininet / http.c
1 /*
2 * Wininet - Http Implementation
3 *
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
11 *
12 * Ulrich Czekalla
13 * David Hammerton
14 *
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.
19 *
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.
24 *
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
28 */
29
30 #define _INC_WINDOWS
31 #define COM_NO_WINDOWS_H
32
33 #include <config.h>
34 //#include "wine/port.h"
35
36 //#include <sys/types.h>
37 #ifdef HAVE_SYS_SOCKET_H
38 # include <sys/socket.h>
39 #endif
40 #ifdef HAVE_ARPA_INET_H
41 # include <arpa/inet.h>
42 #endif
43 //#include <stdarg.h>
44 #include <stdio.h>
45 //#include <stdlib.h>
46 #ifdef HAVE_UNISTD_H
47 # include <unistd.h>
48 #endif
49 //#include <time.h>
50 #include <assert.h>
51 #ifdef HAVE_ZLIB
52 # include <zlib.h>
53 #endif
54
55 #include <windef.h>
56 #include <winbase.h>
57 #include <wininet.h>
58 //#include "winerror.h"
59 #include <winternl.h>
60 #define NO_SHLWAPI_STREAM
61 #define NO_SHLWAPI_REG
62 #define NO_SHLWAPI_STRFCNS
63 #define NO_SHLWAPI_GDI
64 #include <shlwapi.h>
65 #include <sspi.h>
66 //#include "wincrypt.h"
67 #include <winuser.h>
68 #include <cryptuiapi.h>
69
70 #if defined(__MINGW32__) || defined (_MSC_VER)
71 #include <ws2tcpip.h>
72 #endif
73
74 #include "internet.h"
75 #include <wine/debug.h>
76 #include <wine/exception.h>
77 //#include "wine/unicode.h"
78
79 // ReactOS
80 #include "inet_ntop.c"
81
82 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
83
84 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
85 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
86 static const WCHAR szOK[] = {'O','K',0};
87 static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
88 static const WCHAR hostW[] = { 'H','o','s','t',0 };
89 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
90 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
91 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
92 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
93 static const WCHAR szGET[] = { 'G','E','T', 0 };
94 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
95 static const WCHAR szCrLf[] = {'\r','\n', 0};
96
97 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
98 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
99 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
100 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
101 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
102 static const WCHAR szAge[] = { 'A','g','e',0 };
103 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
104 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
105 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
106 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
107 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
108 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
109 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
110 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
111 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
112 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
113 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
114 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 };
115 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
116 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
117 static const WCHAR szDate[] = { 'D','a','t','e',0 };
118 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
119 static const WCHAR szETag[] = { 'E','T','a','g',0 };
120 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
121 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
122 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
123 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
124 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
125 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
126 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
127 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
128 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
129 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
130 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
131 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
132 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
133 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
134 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
135 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
136 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
137 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
138 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
139 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
140 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
141 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 };
142 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
143 static const WCHAR szURI[] = { 'U','R','I',0 };
144 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
145 static const WCHAR szVary[] = { 'V','a','r','y',0 };
146 static const WCHAR szVia[] = { 'V','i','a',0 };
147 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
148 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
149
150 #define HTTP_REFERER szReferer
151 #define HTTP_ACCEPT szAccept
152 #define HTTP_USERAGENT szUser_Agent
153
154 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
155 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
156 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
157 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
158 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
159 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
160 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
161
162 #define COLLECT_TIME 60000
163
164 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
165
166 struct HttpAuthInfo
167 {
168 LPWSTR scheme;
169 CredHandle cred;
170 CtxtHandle ctx;
171 TimeStamp exp;
172 ULONG attr;
173 ULONG max_token;
174 void *auth_data;
175 unsigned int auth_data_len;
176 BOOL finished; /* finished authenticating */
177 };
178
179
180 typedef struct _basicAuthorizationData
181 {
182 struct list entry;
183
184 LPWSTR host;
185 LPWSTR realm;
186 LPSTR authorization;
187 UINT authorizationLen;
188 } basicAuthorizationData;
189
190 typedef struct _authorizationData
191 {
192 struct list entry;
193
194 LPWSTR host;
195 LPWSTR scheme;
196 LPWSTR domain;
197 UINT domain_len;
198 LPWSTR user;
199 UINT user_len;
200 LPWSTR password;
201 UINT password_len;
202 } authorizationData;
203
204 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
205 static struct list authorizationCache = LIST_INIT(authorizationCache);
206
207 static CRITICAL_SECTION authcache_cs;
208 static CRITICAL_SECTION_DEBUG critsect_debug =
209 {
210 0, 0, &authcache_cs,
211 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
212 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
213 };
214 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
215
216 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
217 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
218 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
219 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
220 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
221 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
222 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
223 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
224 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
225 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
226 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
227
228 static CRITICAL_SECTION connection_pool_cs;
229 static CRITICAL_SECTION_DEBUG connection_pool_debug =
230 {
231 0, 0, &connection_pool_cs,
232 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
233 0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
234 };
235 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
236
237 static struct list connection_pool = LIST_INIT(connection_pool);
238 static BOOL collector_running;
239
240 void server_addref(server_t *server)
241 {
242 InterlockedIncrement(&server->ref);
243 }
244
245 void server_release(server_t *server)
246 {
247 if(InterlockedDecrement(&server->ref))
248 return;
249
250 #ifndef __REACTOS__
251 if(!server->ref)
252 server->keep_until = (DWORD64)GetTickCount() + COLLECT_TIME;
253 #else
254 EnterCriticalSection(&connection_pool_cs);
255 list_remove(&server->entry);
256 LeaveCriticalSection(&connection_pool_cs);
257
258 heap_free(server->name);
259 heap_free(server);
260 #endif
261 }
262
263 static server_t *get_server(const WCHAR *name, INTERNET_PORT port)
264 {
265 server_t *iter, *server = NULL;
266
267 EnterCriticalSection(&connection_pool_cs);
268
269 LIST_FOR_EACH_ENTRY(iter, &connection_pool, server_t, entry) {
270 if(iter->port == port && !strcmpW(iter->name, name)) {
271 server = iter;
272 server_addref(server);
273 break;
274 }
275 }
276
277 if(!server) {
278 server = heap_alloc(sizeof(*server));
279 if(server) {
280 server->addr_len = 0;
281 server->ref = 1;
282 server->port = port;
283 list_init(&server->conn_pool);
284 server->name = heap_strdupW(name);
285 if(server->name) {
286 list_add_head(&connection_pool, &server->entry);
287 }else {
288 heap_free(server);
289 server = NULL;
290 }
291 }
292 }
293
294 LeaveCriticalSection(&connection_pool_cs);
295
296 return server;
297 }
298
299 BOOL collect_connections(BOOL collect_all)
300 {
301 netconn_t *netconn, *netconn_safe;
302 server_t *server, *server_safe;
303 BOOL remaining = FALSE;
304 DWORD64 now;
305
306 now = GetTickCount();
307
308 LIST_FOR_EACH_ENTRY_SAFE(server, server_safe, &connection_pool, server_t, entry) {
309 LIST_FOR_EACH_ENTRY_SAFE(netconn, netconn_safe, &server->conn_pool, netconn_t, pool_entry) {
310 if(collect_all || netconn->keep_until < now) {
311 TRACE("freeing %p\n", netconn);
312 list_remove(&netconn->pool_entry);
313 free_netconn(netconn);
314 }else {
315 remaining = TRUE;
316 }
317 }
318
319 if(!server->ref) {
320 if(collect_all || server->keep_until < now) {
321 list_remove(&server->entry);
322
323 heap_free(server->name);
324 heap_free(server);
325 }else {
326 remaining = TRUE;
327 }
328 }
329 }
330
331 return remaining;
332 }
333
334 static DWORD WINAPI collect_connections_proc(void *arg)
335 {
336 BOOL remaining_conns;
337
338 do {
339 /* FIXME: Use more sophisticated method */
340 Sleep(5000);
341
342 EnterCriticalSection(&connection_pool_cs);
343
344 remaining_conns = collect_connections(FALSE);
345 if(!remaining_conns)
346 collector_running = FALSE;
347
348 LeaveCriticalSection(&connection_pool_cs);
349 }while(remaining_conns);
350
351 FreeLibraryAndExitThread(WININET_hModule, 0);
352 }
353
354 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
355 {
356 int HeaderIndex = 0;
357 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
358 if (HeaderIndex == -1)
359 return NULL;
360 else
361 return &req->custHeaders[HeaderIndex];
362 }
363
364 typedef enum {
365 READMODE_SYNC,
366 READMODE_ASYNC,
367 READMODE_NOBLOCK
368 } read_mode_t;
369
370 struct data_stream_vtbl_t {
371 DWORD (*get_avail_data)(data_stream_t*,http_request_t*);
372 BOOL (*end_of_data)(data_stream_t*,http_request_t*);
373 DWORD (*read)(data_stream_t*,http_request_t*,BYTE*,DWORD,DWORD*,read_mode_t);
374 BOOL (*drain_content)(data_stream_t*,http_request_t*);
375 void (*destroy)(data_stream_t*);
376 };
377
378 typedef struct {
379 data_stream_t data_stream;
380
381 BYTE buf[READ_BUFFER_SIZE];
382 DWORD buf_size;
383 DWORD buf_pos;
384 DWORD chunk_size;
385 } chunked_stream_t;
386
387 static inline void destroy_data_stream(data_stream_t *stream)
388 {
389 stream->vtbl->destroy(stream);
390 }
391
392 static void reset_data_stream(http_request_t *req)
393 {
394 destroy_data_stream(req->data_stream);
395 req->data_stream = &req->netconn_stream.data_stream;
396 req->read_pos = req->read_size = req->netconn_stream.content_read = 0;
397 req->read_chunked = req->read_gzip = FALSE;
398 }
399
400 #ifdef HAVE_ZLIB
401
402 typedef struct {
403 data_stream_t stream;
404 data_stream_t *parent_stream;
405 z_stream zstream;
406 BYTE buf[READ_BUFFER_SIZE];
407 DWORD buf_size;
408 DWORD buf_pos;
409 BOOL end_of_data;
410 } gzip_stream_t;
411
412 static DWORD gzip_get_avail_data(data_stream_t *stream, http_request_t *req)
413 {
414 /* Allow reading only from read buffer */
415 return 0;
416 }
417
418 static BOOL gzip_end_of_data(data_stream_t *stream, http_request_t *req)
419 {
420 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
421 return gzip_stream->end_of_data;
422 }
423
424 static DWORD gzip_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
425 DWORD *read, read_mode_t read_mode)
426 {
427 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
428 z_stream *zstream = &gzip_stream->zstream;
429 DWORD current_read, ret_read = 0;
430 BOOL end;
431 int zres;
432 DWORD res = ERROR_SUCCESS;
433
434 while(size && !gzip_stream->end_of_data) {
435 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
436
437 if(gzip_stream->buf_size <= 64 && !end) {
438 if(gzip_stream->buf_pos) {
439 if(gzip_stream->buf_size)
440 memmove(gzip_stream->buf, gzip_stream->buf+gzip_stream->buf_pos, gzip_stream->buf_size);
441 gzip_stream->buf_pos = 0;
442 }
443 res = gzip_stream->parent_stream->vtbl->read(gzip_stream->parent_stream, req, gzip_stream->buf+gzip_stream->buf_size,
444 sizeof(gzip_stream->buf)-gzip_stream->buf_size, &current_read, read_mode);
445 gzip_stream->buf_size += current_read;
446 if(res != ERROR_SUCCESS)
447 break;
448 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
449 if(!current_read && !end) {
450 if(read_mode != READMODE_NOBLOCK) {
451 WARN("unexpected end of data\n");
452 gzip_stream->end_of_data = TRUE;
453 }
454 break;
455 }
456 if(gzip_stream->buf_size <= 64 && !end)
457 continue;
458 }
459
460 zstream->next_in = gzip_stream->buf+gzip_stream->buf_pos;
461 zstream->avail_in = gzip_stream->buf_size-(end ? 0 : 64);
462 zstream->next_out = buf+ret_read;
463 zstream->avail_out = size;
464 zres = inflate(&gzip_stream->zstream, 0);
465 current_read = size - zstream->avail_out;
466 size -= current_read;
467 ret_read += current_read;
468 gzip_stream->buf_size -= zstream->next_in - (gzip_stream->buf+gzip_stream->buf_pos);
469 gzip_stream->buf_pos = zstream->next_in-gzip_stream->buf;
470 if(zres == Z_STREAM_END) {
471 TRACE("end of data\n");
472 gzip_stream->end_of_data = TRUE;
473 inflateEnd(zstream);
474 }else if(zres != Z_OK) {
475 WARN("inflate failed %d: %s\n", zres, debugstr_a(zstream->msg));
476 if(!ret_read)
477 res = ERROR_INTERNET_DECODING_FAILED;
478 break;
479 }
480
481 if(ret_read && read_mode == READMODE_ASYNC)
482 read_mode = READMODE_NOBLOCK;
483 }
484
485 TRACE("read %u bytes\n", ret_read);
486 *read = ret_read;
487 return res;
488 }
489
490 static BOOL gzip_drain_content(data_stream_t *stream, http_request_t *req)
491 {
492 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
493 return gzip_stream->parent_stream->vtbl->drain_content(gzip_stream->parent_stream, req);
494 }
495
496 static void gzip_destroy(data_stream_t *stream)
497 {
498 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
499
500 destroy_data_stream(gzip_stream->parent_stream);
501
502 if(!gzip_stream->end_of_data)
503 inflateEnd(&gzip_stream->zstream);
504 heap_free(gzip_stream);
505 }
506
507 static const data_stream_vtbl_t gzip_stream_vtbl = {
508 gzip_get_avail_data,
509 gzip_end_of_data,
510 gzip_read,
511 gzip_drain_content,
512 gzip_destroy
513 };
514
515 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
516 {
517 return heap_alloc(items*size);
518 }
519
520 static void wininet_zfree(voidpf opaque, voidpf address)
521 {
522 heap_free(address);
523 }
524
525 static DWORD init_gzip_stream(http_request_t *req)
526 {
527 gzip_stream_t *gzip_stream;
528 int index, zres;
529
530 gzip_stream = heap_alloc_zero(sizeof(gzip_stream_t));
531 if(!gzip_stream)
532 return ERROR_OUTOFMEMORY;
533
534 gzip_stream->stream.vtbl = &gzip_stream_vtbl;
535 gzip_stream->zstream.zalloc = wininet_zalloc;
536 gzip_stream->zstream.zfree = wininet_zfree;
537
538 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
539 if(zres != Z_OK) {
540 ERR("inflateInit failed: %d\n", zres);
541 heap_free(gzip_stream);
542 return ERROR_OUTOFMEMORY;
543 }
544
545 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
546 if(index != -1)
547 HTTP_DeleteCustomHeader(req, index);
548
549 if(req->read_size) {
550 memcpy(gzip_stream->buf, req->read_buf+req->read_pos, req->read_size);
551 gzip_stream->buf_size = req->read_size;
552 req->read_pos = req->read_size = 0;
553 }
554
555 req->read_gzip = TRUE;
556 gzip_stream->parent_stream = req->data_stream;
557 req->data_stream = &gzip_stream->stream;
558 return ERROR_SUCCESS;
559 }
560
561 #else
562
563 static DWORD init_gzip_stream(http_request_t *req)
564 {
565 ERR("gzip stream not supported, missing zlib.\n");
566 return ERROR_SUCCESS;
567 }
568
569 #endif
570
571 /***********************************************************************
572 * HTTP_Tokenize (internal)
573 *
574 * Tokenize a string, allocating memory for the tokens.
575 */
576 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
577 {
578 LPWSTR * token_array;
579 int tokens = 0;
580 int i;
581 LPCWSTR next_token;
582
583 if (string)
584 {
585 /* empty string has no tokens */
586 if (*string)
587 tokens++;
588 /* count tokens */
589 for (i = 0; string[i]; i++)
590 {
591 if (!strncmpW(string+i, token_string, strlenW(token_string)))
592 {
593 DWORD j;
594 tokens++;
595 /* we want to skip over separators, but not the null terminator */
596 for (j = 0; j < strlenW(token_string) - 1; j++)
597 if (!string[i+j])
598 break;
599 i += j;
600 }
601 }
602 }
603
604 /* add 1 for terminating NULL */
605 token_array = heap_alloc((tokens+1) * sizeof(*token_array));
606 token_array[tokens] = NULL;
607 if (!tokens)
608 return token_array;
609 for (i = 0; i < tokens; i++)
610 {
611 int len;
612 next_token = strstrW(string, token_string);
613 if (!next_token) next_token = string+strlenW(string);
614 len = next_token - string;
615 token_array[i] = heap_alloc((len+1)*sizeof(WCHAR));
616 memcpy(token_array[i], string, len*sizeof(WCHAR));
617 token_array[i][len] = '\0';
618 string = next_token+strlenW(token_string);
619 }
620 return token_array;
621 }
622
623 /***********************************************************************
624 * HTTP_FreeTokens (internal)
625 *
626 * Frees memory returned from HTTP_Tokenize.
627 */
628 static void HTTP_FreeTokens(LPWSTR * token_array)
629 {
630 int i;
631 for (i = 0; token_array[i]; i++) heap_free(token_array[i]);
632 heap_free(token_array);
633 }
634
635 static void HTTP_FixURL(http_request_t *request)
636 {
637 static const WCHAR szSlash[] = { '/',0 };
638 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
639
640 /* If we don't have a path we set it to root */
641 if (NULL == request->path)
642 request->path = heap_strdupW(szSlash);
643 else /* remove \r and \n*/
644 {
645 int nLen = strlenW(request->path);
646 while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n')))
647 {
648 nLen--;
649 request->path[nLen]='\0';
650 }
651 /* Replace '\' with '/' */
652 while (nLen>0) {
653 nLen--;
654 if (request->path[nLen] == '\\') request->path[nLen]='/';
655 }
656 }
657
658 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
659 request->path, strlenW(request->path), szHttp, strlenW(szHttp) )
660 && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */
661 {
662 WCHAR *fixurl = heap_alloc((strlenW(request->path) + 2)*sizeof(WCHAR));
663 *fixurl = '/';
664 strcpyW(fixurl + 1, request->path);
665 heap_free( request->path );
666 request->path = fixurl;
667 }
668 }
669
670 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *request, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
671 {
672 LPWSTR requestString;
673 DWORD len, n;
674 LPCWSTR *req;
675 UINT i;
676 LPWSTR p;
677
678 static const WCHAR szSpace[] = { ' ',0 };
679 static const WCHAR szColon[] = { ':',' ',0 };
680 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
681
682 /* allocate space for an array of all the string pointers to be added */
683 len = (request->nCustHeaders)*4 + 10;
684 req = heap_alloc(len*sizeof(LPCWSTR));
685
686 /* add the verb, path and HTTP version string */
687 n = 0;
688 req[n++] = verb;
689 req[n++] = szSpace;
690 req[n++] = path;
691 req[n++] = szSpace;
692 req[n++] = version;
693
694 /* Append custom request headers */
695 for (i = 0; i < request->nCustHeaders; i++)
696 {
697 if (request->custHeaders[i].wFlags & HDR_ISREQUEST)
698 {
699 req[n++] = szCrLf;
700 req[n++] = request->custHeaders[i].lpszField;
701 req[n++] = szColon;
702 req[n++] = request->custHeaders[i].lpszValue;
703
704 TRACE("Adding custom header %s (%s)\n",
705 debugstr_w(request->custHeaders[i].lpszField),
706 debugstr_w(request->custHeaders[i].lpszValue));
707 }
708 }
709
710 if( n >= len )
711 ERR("oops. buffer overrun\n");
712
713 req[n] = NULL;
714 requestString = HTTP_build_req( req, 4 );
715 heap_free( req );
716
717 /*
718 * Set (header) termination string for request
719 * Make sure there's exactly two new lines at the end of the request
720 */
721 p = &requestString[strlenW(requestString)-1];
722 while ( (*p == '\n') || (*p == '\r') )
723 p--;
724 strcpyW( p+1, sztwocrlf );
725
726 return requestString;
727 }
728
729 static void HTTP_ProcessCookies( http_request_t *request )
730 {
731 int HeaderIndex;
732 int numCookies = 0;
733 LPHTTPHEADERW setCookieHeader;
734
735 if(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES)
736 return;
737
738 while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, szSet_Cookie, numCookies++, FALSE)) != -1)
739 {
740 HTTPHEADERW *host;
741 const WCHAR *data;
742 WCHAR *name;
743
744 setCookieHeader = &request->custHeaders[HeaderIndex];
745
746 if (!setCookieHeader->lpszValue)
747 continue;
748
749 host = HTTP_GetHeader(request, hostW);
750 if(!host)
751 continue;
752
753 data = strchrW(setCookieHeader->lpszValue, '=');
754 if(!data)
755 continue;
756
757 name = heap_strndupW(setCookieHeader->lpszValue, data-setCookieHeader->lpszValue);
758 if(!name)
759 continue;
760
761 data++;
762 set_cookie(host->lpszValue, request->path, name, data);
763 heap_free(name);
764 }
765 }
766
767 static void strip_spaces(LPWSTR start)
768 {
769 LPWSTR str = start;
770 LPWSTR end;
771
772 while (*str == ' ' && *str != '\0')
773 str++;
774
775 if (str != start)
776 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
777
778 end = start + strlenW(start) - 1;
779 while (end >= start && *end == ' ')
780 {
781 *end = '\0';
782 end--;
783 }
784 }
785
786 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
787 {
788 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
789 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
790 BOOL is_basic;
791 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
792 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
793 if (is_basic && pszRealm)
794 {
795 LPCWSTR token;
796 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
797 LPCWSTR realm;
798 ptr++;
799 *pszRealm=NULL;
800 token = strchrW(ptr,'=');
801 if (!token)
802 return TRUE;
803 realm = ptr;
804 while (*realm == ' ' && *realm != '\0')
805 realm++;
806 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
807 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
808 {
809 token++;
810 while (*token == ' ' && *token != '\0')
811 token++;
812 if (*token == '\0')
813 return TRUE;
814 *pszRealm = heap_strdupW(token);
815 strip_spaces(*pszRealm);
816 }
817 }
818
819 return is_basic;
820 }
821
822 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
823 {
824 if (!authinfo) return;
825
826 if (SecIsValidHandle(&authinfo->ctx))
827 DeleteSecurityContext(&authinfo->ctx);
828 if (SecIsValidHandle(&authinfo->cred))
829 FreeCredentialsHandle(&authinfo->cred);
830
831 heap_free(authinfo->auth_data);
832 heap_free(authinfo->scheme);
833 heap_free(authinfo);
834 }
835
836 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
837 {
838 basicAuthorizationData *ad;
839 UINT rc = 0;
840
841 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
842
843 EnterCriticalSection(&authcache_cs);
844 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
845 {
846 if (!strcmpiW(host,ad->host) && !strcmpW(realm,ad->realm))
847 {
848 TRACE("Authorization found in cache\n");
849 *auth_data = heap_alloc(ad->authorizationLen);
850 memcpy(*auth_data,ad->authorization,ad->authorizationLen);
851 rc = ad->authorizationLen;
852 break;
853 }
854 }
855 LeaveCriticalSection(&authcache_cs);
856 return rc;
857 }
858
859 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
860 {
861 struct list *cursor;
862 basicAuthorizationData* ad = NULL;
863
864 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
865
866 EnterCriticalSection(&authcache_cs);
867 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
868 {
869 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
870 if (!strcmpiW(host,check->host) && !strcmpW(realm,check->realm))
871 {
872 ad = check;
873 break;
874 }
875 }
876
877 if (ad)
878 {
879 TRACE("Found match in cache, replacing\n");
880 heap_free(ad->authorization);
881 ad->authorization = heap_alloc(auth_data_len);
882 memcpy(ad->authorization, auth_data, auth_data_len);
883 ad->authorizationLen = auth_data_len;
884 }
885 else
886 {
887 ad = heap_alloc(sizeof(basicAuthorizationData));
888 ad->host = heap_strdupW(host);
889 ad->realm = heap_strdupW(realm);
890 ad->authorization = heap_alloc(auth_data_len);
891 memcpy(ad->authorization, auth_data, auth_data_len);
892 ad->authorizationLen = auth_data_len;
893 list_add_head(&basicAuthorizationCache,&ad->entry);
894 TRACE("authorization cached\n");
895 }
896 LeaveCriticalSection(&authcache_cs);
897 }
898
899 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
900 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
901 {
902 authorizationData *ad;
903
904 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
905
906 EnterCriticalSection(&authcache_cs);
907 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
908 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
909 TRACE("Authorization found in cache\n");
910
911 nt_auth_identity->User = heap_strdupW(ad->user);
912 nt_auth_identity->Password = heap_strdupW(ad->password);
913 nt_auth_identity->Domain = heap_alloc(sizeof(WCHAR)*ad->domain_len);
914 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
915 (!nt_auth_identity->Domain && ad->domain_len)) {
916 heap_free(nt_auth_identity->User);
917 heap_free(nt_auth_identity->Password);
918 heap_free(nt_auth_identity->Domain);
919 break;
920 }
921
922 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
923 nt_auth_identity->UserLength = ad->user_len;
924 nt_auth_identity->PasswordLength = ad->password_len;
925 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
926 nt_auth_identity->DomainLength = ad->domain_len;
927 LeaveCriticalSection(&authcache_cs);
928 return TRUE;
929 }
930 }
931 LeaveCriticalSection(&authcache_cs);
932
933 return FALSE;
934 }
935
936 static void cache_authorization(LPWSTR host, LPWSTR scheme,
937 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
938 {
939 authorizationData *ad;
940 BOOL found = FALSE;
941
942 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
943
944 EnterCriticalSection(&authcache_cs);
945 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
946 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
947 found = TRUE;
948 break;
949 }
950
951 if(found) {
952 heap_free(ad->user);
953 heap_free(ad->password);
954 heap_free(ad->domain);
955 } else {
956 ad = heap_alloc(sizeof(authorizationData));
957 if(!ad) {
958 LeaveCriticalSection(&authcache_cs);
959 return;
960 }
961
962 ad->host = heap_strdupW(host);
963 ad->scheme = heap_strdupW(scheme);
964 list_add_head(&authorizationCache, &ad->entry);
965 }
966
967 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
968 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
969 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
970 ad->user_len = nt_auth_identity->UserLength;
971 ad->password_len = nt_auth_identity->PasswordLength;
972 ad->domain_len = nt_auth_identity->DomainLength;
973
974 if(!ad->host || !ad->scheme || !ad->user || !ad->password
975 || (nt_auth_identity->Domain && !ad->domain)) {
976 heap_free(ad->host);
977 heap_free(ad->scheme);
978 heap_free(ad->user);
979 heap_free(ad->password);
980 heap_free(ad->domain);
981 list_remove(&ad->entry);
982 heap_free(ad);
983 }
984
985 LeaveCriticalSection(&authcache_cs);
986 }
987
988 static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue,
989 struct HttpAuthInfo **ppAuthInfo,
990 LPWSTR domain_and_username, LPWSTR password,
991 LPWSTR host )
992 {
993 SECURITY_STATUS sec_status;
994 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
995 BOOL first = FALSE;
996 LPWSTR szRealm = NULL;
997
998 TRACE("%s\n", debugstr_w(pszAuthValue));
999
1000 if (!pAuthInfo)
1001 {
1002 TimeStamp exp;
1003
1004 first = TRUE;
1005 pAuthInfo = heap_alloc(sizeof(*pAuthInfo));
1006 if (!pAuthInfo)
1007 return FALSE;
1008
1009 SecInvalidateHandle(&pAuthInfo->cred);
1010 SecInvalidateHandle(&pAuthInfo->ctx);
1011 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
1012 pAuthInfo->attr = 0;
1013 pAuthInfo->auth_data = NULL;
1014 pAuthInfo->auth_data_len = 0;
1015 pAuthInfo->finished = FALSE;
1016
1017 if (is_basic_auth_value(pszAuthValue,NULL))
1018 {
1019 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
1020 pAuthInfo->scheme = heap_strdupW(szBasic);
1021 if (!pAuthInfo->scheme)
1022 {
1023 heap_free(pAuthInfo);
1024 return FALSE;
1025 }
1026 }
1027 else
1028 {
1029 PVOID pAuthData;
1030 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
1031
1032 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
1033 if (!pAuthInfo->scheme)
1034 {
1035 heap_free(pAuthInfo);
1036 return FALSE;
1037 }
1038
1039 if (domain_and_username)
1040 {
1041 WCHAR *user = strchrW(domain_and_username, '\\');
1042 WCHAR *domain = domain_and_username;
1043
1044 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1045
1046 pAuthData = &nt_auth_identity;
1047
1048 if (user) user++;
1049 else
1050 {
1051 user = domain_and_username;
1052 domain = NULL;
1053 }
1054
1055 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1056 nt_auth_identity.User = user;
1057 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
1058 nt_auth_identity.Domain = domain;
1059 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
1060 nt_auth_identity.Password = password;
1061 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
1062
1063 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
1064 }
1065 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
1066 pAuthData = &nt_auth_identity;
1067 else
1068 /* use default credentials */
1069 pAuthData = NULL;
1070
1071 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
1072 SECPKG_CRED_OUTBOUND, NULL,
1073 pAuthData, NULL,
1074 NULL, &pAuthInfo->cred,
1075 &exp);
1076
1077 if(pAuthData && !domain_and_username) {
1078 heap_free(nt_auth_identity.User);
1079 heap_free(nt_auth_identity.Domain);
1080 heap_free(nt_auth_identity.Password);
1081 }
1082
1083 if (sec_status == SEC_E_OK)
1084 {
1085 PSecPkgInfoW sec_pkg_info;
1086 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
1087 if (sec_status == SEC_E_OK)
1088 {
1089 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
1090 FreeContextBuffer(sec_pkg_info);
1091 }
1092 }
1093 if (sec_status != SEC_E_OK)
1094 {
1095 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1096 debugstr_w(pAuthInfo->scheme), sec_status);
1097 heap_free(pAuthInfo->scheme);
1098 heap_free(pAuthInfo);
1099 return FALSE;
1100 }
1101 }
1102 *ppAuthInfo = pAuthInfo;
1103 }
1104 else if (pAuthInfo->finished)
1105 return FALSE;
1106
1107 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
1108 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
1109 {
1110 ERR("authentication scheme changed from %s to %s\n",
1111 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
1112 return FALSE;
1113 }
1114
1115 if (is_basic_auth_value(pszAuthValue,&szRealm))
1116 {
1117 int userlen;
1118 int passlen;
1119 char *auth_data = NULL;
1120 UINT auth_data_len = 0;
1121
1122 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
1123
1124 if (!domain_and_username)
1125 {
1126 if (host && szRealm)
1127 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
1128 if (auth_data_len == 0)
1129 {
1130 heap_free(szRealm);
1131 return FALSE;
1132 }
1133 }
1134 else
1135 {
1136 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
1137 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
1138
1139 /* length includes a nul terminator, which will be re-used for the ':' */
1140 auth_data = heap_alloc(userlen + 1 + passlen);
1141 if (!auth_data)
1142 {
1143 heap_free(szRealm);
1144 return FALSE;
1145 }
1146
1147 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
1148 auth_data[userlen] = ':';
1149 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
1150 auth_data_len = userlen + 1 + passlen;
1151 if (host && szRealm)
1152 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
1153 }
1154
1155 pAuthInfo->auth_data = auth_data;
1156 pAuthInfo->auth_data_len = auth_data_len;
1157 pAuthInfo->finished = TRUE;
1158 heap_free(szRealm);
1159 return TRUE;
1160 }
1161 else
1162 {
1163 LPCWSTR pszAuthData;
1164 SecBufferDesc out_desc, in_desc;
1165 SecBuffer out, in;
1166 unsigned char *buffer;
1167 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
1168 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
1169
1170 in.BufferType = SECBUFFER_TOKEN;
1171 in.cbBuffer = 0;
1172 in.pvBuffer = NULL;
1173
1174 in_desc.ulVersion = 0;
1175 in_desc.cBuffers = 1;
1176 in_desc.pBuffers = &in;
1177
1178 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
1179 if (*pszAuthData == ' ')
1180 {
1181 pszAuthData++;
1182 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
1183 in.pvBuffer = heap_alloc(in.cbBuffer);
1184 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
1185 }
1186
1187 buffer = heap_alloc(pAuthInfo->max_token);
1188
1189 out.BufferType = SECBUFFER_TOKEN;
1190 out.cbBuffer = pAuthInfo->max_token;
1191 out.pvBuffer = buffer;
1192
1193 out_desc.ulVersion = 0;
1194 out_desc.cBuffers = 1;
1195 out_desc.pBuffers = &out;
1196
1197 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
1198 first ? NULL : &pAuthInfo->ctx,
1199 first ? request->session->serverName : NULL,
1200 context_req, 0, SECURITY_NETWORK_DREP,
1201 in.pvBuffer ? &in_desc : NULL,
1202 0, &pAuthInfo->ctx, &out_desc,
1203 &pAuthInfo->attr, &pAuthInfo->exp);
1204 if (sec_status == SEC_E_OK)
1205 {
1206 pAuthInfo->finished = TRUE;
1207 pAuthInfo->auth_data = out.pvBuffer;
1208 pAuthInfo->auth_data_len = out.cbBuffer;
1209 TRACE("sending last auth packet\n");
1210 }
1211 else if (sec_status == SEC_I_CONTINUE_NEEDED)
1212 {
1213 pAuthInfo->auth_data = out.pvBuffer;
1214 pAuthInfo->auth_data_len = out.cbBuffer;
1215 TRACE("sending next auth packet\n");
1216 }
1217 else
1218 {
1219 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
1220 heap_free(out.pvBuffer);
1221 destroy_authinfo(pAuthInfo);
1222 *ppAuthInfo = NULL;
1223 return FALSE;
1224 }
1225 }
1226
1227 return TRUE;
1228 }
1229
1230 /***********************************************************************
1231 * HTTP_HttpAddRequestHeadersW (internal)
1232 */
1233 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *request,
1234 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1235 {
1236 LPWSTR lpszStart;
1237 LPWSTR lpszEnd;
1238 LPWSTR buffer;
1239 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
1240
1241 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
1242
1243 if( dwHeaderLength == ~0U )
1244 len = strlenW(lpszHeader);
1245 else
1246 len = dwHeaderLength;
1247 buffer = heap_alloc(sizeof(WCHAR)*(len+1));
1248 lstrcpynW( buffer, lpszHeader, len + 1);
1249
1250 lpszStart = buffer;
1251
1252 do
1253 {
1254 LPWSTR * pFieldAndValue;
1255
1256 lpszEnd = lpszStart;
1257
1258 while (*lpszEnd != '\0')
1259 {
1260 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1261 break;
1262 lpszEnd++;
1263 }
1264
1265 if (*lpszStart == '\0')
1266 break;
1267
1268 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1269 {
1270 *lpszEnd = '\0';
1271 lpszEnd++; /* Jump over newline */
1272 }
1273 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1274 if (*lpszStart == '\0')
1275 {
1276 /* Skip 0-length headers */
1277 lpszStart = lpszEnd;
1278 res = ERROR_SUCCESS;
1279 continue;
1280 }
1281 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1282 if (pFieldAndValue)
1283 {
1284 res = HTTP_VerifyValidHeader(request, pFieldAndValue[0]);
1285 if (res == ERROR_SUCCESS)
1286 res = HTTP_ProcessHeader(request, pFieldAndValue[0],
1287 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1288 HTTP_FreeTokens(pFieldAndValue);
1289 }
1290
1291 lpszStart = lpszEnd;
1292 } while (res == ERROR_SUCCESS);
1293
1294 heap_free(buffer);
1295 return res;
1296 }
1297
1298 /***********************************************************************
1299 * HttpAddRequestHeadersW (WININET.@)
1300 *
1301 * Adds one or more HTTP header to the request handler
1302 *
1303 * NOTE
1304 * On Windows if dwHeaderLength includes the trailing '\0', then
1305 * HttpAddRequestHeadersW() adds it too. However this results in an
1306 * invalid Http header which is rejected by some servers so we probably
1307 * don't need to match Windows on that point.
1308 *
1309 * RETURNS
1310 * TRUE on success
1311 * FALSE on failure
1312 *
1313 */
1314 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1315 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1316 {
1317 http_request_t *request;
1318 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1319
1320 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1321
1322 if (!lpszHeader)
1323 return TRUE;
1324
1325 request = (http_request_t*) get_handle_object( hHttpRequest );
1326 if (request && request->hdr.htype == WH_HHTTPREQ)
1327 res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier );
1328 if( request )
1329 WININET_Release( &request->hdr );
1330
1331 if(res != ERROR_SUCCESS)
1332 SetLastError(res);
1333 return res == ERROR_SUCCESS;
1334 }
1335
1336 /***********************************************************************
1337 * HttpAddRequestHeadersA (WININET.@)
1338 *
1339 * Adds one or more HTTP header to the request handler
1340 *
1341 * RETURNS
1342 * TRUE on success
1343 * FALSE on failure
1344 *
1345 */
1346 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1347 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1348 {
1349 DWORD len;
1350 LPWSTR hdr;
1351 BOOL r;
1352
1353 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1354
1355 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1356 hdr = heap_alloc(len*sizeof(WCHAR));
1357 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1358 if( dwHeaderLength != ~0U )
1359 dwHeaderLength = len;
1360
1361 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1362
1363 heap_free( hdr );
1364 return r;
1365 }
1366
1367 static void free_accept_types( WCHAR **accept_types )
1368 {
1369 WCHAR *ptr, **types = accept_types;
1370
1371 if (!types) return;
1372 while ((ptr = *types))
1373 {
1374 heap_free( ptr );
1375 types++;
1376 }
1377 heap_free( accept_types );
1378 }
1379
1380 static WCHAR **convert_accept_types( const char **accept_types )
1381 {
1382 unsigned int count;
1383 const char **types = accept_types;
1384 WCHAR **typesW;
1385 BOOL invalid_pointer = FALSE;
1386
1387 if (!types) return NULL;
1388 count = 0;
1389 while (*types)
1390 {
1391 __TRY
1392 {
1393 /* find out how many there are */
1394 if (*types && **types)
1395 {
1396 TRACE("accept type: %s\n", debugstr_a(*types));
1397 count++;
1398 }
1399 }
1400 __EXCEPT_PAGE_FAULT
1401 {
1402 WARN("invalid accept type pointer\n");
1403 invalid_pointer = TRUE;
1404 }
1405 __ENDTRY;
1406 types++;
1407 }
1408 if (invalid_pointer) return NULL;
1409 if (!(typesW = heap_alloc( sizeof(WCHAR *) * (count + 1) ))) return NULL;
1410 count = 0;
1411 types = accept_types;
1412 while (*types)
1413 {
1414 if (*types && **types) typesW[count++] = heap_strdupAtoW( *types );
1415 types++;
1416 }
1417 typesW[count] = NULL;
1418 return typesW;
1419 }
1420
1421 /***********************************************************************
1422 * HttpOpenRequestA (WININET.@)
1423 *
1424 * Open a HTTP request handle
1425 *
1426 * RETURNS
1427 * HINTERNET a HTTP request handle on success
1428 * NULL on failure
1429 *
1430 */
1431 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1432 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1433 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1434 DWORD dwFlags, DWORD_PTR dwContext)
1435 {
1436 LPWSTR szVerb = NULL, szObjectName = NULL;
1437 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1438 HINTERNET rc = FALSE;
1439
1440 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1441 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1442 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1443 dwFlags, dwContext);
1444
1445 if (lpszVerb)
1446 {
1447 szVerb = heap_strdupAtoW(lpszVerb);
1448 if ( !szVerb )
1449 goto end;
1450 }
1451
1452 if (lpszObjectName)
1453 {
1454 szObjectName = heap_strdupAtoW(lpszObjectName);
1455 if ( !szObjectName )
1456 goto end;
1457 }
1458
1459 if (lpszVersion)
1460 {
1461 szVersion = heap_strdupAtoW(lpszVersion);
1462 if ( !szVersion )
1463 goto end;
1464 }
1465
1466 if (lpszReferrer)
1467 {
1468 szReferrer = heap_strdupAtoW(lpszReferrer);
1469 if ( !szReferrer )
1470 goto end;
1471 }
1472
1473 szAcceptTypes = convert_accept_types( lpszAcceptTypes );
1474 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName, szVersion, szReferrer,
1475 (const WCHAR **)szAcceptTypes, dwFlags, dwContext);
1476
1477 end:
1478 free_accept_types(szAcceptTypes);
1479 heap_free(szReferrer);
1480 heap_free(szVersion);
1481 heap_free(szObjectName);
1482 heap_free(szVerb);
1483 return rc;
1484 }
1485
1486 /***********************************************************************
1487 * HTTP_EncodeBase64
1488 */
1489 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1490 {
1491 UINT n = 0, x;
1492 static const CHAR HTTP_Base64Enc[] =
1493 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1494
1495 while( len > 0 )
1496 {
1497 /* first 6 bits, all from bin[0] */
1498 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1499 x = (bin[0] & 3) << 4;
1500
1501 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1502 if( len == 1 )
1503 {
1504 base64[n++] = HTTP_Base64Enc[x];
1505 base64[n++] = '=';
1506 base64[n++] = '=';
1507 break;
1508 }
1509 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1510 x = ( bin[1] & 0x0f ) << 2;
1511
1512 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1513 if( len == 2 )
1514 {
1515 base64[n++] = HTTP_Base64Enc[x];
1516 base64[n++] = '=';
1517 break;
1518 }
1519 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1520
1521 /* last 6 bits, all from bin [2] */
1522 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1523 bin += 3;
1524 len -= 3;
1525 }
1526 base64[n] = 0;
1527 return n;
1528 }
1529
1530 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1531 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1532 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1533 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1534 static const signed char HTTP_Base64Dec[256] =
1535 {
1536 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1537 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1538 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1539 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1540 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1541 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1542 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1543 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1544 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1545 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1546 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1547 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1548 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1549 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1550 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1551 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1552 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1553 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1554 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1555 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1556 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1557 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1558 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1559 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1560 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1561 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1562 };
1563 #undef CH
1564
1565 /***********************************************************************
1566 * HTTP_DecodeBase64
1567 */
1568 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1569 {
1570 unsigned int n = 0;
1571
1572 while(*base64)
1573 {
1574 signed char in[4];
1575
1576 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1577 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1578 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1579 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1580 {
1581 WARN("invalid base64: %s\n", debugstr_w(base64));
1582 return 0;
1583 }
1584 if (bin)
1585 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1586 n++;
1587
1588 if ((base64[2] == '=') && (base64[3] == '='))
1589 break;
1590 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1591 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1592 {
1593 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1594 return 0;
1595 }
1596 if (bin)
1597 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1598 n++;
1599
1600 if (base64[3] == '=')
1601 break;
1602 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1603 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1604 {
1605 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1606 return 0;
1607 }
1608 if (bin)
1609 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1610 n++;
1611
1612 base64 += 4;
1613 }
1614
1615 return n;
1616 }
1617
1618 /***********************************************************************
1619 * HTTP_InsertAuthorization
1620 *
1621 * Insert or delete the authorization field in the request header.
1622 */
1623 static BOOL HTTP_InsertAuthorization( http_request_t *request, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1624 {
1625 if (pAuthInfo)
1626 {
1627 static const WCHAR wszSpace[] = {' ',0};
1628 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1629 unsigned int len;
1630 WCHAR *authorization = NULL;
1631
1632 if (pAuthInfo->auth_data_len)
1633 {
1634 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1635 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1636 authorization = heap_alloc((len+1)*sizeof(WCHAR));
1637 if (!authorization)
1638 return FALSE;
1639
1640 strcpyW(authorization, pAuthInfo->scheme);
1641 strcatW(authorization, wszSpace);
1642 HTTP_EncodeBase64(pAuthInfo->auth_data,
1643 pAuthInfo->auth_data_len,
1644 authorization+strlenW(authorization));
1645
1646 /* clear the data as it isn't valid now that it has been sent to the
1647 * server, unless it's Basic authentication which doesn't do
1648 * connection tracking */
1649 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1650 {
1651 heap_free(pAuthInfo->auth_data);
1652 pAuthInfo->auth_data = NULL;
1653 pAuthInfo->auth_data_len = 0;
1654 }
1655 }
1656
1657 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1658
1659 HTTP_ProcessHeader(request, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1660 heap_free(authorization);
1661 }
1662 return TRUE;
1663 }
1664
1665 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1666 {
1667 static const WCHAR slash[] = { '/',0 };
1668 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','u',0 };
1669 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','u',0 };
1670 http_session_t *session = req->session;
1671 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1672 DWORD size;
1673
1674 size = sizeof(new_location);
1675 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1676 {
1677 URL_COMPONENTSW UrlComponents;
1678
1679 if (!(url = heap_alloc(size + sizeof(WCHAR)))) return NULL;
1680 strcpyW( url, new_location );
1681
1682 ZeroMemory(&UrlComponents,sizeof(URL_COMPONENTSW));
1683 if(InternetCrackUrlW(url, 0, 0, &UrlComponents)) goto done;
1684 heap_free(url);
1685 }
1686
1687 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1688 size += strlenW( session->hostName ) + strlenW( req->path );
1689
1690 if (!(url = heap_alloc(size * sizeof(WCHAR)))) return NULL;
1691
1692 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1693 sprintfW( url, formatSSL, session->hostName, session->hostPort );
1694 else
1695 sprintfW( url, format, session->hostName, session->hostPort );
1696 if (req->path[0] != '/') strcatW( url, slash );
1697 strcatW( url, req->path );
1698
1699 done:
1700 TRACE("url=%s\n", debugstr_w(url));
1701 return url;
1702 }
1703
1704 /***********************************************************************
1705 * HTTP_DealWithProxy
1706 */
1707 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_request_t *request)
1708 {
1709 WCHAR buf[INTERNET_MAX_HOST_NAME_LENGTH];
1710 WCHAR protoProxy[INTERNET_MAX_URL_LENGTH];
1711 DWORD protoProxyLen = INTERNET_MAX_URL_LENGTH;
1712 WCHAR proxy[INTERNET_MAX_URL_LENGTH];
1713 static WCHAR szNul[] = { 0 };
1714 URL_COMPONENTSW UrlComponents;
1715 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1716 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1717 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1718
1719 memset( &UrlComponents, 0, sizeof UrlComponents );
1720 UrlComponents.dwStructSize = sizeof UrlComponents;
1721 UrlComponents.lpszHostName = buf;
1722 UrlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
1723
1724 if (!INTERNET_FindProxyForProtocol(hIC->proxy, protoHttp, protoProxy, &protoProxyLen))
1725 return FALSE;
1726 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1727 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1728 sprintfW(proxy, szFormat, protoProxy);
1729 else
1730 strcpyW(proxy, protoProxy);
1731 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1732 return FALSE;
1733 if( UrlComponents.dwHostNameLength == 0 )
1734 return FALSE;
1735
1736 if( !request->path )
1737 request->path = szNul;
1738
1739 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1740 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1741
1742 heap_free(session->serverName);
1743 session->serverName = heap_strdupW(UrlComponents.lpszHostName);
1744 session->serverPort = UrlComponents.nPort;
1745
1746 TRACE("proxy server=%s port=%d\n", debugstr_w(session->serverName), session->serverPort);
1747 return TRUE;
1748 }
1749
1750 static DWORD HTTP_ResolveName(http_request_t *request, server_t *server)
1751 {
1752 socklen_t addr_len;
1753 const void *addr;
1754
1755 if(server->addr_len)
1756 return ERROR_SUCCESS;
1757
1758 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1759 INTERNET_STATUS_RESOLVING_NAME,
1760 server->name,
1761 (strlenW(server->name)+1) * sizeof(WCHAR));
1762
1763 addr_len = sizeof(server->addr);
1764 if (!GetAddress(server->name, server->port, (struct sockaddr *)&server->addr, &addr_len))
1765 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1766
1767 switch(server->addr.ss_family) {
1768 case AF_INET:
1769 addr = &((struct sockaddr_in *)&server->addr)->sin_addr;
1770 break;
1771 case AF_INET6:
1772 addr = &((struct sockaddr_in6 *)&server->addr)->sin6_addr;
1773 break;
1774 default:
1775 WARN("unsupported family %d\n", server->addr.ss_family);
1776 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1777 }
1778
1779 server->addr_len = addr_len;
1780 inet_ntop(server->addr.ss_family, addr, server->addr_str, sizeof(server->addr_str));
1781 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1782 INTERNET_STATUS_NAME_RESOLVED,
1783 server->addr_str, strlen(server->addr_str)+1);
1784
1785 TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str);
1786 return ERROR_SUCCESS;
1787 }
1788
1789 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1790 {
1791 static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
1792 static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
1793 static const WCHAR slash[] = { '/',0 };
1794 LPHTTPHEADERW host_header;
1795 LPCWSTR scheme;
1796
1797 host_header = HTTP_GetHeader(req, hostW);
1798 if(!host_header)
1799 return FALSE;
1800
1801 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1802 scheme = https;
1803 else
1804 scheme = http;
1805 strcpyW(buf, scheme);
1806 strcatW(buf, host_header->lpszValue);
1807 if (req->path[0] != '/')
1808 strcatW(buf, slash);
1809 strcatW(buf, req->path);
1810 return TRUE;
1811 }
1812
1813
1814 /***********************************************************************
1815 * HTTPREQ_Destroy (internal)
1816 *
1817 * Deallocate request handle
1818 *
1819 */
1820 static void HTTPREQ_Destroy(object_header_t *hdr)
1821 {
1822 http_request_t *request = (http_request_t*) hdr;
1823 DWORD i;
1824
1825 TRACE("\n");
1826
1827 if(request->hCacheFile) {
1828 WCHAR url[INTERNET_MAX_URL_LENGTH];
1829
1830 CloseHandle(request->hCacheFile);
1831
1832 if(HTTP_GetRequestURL(request, url)) {
1833 DWORD headersLen;
1834
1835 headersLen = request->rawHeaders ? strlenW(request->rawHeaders) : 0;
1836 CommitUrlCacheEntryW(url, request->cacheFile, request->expires,
1837 request->last_modified, NORMAL_CACHE_ENTRY,
1838 request->rawHeaders, headersLen, NULL, 0);
1839 }
1840 }
1841 heap_free(request->cacheFile);
1842
1843 request->read_section.DebugInfo->Spare[0] = 0;
1844 DeleteCriticalSection( &request->read_section );
1845 WININET_Release(&request->session->hdr);
1846
1847 destroy_authinfo(request->authInfo);
1848 destroy_authinfo(request->proxyAuthInfo);
1849
1850 heap_free(request->path);
1851 heap_free(request->verb);
1852 heap_free(request->rawHeaders);
1853 heap_free(request->version);
1854 heap_free(request->statusText);
1855
1856 for (i = 0; i < request->nCustHeaders; i++)
1857 {
1858 heap_free(request->custHeaders[i].lpszField);
1859 heap_free(request->custHeaders[i].lpszValue);
1860 }
1861 destroy_data_stream(request->data_stream);
1862 heap_free(request->custHeaders);
1863 }
1864
1865 static void http_release_netconn(http_request_t *req, BOOL reuse)
1866 {
1867 TRACE("%p %p\n",req, req->netconn);
1868
1869 if(!req->netconn)
1870 return;
1871
1872 #ifndef __REACTOS__
1873 if(reuse && req->netconn->keep_alive) {
1874 BOOL run_collector;
1875
1876 EnterCriticalSection(&connection_pool_cs);
1877
1878 list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry);
1879 req->netconn->keep_until = (DWORD64)GetTickCount() + COLLECT_TIME;
1880 req->netconn = NULL;
1881
1882 run_collector = !collector_running;
1883 collector_running = TRUE;
1884
1885 LeaveCriticalSection(&connection_pool_cs);
1886
1887 if(run_collector) {
1888 HANDLE thread = NULL;
1889 HMODULE module;
1890
1891 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
1892 if(module)
1893 thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL);
1894 if(!thread) {
1895 EnterCriticalSection(&connection_pool_cs);
1896 collector_running = FALSE;
1897 LeaveCriticalSection(&connection_pool_cs);
1898
1899 if(module)
1900 FreeLibrary(module);
1901 }
1902 else
1903 CloseHandle(thread);
1904 }
1905 return;
1906 }
1907 #else
1908 // silence unused function warning
1909 (void)collect_connections_proc;
1910 #endif
1911
1912 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1913 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1914
1915 free_netconn(req->netconn);
1916 req->netconn = NULL;
1917
1918 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1919 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1920 }
1921
1922 static void drain_content(http_request_t *req)
1923 {
1924 BOOL try_reuse;
1925
1926 if (!req->netconn) return;
1927
1928 if (req->contentLength == -1)
1929 try_reuse = FALSE;
1930 else if(!strcmpW(req->verb, szHEAD))
1931 try_reuse = TRUE;
1932 else
1933 try_reuse = req->data_stream->vtbl->drain_content(req->data_stream, req);
1934
1935 http_release_netconn(req, try_reuse);
1936 }
1937
1938 static BOOL HTTP_KeepAlive(http_request_t *request)
1939 {
1940 WCHAR szVersion[10];
1941 WCHAR szConnectionResponse[20];
1942 DWORD dwBufferSize = sizeof(szVersion);
1943 BOOL keepalive = FALSE;
1944
1945 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1946 * the connection is keep-alive by default */
1947 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1948 && !strcmpiW(szVersion, g_szHttp1_1))
1949 {
1950 keepalive = TRUE;
1951 }
1952
1953 dwBufferSize = sizeof(szConnectionResponse);
1954 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1955 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1956 {
1957 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1958 }
1959
1960 return keepalive;
1961 }
1962
1963 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1964 {
1965 http_request_t *req = (http_request_t*)hdr;
1966
1967 drain_content(req);
1968 }
1969
1970 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1971 {
1972 http_request_t *req = (http_request_t*)hdr;
1973
1974 switch(option) {
1975 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1976 {
1977 http_session_t *session = req->session;
1978 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1979
1980 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1981
1982 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1983 return ERROR_INSUFFICIENT_BUFFER;
1984 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1985 /* FIXME: can't get a SOCKET from our connection since we don't use
1986 * winsock
1987 */
1988 info->Socket = 0;
1989 /* FIXME: get source port from req->netConnection */
1990 info->SourcePort = 0;
1991 info->DestPort = session->hostPort;
1992 info->Flags = 0;
1993 if (HTTP_KeepAlive(req))
1994 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1995 if (session->appInfo->proxy && session->appInfo->proxy[0] != 0)
1996 info->Flags |= IDSI_FLAG_PROXY;
1997 if (req->netconn->useSSL)
1998 info->Flags |= IDSI_FLAG_SECURE;
1999
2000 return ERROR_SUCCESS;
2001 }
2002
2003 case INTERNET_OPTION_SECURITY_FLAGS:
2004 {
2005 DWORD flags;
2006
2007 if (*size < sizeof(ULONG))
2008 return ERROR_INSUFFICIENT_BUFFER;
2009
2010 *size = sizeof(DWORD);
2011 flags = 0;
2012 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
2013 flags |= SECURITY_FLAG_SECURE;
2014 flags |= req->security_flags;
2015 if(req->netconn) {
2016 int bits = NETCON_GetCipherStrength(req->netconn);
2017 if (bits >= 128)
2018 flags |= SECURITY_FLAG_STRENGTH_STRONG;
2019 else if (bits >= 56)
2020 flags |= SECURITY_FLAG_STRENGTH_MEDIUM;
2021 else
2022 flags |= SECURITY_FLAG_STRENGTH_WEAK;
2023 }
2024 *(DWORD *)buffer = flags;
2025 return ERROR_SUCCESS;
2026 }
2027
2028 case INTERNET_OPTION_HANDLE_TYPE:
2029 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2030
2031 if (*size < sizeof(ULONG))
2032 return ERROR_INSUFFICIENT_BUFFER;
2033
2034 *size = sizeof(DWORD);
2035 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
2036 return ERROR_SUCCESS;
2037
2038 case INTERNET_OPTION_URL: {
2039 WCHAR url[INTERNET_MAX_URL_LENGTH];
2040 HTTPHEADERW *host;
2041 DWORD len;
2042 WCHAR *pch;
2043
2044 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
2045
2046 TRACE("INTERNET_OPTION_URL\n");
2047
2048 host = HTTP_GetHeader(req, hostW);
2049 strcpyW(url, httpW);
2050 strcatW(url, host->lpszValue);
2051 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
2052 *pch = 0;
2053 strcatW(url, req->path);
2054
2055 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
2056
2057 if(unicode) {
2058 len = (strlenW(url)+1) * sizeof(WCHAR);
2059 if(*size < len)
2060 return ERROR_INSUFFICIENT_BUFFER;
2061
2062 *size = len;
2063 strcpyW(buffer, url);
2064 return ERROR_SUCCESS;
2065 }else {
2066 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
2067 if(len > *size)
2068 return ERROR_INSUFFICIENT_BUFFER;
2069
2070 *size = len;
2071 return ERROR_SUCCESS;
2072 }
2073 }
2074
2075 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
2076 INTERNET_CACHE_ENTRY_INFOW *info;
2077 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
2078 WCHAR url[INTERNET_MAX_URL_LENGTH];
2079 DWORD nbytes, error;
2080 BOOL ret;
2081
2082 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2083
2084 if (*size < sizeof(*ts))
2085 {
2086 *size = sizeof(*ts);
2087 return ERROR_INSUFFICIENT_BUFFER;
2088 }
2089 nbytes = 0;
2090 HTTP_GetRequestURL(req, url);
2091 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
2092 error = GetLastError();
2093 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2094 {
2095 if (!(info = heap_alloc(nbytes)))
2096 return ERROR_OUTOFMEMORY;
2097
2098 GetUrlCacheEntryInfoW(url, info, &nbytes);
2099
2100 ts->ftExpires = info->ExpireTime;
2101 ts->ftLastModified = info->LastModifiedTime;
2102
2103 heap_free(info);
2104 *size = sizeof(*ts);
2105 return ERROR_SUCCESS;
2106 }
2107 return error;
2108 }
2109
2110 case INTERNET_OPTION_DATAFILE_NAME: {
2111 DWORD req_size;
2112
2113 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2114
2115 if(!req->cacheFile) {
2116 *size = 0;
2117 return ERROR_INTERNET_ITEM_NOT_FOUND;
2118 }
2119
2120 if(unicode) {
2121 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
2122 if(*size < req_size)
2123 return ERROR_INSUFFICIENT_BUFFER;
2124
2125 *size = req_size;
2126 memcpy(buffer, req->cacheFile, *size);
2127 return ERROR_SUCCESS;
2128 }else {
2129 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
2130 if (req_size > *size)
2131 return ERROR_INSUFFICIENT_BUFFER;
2132
2133 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
2134 -1, buffer, *size, NULL, NULL);
2135 return ERROR_SUCCESS;
2136 }
2137 }
2138
2139 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
2140 PCCERT_CONTEXT context;
2141
2142 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2143 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2144 return ERROR_INSUFFICIENT_BUFFER;
2145 }
2146
2147 context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2148 if(context) {
2149 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2150 DWORD len;
2151
2152 memset(info, 0, sizeof(*info));
2153 info->ftExpiry = context->pCertInfo->NotAfter;
2154 info->ftStart = context->pCertInfo->NotBefore;
2155 len = CertNameToStrA(context->dwCertEncodingType,
2156 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
2157 info->lpszSubjectInfo = LocalAlloc(0, len);
2158 if(info->lpszSubjectInfo)
2159 CertNameToStrA(context->dwCertEncodingType,
2160 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
2161 info->lpszSubjectInfo, len);
2162 len = CertNameToStrA(context->dwCertEncodingType,
2163 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
2164 info->lpszIssuerInfo = LocalAlloc(0, len);
2165 if(info->lpszIssuerInfo)
2166 CertNameToStrA(context->dwCertEncodingType,
2167 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
2168 info->lpszIssuerInfo, len);
2169 info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2170 CertFreeCertificateContext(context);
2171 return ERROR_SUCCESS;
2172 }
2173 return ERROR_NOT_SUPPORTED;
2174 }
2175 case INTERNET_OPTION_CONNECT_TIMEOUT:
2176 if (*size < sizeof(DWORD))
2177 return ERROR_INSUFFICIENT_BUFFER;
2178
2179 *size = sizeof(DWORD);
2180 *(DWORD *)buffer = req->connect_timeout;
2181 return ERROR_SUCCESS;
2182 }
2183
2184 return INET_QueryOption(hdr, option, buffer, size, unicode);
2185 }
2186
2187 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2188 {
2189 http_request_t *req = (http_request_t*)hdr;
2190
2191 switch(option) {
2192 case INTERNET_OPTION_SECURITY_FLAGS:
2193 {
2194 DWORD flags;
2195
2196 if (!buffer || size != sizeof(DWORD))
2197 return ERROR_INVALID_PARAMETER;
2198 flags = *(DWORD *)buffer;
2199 TRACE("%08x\n", flags);
2200 req->security_flags = flags;
2201 if(req->netconn)
2202 req->netconn->security_flags = flags;
2203 return ERROR_SUCCESS;
2204 }
2205 case INTERNET_OPTION_CONNECT_TIMEOUT:
2206 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2207 req->connect_timeout = *(DWORD *)buffer;
2208 return ERROR_SUCCESS;
2209
2210 case INTERNET_OPTION_SEND_TIMEOUT:
2211 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2212 req->send_timeout = *(DWORD *)buffer;
2213 return ERROR_SUCCESS;
2214
2215 case INTERNET_OPTION_RECEIVE_TIMEOUT:
2216 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2217 req->receive_timeout = *(DWORD *)buffer;
2218 return ERROR_SUCCESS;
2219
2220 case INTERNET_OPTION_USERNAME:
2221 heap_free(req->session->userName);
2222 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2223 return ERROR_SUCCESS;
2224
2225 case INTERNET_OPTION_PASSWORD:
2226 heap_free(req->session->password);
2227 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2228 return ERROR_SUCCESS;
2229 case INTERNET_OPTION_HTTP_DECODING:
2230 if(size != sizeof(BOOL))
2231 return ERROR_INVALID_PARAMETER;
2232 req->decoding = *(BOOL*)buffer;
2233 return ERROR_SUCCESS;
2234 }
2235
2236 return INET_SetOption(hdr, option, buffer, size);
2237 }
2238
2239 /* read some more data into the read buffer (the read section must be held) */
2240 static DWORD read_more_data( http_request_t *req, int maxlen )
2241 {
2242 DWORD res;
2243 int len;
2244
2245 if (req->read_pos)
2246 {
2247 /* move existing data to the start of the buffer */
2248 if(req->read_size)
2249 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2250 req->read_pos = 0;
2251 }
2252
2253 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2254
2255 res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2256 maxlen - req->read_size, 0, &len );
2257 if(res == ERROR_SUCCESS)
2258 req->read_size += len;
2259
2260 return res;
2261 }
2262
2263 /* remove some amount of data from the read buffer (the read section must be held) */
2264 static void remove_data( http_request_t *req, int count )
2265 {
2266 if (!(req->read_size -= count)) req->read_pos = 0;
2267 else req->read_pos += count;
2268 }
2269
2270 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2271 {
2272 int count, bytes_read, pos = 0;
2273 DWORD res;
2274
2275 EnterCriticalSection( &req->read_section );
2276 for (;;)
2277 {
2278 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2279
2280 if (eol)
2281 {
2282 count = eol - (req->read_buf + req->read_pos);
2283 bytes_read = count + 1;
2284 }
2285 else count = bytes_read = req->read_size;
2286
2287 count = min( count, *len - pos );
2288 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2289 pos += count;
2290 remove_data( req, bytes_read );
2291 if (eol) break;
2292
2293 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2294 {
2295 *len = 0;
2296 TRACE( "returning empty string %u\n", res);
2297 LeaveCriticalSection( &req->read_section );
2298 INTERNET_SetLastError(res);
2299 return FALSE;
2300 }
2301 }
2302 LeaveCriticalSection( &req->read_section );
2303
2304 if (pos < *len)
2305 {
2306 if (pos && buffer[pos - 1] == '\r') pos--;
2307 *len = pos + 1;
2308 }
2309 buffer[*len - 1] = 0;
2310 TRACE( "returning %s\n", debugstr_a(buffer));
2311 return TRUE;
2312 }
2313
2314 /* check if we have reached the end of the data to read (the read section must be held) */
2315 static BOOL end_of_read_data( http_request_t *req )
2316 {
2317 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2318 }
2319
2320 /* fetch some more data into the read buffer (the read section must be held) */
2321 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2322 {
2323 DWORD res, read=0, want;
2324
2325 if(req->read_size == sizeof(req->read_buf))
2326 return ERROR_SUCCESS;
2327
2328 if(req->read_pos) {
2329 if(req->read_size)
2330 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2331 req->read_pos = 0;
2332 }
2333
2334 want = sizeof(req->read_buf) - req->read_size;
2335 res = req->data_stream->vtbl->read(req->data_stream, req, req->read_buf+req->read_size,
2336 want, &read, read_mode);
2337 assert(read <= want);
2338 req->read_size += read;
2339
2340 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2341 if(read_bytes)
2342 *read_bytes = read;
2343 return res;
2344 }
2345
2346 /* return the size of data available to be read immediately (the read section must be held) */
2347 static DWORD get_avail_data( http_request_t *req )
2348 {
2349 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2350 }
2351
2352 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2353 {
2354 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2355 DWORD avail = 0;
2356
2357 if(req->netconn)
2358 NETCON_query_data_available(req->netconn, &avail);
2359 return netconn_stream->content_length == ~0u
2360 ? avail
2361 : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2362 }
2363
2364 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2365 {
2366 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2367 return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2368 }
2369
2370 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2371 DWORD *read, read_mode_t read_mode)
2372 {
2373 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2374 int len = 0;
2375
2376 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2377
2378 if(read_mode == READMODE_NOBLOCK) {
2379 DWORD avail = netconn_get_avail_data(stream, req);
2380 if (size > avail)
2381 size = avail;
2382 }
2383
2384 if(size && req->netconn) {
2385 if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2386 len = 0;
2387 if(!len)
2388 netconn_stream->content_length = netconn_stream->content_read;
2389 }
2390
2391 netconn_stream->content_read += *read = len;
2392 TRACE("read %u bytes\n", len);
2393 return ERROR_SUCCESS;
2394 }
2395
2396 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
2397 {
2398 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2399 BYTE buf[1024];
2400 DWORD avail;
2401 int len;
2402
2403 if(netconn_end_of_data(stream, req))
2404 return TRUE;
2405
2406 do {
2407 avail = netconn_get_avail_data(stream, req);
2408 if(!avail)
2409 return FALSE;
2410
2411 if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
2412 return FALSE;
2413
2414 netconn_stream->content_read += len;
2415 }while(netconn_stream->content_read < netconn_stream->content_length);
2416
2417 return TRUE;
2418 }
2419
2420 static void netconn_destroy(data_stream_t *stream)
2421 {
2422 }
2423
2424 static const data_stream_vtbl_t netconn_stream_vtbl = {
2425 netconn_get_avail_data,
2426 netconn_end_of_data,
2427 netconn_read,
2428 netconn_drain_content,
2429 netconn_destroy
2430 };
2431
2432 /* read some more data into the read buffer (the read section must be held) */
2433 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2434 {
2435 DWORD res;
2436 int len;
2437
2438 if (stream->buf_pos)
2439 {
2440 /* move existing data to the start of the buffer */
2441 if(stream->buf_size)
2442 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2443 stream->buf_pos = 0;
2444 }
2445
2446 if (maxlen == -1) maxlen = sizeof(stream->buf);
2447
2448 res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2449 maxlen - stream->buf_size, 0, &len );
2450 if(res == ERROR_SUCCESS)
2451 stream->buf_size += len;
2452
2453 return res;
2454 }
2455
2456 /* remove some amount of data from the read buffer (the read section must be held) */
2457 static void remove_chunked_data(chunked_stream_t *stream, int count)
2458 {
2459 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2460 else stream->buf_pos += count;
2461 }
2462
2463 /* discard data contents until we reach end of line (the read section must be held) */
2464 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2465 {
2466 DWORD res;
2467
2468 do
2469 {
2470 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2471 if (eol)
2472 {
2473 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2474 break;
2475 }
2476 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2477 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2478 } while (stream->buf_size);
2479 return ERROR_SUCCESS;
2480 }
2481
2482 /* read the size of the next chunk (the read section must be held) */
2483 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2484 {
2485 /* TODOO */
2486 DWORD chunk_size = 0, res;
2487
2488 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2489 return res;
2490
2491 for (;;)
2492 {
2493 while (stream->buf_size)
2494 {
2495 char ch = stream->buf[stream->buf_pos];
2496 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2497 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2498 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2499 else if (ch == ';' || ch == '\r' || ch == '\n')
2500 {
2501 TRACE( "reading %u byte chunk\n", chunk_size );
2502 stream->chunk_size = chunk_size;
2503 req->contentLength += chunk_size;
2504 return discard_chunked_eol(stream, req);
2505 }
2506 remove_chunked_data(stream, 1);
2507 }
2508 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2509 if (!stream->buf_size)
2510 {
2511 stream->chunk_size = 0;
2512 return ERROR_SUCCESS;
2513 }
2514 }
2515 }
2516
2517 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2518 {
2519 /* Allow reading only from read buffer */
2520 return 0;
2521 }
2522
2523 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2524 {
2525 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2526 return !chunked_stream->chunk_size;
2527 }
2528
2529 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2530 DWORD *read, read_mode_t read_mode)
2531 {
2532 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2533 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2534
2535 if(chunked_stream->chunk_size == ~0u) {
2536 res = start_next_chunk(chunked_stream, req);
2537 if(res != ERROR_SUCCESS)
2538 return res;
2539 }
2540
2541 while(size && chunked_stream->chunk_size) {
2542 if(chunked_stream->buf_size) {
2543 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2544
2545 /* this could block */
2546 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2547 break;
2548
2549 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2550 remove_chunked_data(chunked_stream, read_bytes);
2551 }else {
2552 read_bytes = min(size, chunked_stream->chunk_size);
2553
2554 if(read_mode == READMODE_NOBLOCK) {
2555 DWORD avail;
2556
2557 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2558 break;
2559 if(read_bytes > avail)
2560 read_bytes = avail;
2561
2562 /* this could block */
2563 if(read_bytes == chunked_stream->chunk_size)
2564 break;
2565 }
2566
2567 res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2568 if(res != ERROR_SUCCESS)
2569 break;
2570 }
2571
2572 chunked_stream->chunk_size -= read_bytes;
2573 size -= read_bytes;
2574 ret_read += read_bytes;
2575 if(!chunked_stream->chunk_size) {
2576 assert(read_mode != READMODE_NOBLOCK);
2577 res = start_next_chunk(chunked_stream, req);
2578 if(res != ERROR_SUCCESS)
2579 break;
2580 }
2581
2582 if(read_mode == READMODE_ASYNC)
2583 read_mode = READMODE_NOBLOCK;
2584 }
2585
2586 TRACE("read %u bytes\n", ret_read);
2587 *read = ret_read;
2588 return res;
2589 }
2590
2591 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
2592 {
2593 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2594
2595 /* FIXME: we can do better */
2596 return !chunked_stream->chunk_size;
2597 }
2598
2599 static void chunked_destroy(data_stream_t *stream)
2600 {
2601 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2602 heap_free(chunked_stream);
2603 }
2604
2605 static const data_stream_vtbl_t chunked_stream_vtbl = {
2606 chunked_get_avail_data,
2607 chunked_end_of_data,
2608 chunked_read,
2609 chunked_drain_content,
2610 chunked_destroy
2611 };
2612
2613 /* set the request content length based on the headers */
2614 static DWORD set_content_length(http_request_t *request)
2615 {
2616 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2617 WCHAR encoding[20];
2618 DWORD size;
2619
2620 if(request->status_code == HTTP_STATUS_NO_CONTENT) {
2621 request->contentLength = request->netconn_stream.content_length = 0;
2622 return ERROR_SUCCESS;
2623 }
2624
2625 size = sizeof(request->contentLength);
2626 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2627 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2628 request->contentLength = ~0u;
2629 request->netconn_stream.content_length = request->contentLength;
2630 request->netconn_stream.content_read = request->read_size;
2631
2632 size = sizeof(encoding);
2633 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2634 !strcmpiW(encoding, szChunked))
2635 {
2636 chunked_stream_t *chunked_stream;
2637
2638 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2639 if(!chunked_stream)
2640 return ERROR_OUTOFMEMORY;
2641
2642 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2643 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2644 chunked_stream->chunk_size = ~0u;
2645
2646 if(request->read_size) {
2647 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2648 chunked_stream->buf_size = request->read_size;
2649 request->read_size = request->read_pos = 0;
2650 }
2651
2652 request->data_stream = &chunked_stream->data_stream;
2653 request->contentLength = ~0u;
2654 request->read_chunked = TRUE;
2655 }
2656
2657 if(request->decoding) {
2658 int encoding_idx;
2659
2660 static const WCHAR gzipW[] = {'g','z','i','p',0};
2661
2662 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2663 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2664 return init_gzip_stream(request);
2665 }
2666
2667 return ERROR_SUCCESS;
2668 }
2669
2670 static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2671 {
2672 INTERNET_ASYNC_RESULT iar;
2673
2674 iar.dwResult = result;
2675 iar.dwError = error;
2676
2677 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2678 sizeof(INTERNET_ASYNC_RESULT));
2679 }
2680
2681 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2682 {
2683 DWORD res, read = 0, avail = 0;
2684 read_mode_t mode;
2685
2686 TRACE("%p\n", req);
2687
2688 EnterCriticalSection( &req->read_section );
2689
2690 mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
2691 res = refill_read_buffer(req, mode, &read);
2692 if(res == ERROR_SUCCESS && !first_notif)
2693 avail = get_avail_data(req);
2694
2695 LeaveCriticalSection( &req->read_section );
2696
2697 if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
2698 WARN("res %u read %u, closing connection\n", res, read);
2699 http_release_netconn(req, FALSE);
2700 }
2701
2702 if(res == ERROR_SUCCESS)
2703 send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2704 else
2705 send_request_complete(req, 0, res);
2706 }
2707
2708 /* read data from the http connection (the read section must be held) */
2709 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2710 {
2711 DWORD current_read = 0, ret_read = 0;
2712 read_mode_t read_mode;
2713 DWORD res = ERROR_SUCCESS;
2714
2715 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2716
2717 EnterCriticalSection( &req->read_section );
2718
2719 if(req->read_size) {
2720 ret_read = min(size, req->read_size);
2721 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2722 req->read_size -= ret_read;
2723 req->read_pos += ret_read;
2724 if(read_mode == READMODE_ASYNC)
2725 read_mode = READMODE_NOBLOCK;
2726 }
2727
2728 if(ret_read < size) {
2729 res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, read_mode);
2730 ret_read += current_read;
2731 }
2732
2733 LeaveCriticalSection( &req->read_section );
2734
2735 *read = ret_read;
2736 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2737
2738 if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
2739 BOOL res;
2740 DWORD written;
2741
2742 res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
2743 if(!res)
2744 WARN("WriteFile failed: %u\n", GetLastError());
2745 }
2746
2747 if(size && !ret_read)
2748 http_release_netconn(req, res == ERROR_SUCCESS);
2749
2750 return res;
2751 }
2752
2753
2754 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2755 {
2756 http_request_t *req = (http_request_t*)hdr;
2757 DWORD res;
2758
2759 EnterCriticalSection( &req->read_section );
2760 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2761 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2762
2763 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2764 if(res == ERROR_SUCCESS)
2765 res = hdr->dwError;
2766 LeaveCriticalSection( &req->read_section );
2767
2768 return res;
2769 }
2770
2771 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2772 {
2773 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2774 http_request_t *req = (http_request_t*)workRequest->hdr;
2775 DWORD res;
2776
2777 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2778
2779 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2780 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2781
2782 send_request_complete(req, res == ERROR_SUCCESS, res);
2783 }
2784
2785 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2786 DWORD flags, DWORD_PTR context)
2787 {
2788 http_request_t *req = (http_request_t*)hdr;
2789 DWORD res, size, read, error = ERROR_SUCCESS;
2790
2791 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2792 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2793
2794 if (buffers->dwStructSize != sizeof(*buffers))
2795 return ERROR_INVALID_PARAMETER;
2796
2797 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2798
2799 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2800 {
2801 WORKREQUEST workRequest;
2802
2803 if (TryEnterCriticalSection( &req->read_section ))
2804 {
2805 if (get_avail_data(req))
2806 {
2807 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2808 &buffers->dwBufferLength, FALSE);
2809 size = buffers->dwBufferLength;
2810 LeaveCriticalSection( &req->read_section );
2811 goto done;
2812 }
2813 LeaveCriticalSection( &req->read_section );
2814 }
2815
2816 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2817 workRequest.hdr = WININET_AddRef(&req->hdr);
2818 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2819
2820 INTERNET_AsyncCall(&workRequest);
2821
2822 return ERROR_IO_PENDING;
2823 }
2824
2825 read = 0;
2826 size = buffers->dwBufferLength;
2827
2828 EnterCriticalSection( &req->read_section );
2829 if(hdr->dwError == ERROR_SUCCESS)
2830 hdr->dwError = INTERNET_HANDLE_IN_USE;
2831 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2832 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2833
2834 while(1) {
2835 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2836 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2837 if(res != ERROR_SUCCESS)
2838 break;
2839
2840 read += buffers->dwBufferLength;
2841 if(read == size || end_of_read_data(req))
2842 break;
2843
2844 LeaveCriticalSection( &req->read_section );
2845
2846 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2847 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2848 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2849 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2850
2851 EnterCriticalSection( &req->read_section );
2852 }
2853
2854 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2855 hdr->dwError = ERROR_SUCCESS;
2856 else
2857 error = hdr->dwError;
2858
2859 LeaveCriticalSection( &req->read_section );
2860 size = buffers->dwBufferLength;
2861 buffers->dwBufferLength = read;
2862
2863 done:
2864 if (res == ERROR_SUCCESS) {
2865 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2866 &size, sizeof(size));
2867 }
2868
2869 return res==ERROR_SUCCESS ? error : res;
2870 }
2871
2872 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2873 {
2874 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2875 http_request_t *req = (http_request_t*)workRequest->hdr;
2876 DWORD res;
2877
2878 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2879
2880 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2881 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2882
2883 send_request_complete(req, res == ERROR_SUCCESS, res);
2884 }
2885
2886 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2887 DWORD flags, DWORD_PTR context)
2888 {
2889
2890 http_request_t *req = (http_request_t*)hdr;
2891 DWORD res, size, read, error = ERROR_SUCCESS;
2892
2893 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2894 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2895
2896 if (buffers->dwStructSize != sizeof(*buffers))
2897 return ERROR_INVALID_PARAMETER;
2898
2899 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2900
2901 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2902 {
2903 WORKREQUEST workRequest;
2904
2905 if (TryEnterCriticalSection( &req->read_section ))
2906 {
2907 if (get_avail_data(req))
2908 {
2909 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2910 &buffers->dwBufferLength, FALSE);
2911 size = buffers->dwBufferLength;
2912 LeaveCriticalSection( &req->read_section );
2913 goto done;
2914 }
2915 LeaveCriticalSection( &req->read_section );
2916 }
2917
2918 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2919 workRequest.hdr = WININET_AddRef(&req->hdr);
2920 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2921
2922 INTERNET_AsyncCall(&workRequest);
2923
2924 return ERROR_IO_PENDING;
2925 }
2926
2927 read = 0;
2928 size = buffers->dwBufferLength;
2929
2930 EnterCriticalSection( &req->read_section );
2931 if(hdr->dwError == ERROR_SUCCESS)
2932 hdr->dwError = INTERNET_HANDLE_IN_USE;
2933 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2934 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2935
2936 while(1) {
2937 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2938 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2939 if(res != ERROR_SUCCESS)
2940 break;
2941
2942 read += buffers->dwBufferLength;
2943 if(read == size || end_of_read_data(req))
2944 break;
2945
2946 LeaveCriticalSection( &req->read_section );
2947
2948 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2949 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2950 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2951 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2952
2953 EnterCriticalSection( &req->read_section );
2954 }
2955
2956 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2957 hdr->dwError = ERROR_SUCCESS;
2958 else
2959 error = hdr->dwError;
2960
2961 LeaveCriticalSection( &req->read_section );
2962 size = buffers->dwBufferLength;
2963 buffers->dwBufferLength = read;
2964
2965 done:
2966 if (res == ERROR_SUCCESS) {
2967 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2968 &size, sizeof(size));
2969 }
2970
2971 return res==ERROR_SUCCESS ? error : res;
2972 }
2973
2974 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2975 {
2976 DWORD res;
2977 http_request_t *request = (http_request_t*)hdr;
2978
2979 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2980
2981 *written = 0;
2982 res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
2983 if (res == ERROR_SUCCESS)
2984 request->bytesWritten += *written;
2985
2986 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2987 return res;
2988 }
2989
2990 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2991 {
2992 http_request_t *req = (http_request_t*)workRequest->hdr;
2993
2994 HTTP_ReceiveRequestData(req, FALSE);
2995 }
2996
2997 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2998 {
2999 http_request_t *req = (http_request_t*)hdr;
3000
3001 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
3002
3003 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3004 {
3005 WORKREQUEST workRequest;
3006
3007 /* never wait, if we can't enter the section we queue an async request right away */
3008 if (TryEnterCriticalSection( &req->read_section ))
3009 {
3010 refill_read_buffer(req, READMODE_NOBLOCK, NULL);
3011 if ((*available = get_avail_data( req ))) goto done;
3012 if (end_of_read_data( req )) goto done;
3013 LeaveCriticalSection( &req->read_section );
3014 }
3015
3016 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
3017 workRequest.hdr = WININET_AddRef( &req->hdr );
3018
3019 INTERNET_AsyncCall(&workRequest);
3020
3021 return ERROR_IO_PENDING;
3022 }
3023
3024 EnterCriticalSection( &req->read_section );
3025
3026 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
3027 {
3028 refill_read_buffer( req, READMODE_ASYNC, NULL );
3029 *available = get_avail_data( req );
3030 }
3031
3032 done:
3033 LeaveCriticalSection( &req->read_section );
3034
3035 TRACE( "returning %u\n", *available );
3036 return ERROR_SUCCESS;
3037 }
3038
3039 static const object_vtbl_t HTTPREQVtbl = {
3040 HTTPREQ_Destroy,
3041 HTTPREQ_CloseConnection,
3042 HTTPREQ_QueryOption,
3043 HTTPREQ_SetOption,
3044 HTTPREQ_ReadFile,
3045 HTTPREQ_ReadFileExA,
3046 HTTPREQ_ReadFileExW,
3047 HTTPREQ_WriteFile,
3048 HTTPREQ_QueryDataAvailable,
3049 NULL
3050 };
3051
3052 /***********************************************************************
3053 * HTTP_HttpOpenRequestW (internal)
3054 *
3055 * Open a HTTP request handle
3056 *
3057 * RETURNS
3058 * HINTERNET a HTTP request handle on success
3059 * NULL on failure
3060 *
3061 */
3062 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3063 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3064 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3065 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3066 {
3067 appinfo_t *hIC = session->appInfo;
3068 http_request_t *request;
3069 DWORD len, res = ERROR_SUCCESS;
3070
3071 TRACE("-->\n");
3072
3073 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
3074 if(!request)
3075 return ERROR_OUTOFMEMORY;
3076
3077 request->hdr.htype = WH_HHTTPREQ;
3078 request->hdr.dwFlags = dwFlags;
3079 request->hdr.dwContext = dwContext;
3080 request->contentLength = ~0u;
3081
3082 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3083 request->data_stream = &request->netconn_stream.data_stream;
3084 request->connect_timeout = session->connect_timeout;
3085 request->send_timeout = session->send_timeout;
3086 request->receive_timeout = session->receive_timeout;
3087
3088 InitializeCriticalSection( &request->read_section );
3089 request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3090
3091 WININET_AddRef( &session->hdr );
3092 request->session = session;
3093 list_add_head( &session->hdr.children, &request->hdr.entry );
3094
3095 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3096 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3097 if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3098 request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3099
3100 if (lpszObjectName && *lpszObjectName) {
3101 HRESULT rc;
3102
3103 len = 0;
3104 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3105 if (rc != E_POINTER)
3106 len = strlenW(lpszObjectName)+1;
3107 request->path = heap_alloc(len*sizeof(WCHAR));
3108 rc = UrlEscapeW(lpszObjectName, request->path, &len,
3109 URL_ESCAPE_SPACES_ONLY);
3110 if (rc != S_OK)
3111 {
3112 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3113 strcpyW(request->path,lpszObjectName);
3114 }
3115 }else {
3116 static const WCHAR slashW[] = {'/',0};
3117
3118 request->path = heap_strdupW(slashW);
3119 }
3120
3121 if (lpszReferrer && *lpszReferrer)
3122 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3123
3124 if (lpszAcceptTypes)
3125 {
3126 int i;
3127 for (i = 0; lpszAcceptTypes[i]; i++)
3128 {
3129 if (!*lpszAcceptTypes[i]) continue;
3130 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3131 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
3132 HTTP_ADDHDR_FLAG_REQ |
3133 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
3134 }
3135 }
3136
3137 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3138 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
3139
3140 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
3141 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
3142 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
3143 {
3144 WCHAR *host_name;
3145
3146 static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
3147
3148 host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
3149 if (!host_name) {
3150 res = ERROR_OUTOFMEMORY;
3151 goto lend;
3152 }
3153
3154 sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
3155 HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3156 heap_free(host_name);
3157 }
3158 else
3159 HTTP_ProcessHeader(request, hostW, session->hostName,
3160 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3161
3162 if (session->serverPort == INTERNET_INVALID_PORT_NUMBER)
3163 session->serverPort = (dwFlags & INTERNET_FLAG_SECURE ?
3164 INTERNET_DEFAULT_HTTPS_PORT :
3165 INTERNET_DEFAULT_HTTP_PORT);
3166
3167 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
3168 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3169 INTERNET_DEFAULT_HTTPS_PORT :
3170 INTERNET_DEFAULT_HTTP_PORT);
3171
3172 if (hIC->proxy && hIC->proxy[0])
3173 HTTP_DealWithProxy( hIC, session, request );
3174
3175 INTERNET_SendCallback(&session->hdr, dwContext,
3176 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3177 sizeof(HINTERNET));
3178
3179 lend:
3180 TRACE("<-- %u (%p)\n", res, request);
3181
3182 if(res != ERROR_SUCCESS) {
3183 WININET_Release( &request->hdr );
3184 *ret = NULL;
3185 return res;
3186 }
3187
3188 *ret = request->hdr.hInternet;
3189 return ERROR_SUCCESS;
3190 }
3191
3192 /***********************************************************************
3193 * HttpOpenRequestW (WININET.@)
3194 *
3195 * Open a HTTP request handle
3196 *
3197 * RETURNS
3198 * HINTERNET a HTTP request handle on success
3199 * NULL on failure
3200 *
3201 */
3202 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
3203 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3204 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3205 DWORD dwFlags, DWORD_PTR dwContext)
3206 {
3207 http_session_t *session;
3208 HINTERNET handle = NULL;
3209 DWORD res;
3210
3211 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3212 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3213 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3214 dwFlags, dwContext);
3215 if(lpszAcceptTypes!=NULL)
3216 {
3217 int i;
3218 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3219 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3220 }
3221
3222 session = (http_session_t*) get_handle_object( hHttpSession );
3223 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3224 {
3225 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3226 goto lend;
3227 }
3228
3229 /*
3230 * My tests seem to show that the windows version does not
3231 * become asynchronous until after this point. And anyhow
3232 * if this call was asynchronous then how would you get the
3233 * necessary HINTERNET pointer returned by this function.
3234 *
3235 */
3236 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3237 lpszVersion, lpszReferrer, lpszAcceptTypes,
3238 dwFlags, dwContext, &handle);
3239 lend:
3240 if( session )
3241 WININET_Release( &session->hdr );
3242 TRACE("returning %p\n", handle);
3243 if(res != ERROR_SUCCESS)
3244 SetLastError(res);
3245 return handle;
3246 }
3247
3248 static const LPCWSTR header_lookup[] = {
3249 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3250 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3251 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3252 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3253 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3254 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3255 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3256 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3257 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3258 szDate, /* HTTP_QUERY_DATE = 9 */
3259 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3260 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3261 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3262 szURI, /* HTTP_QUERY_URI = 13 */
3263 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3264 NULL, /* HTTP_QUERY_COST = 15 */
3265 NULL, /* HTTP_QUERY_LINK = 16 */
3266 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3267 NULL, /* HTTP_QUERY_VERSION = 18 */
3268 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3269 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3270 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3271 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3272 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3273 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3274 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3275 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3276 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3277 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3278 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3279 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3280 NULL, /* HTTP_QUERY_FROM = 31 */
3281 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3282 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3283 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3284 szReferer, /* HTTP_QUERY_REFERER = 35 */
3285 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3286 szServer, /* HTTP_QUERY_SERVER = 37 */
3287 NULL, /* HTTP_TITLE = 38 */
3288 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3289 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3290 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3291 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3292 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3293 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3294 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3295 NULL, /* HTTP_QUERY_REFRESH = 46 */
3296 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3297 szAge, /* HTTP_QUERY_AGE = 48 */
3298 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3299 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3300 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3301 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3302 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3303 szETag, /* HTTP_QUERY_ETAG = 54 */
3304 hostW, /* HTTP_QUERY_HOST = 55 */
3305 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3306 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3307 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3308 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3309 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3310 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3311 szRange, /* HTTP_QUERY_RANGE = 62 */
3312 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3313 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3314 szVary, /* HTTP_QUERY_VARY = 65 */
3315 szVia, /* HTTP_QUERY_VIA = 66 */
3316 szWarning, /* HTTP_QUERY_WARNING = 67 */
3317 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3318 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3319 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3320 };
3321
3322 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3323
3324 /***********************************************************************
3325 * HTTP_HttpQueryInfoW (internal)
3326 */
3327 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3328 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3329 {
3330 LPHTTPHEADERW lphttpHdr = NULL;
3331 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3332 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3333 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3334 INT index = -1;
3335
3336 /* Find requested header structure */
3337 switch (level)
3338 {
3339 case HTTP_QUERY_CUSTOM:
3340 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3341 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3342 break;
3343 case HTTP_QUERY_RAW_HEADERS_CRLF:
3344 {
3345 LPWSTR headers;
3346 DWORD len = 0;
3347 DWORD res = ERROR_INVALID_PARAMETER;
3348
3349 if (request_only)
3350 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3351 else
3352 headers = request->rawHeaders;
3353
3354 if (headers)
3355 len = strlenW(headers) * sizeof(WCHAR);
3356
3357 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3358 {
3359 len += sizeof(WCHAR);
3360 res = ERROR_INSUFFICIENT_BUFFER;
3361 }
3362 else if (lpBuffer)
3363 {
3364 if (headers)
3365 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3366 else
3367 {
3368 len = strlenW(szCrLf) * sizeof(WCHAR);
3369 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3370 }
3371 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3372 res = ERROR_SUCCESS;
3373 }
3374 *lpdwBufferLength = len;
3375
3376 if (request_only) heap_free(headers);
3377 return res;
3378 }
3379 case HTTP_QUERY_RAW_HEADERS:
3380 {
3381 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3382 DWORD i, size = 0;
3383 LPWSTR pszString = lpBuffer;
3384
3385 for (i = 0; ppszRawHeaderLines[i]; i++)
3386 size += strlenW(ppszRawHeaderLines[i]) + 1;
3387
3388 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3389 {
3390 HTTP_FreeTokens(ppszRawHeaderLines);
3391 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3392 return ERROR_INSUFFICIENT_BUFFER;
3393 }
3394 if (pszString)
3395 {
3396 for (i = 0; ppszRawHeaderLines[i]; i++)
3397 {
3398 DWORD len = strlenW(ppszRawHeaderLines[i]);
3399 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3400 pszString += len+1;
3401 }
3402 *pszString = '\0';
3403 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3404 }
3405 *lpdwBufferLength = size * sizeof(WCHAR);
3406 HTTP_FreeTokens(ppszRawHeaderLines);
3407
3408 return ERROR_SUCCESS;
3409 }
3410 case HTTP_QUERY_STATUS_TEXT:
3411 if (request->statusText)
3412 {
3413 DWORD len = strlenW(request->statusText);
3414 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3415 {
3416 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3417 return ERROR_INSUFFICIENT_BUFFER;
3418 }
3419 if (lpBuffer)
3420 {
3421 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3422 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3423 }
3424 *lpdwBufferLength = len * sizeof(WCHAR);
3425 return ERROR_SUCCESS;
3426 }
3427 break;
3428 case HTTP_QUERY_VERSION:
3429 if (request->version)
3430 {
3431 DWORD len = strlenW(request->version);
3432 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3433 {
3434 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3435 return ERROR_INSUFFICIENT_BUFFER;
3436 }
3437 if (lpBuffer)
3438 {
3439 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3440 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3441 }
3442 *lpdwBufferLength = len * sizeof(WCHAR);
3443 return ERROR_SUCCESS;
3444 }
3445 break;
3446 case HTTP_QUERY_CONTENT_ENCODING:
3447 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3448 requested_index,request_only);
3449 break;
3450 case HTTP_QUERY_STATUS_CODE: {
3451 DWORD res = ERROR_SUCCESS;
3452
3453 if(request_only || requested_index)
3454 break;
3455
3456 if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
3457 if(*lpdwBufferLength >= sizeof(DWORD))
3458 *(DWORD*)lpBuffer = request->status_code;
3459 else
3460 res = ERROR_INSUFFICIENT_BUFFER;
3461 *lpdwBufferLength = sizeof(DWORD);
3462 }else {
3463 WCHAR buf[12];
3464 DWORD size;
3465 static const WCHAR formatW[] = {'%','u',0};
3466
3467 size = (sprintfW(buf, formatW, request->status_code)+1) * sizeof(WCHAR);
3468
3469 if(size <= *lpdwBufferLength)
3470 memcpy(lpBuffer, buf, size);
3471 else
3472 res = ERROR_INSUFFICIENT_BUFFER;
3473
3474 *lpdwBufferLength = size;
3475 }
3476 return res;
3477 }
3478 default:
3479 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3480
3481 if (level < LAST_TABLE_HEADER && header_lookup[level])
3482 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3483 requested_index,request_only);
3484 }
3485
3486 if (index >= 0)
3487 lphttpHdr = &request->custHeaders[index];
3488
3489 /* Ensure header satisfies requested attributes */
3490 if (!lphttpHdr ||
3491 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3492 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3493 {
3494 return ERROR_HTTP_HEADER_NOT_FOUND;
3495 }
3496
3497 if (lpdwIndex) (*lpdwIndex)++;
3498
3499 /* coalesce value to requested type */
3500 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3501 {
3502 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3503 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3504 }
3505 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3506 {
3507 time_t tmpTime;
3508 struct tm tmpTM;
3509 SYSTEMTIME *STHook;
3510
3511 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3512
3513 tmpTM = *gmtime(&tmpTime);
3514 STHook = (SYSTEMTIME *)lpBuffer;
3515 STHook->wDay = tmpTM.tm_mday;
3516 STHook->wHour = tmpTM.tm_hour;
3517 STHook->wMilliseconds = 0;
3518 STHook->wMinute = tmpTM.tm_min;
3519 STHook->wDayOfWeek = tmpTM.tm_wday;
3520 STHook->wMonth = tmpTM.tm_mon + 1;
3521 STHook->wSecond = tmpTM.tm_sec;
3522 STHook->wYear = tmpTM.tm_year;
3523
3524 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3525 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3526 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3527 }
3528 else if (lphttpHdr->lpszValue)
3529 {
3530 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3531
3532 if (len > *lpdwBufferLength)
3533 {
3534 *lpdwBufferLength = len;
3535 return ERROR_INSUFFICIENT_BUFFER;
3536 }
3537 if (lpBuffer)
3538 {
3539 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3540 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3541 }
3542 *lpdwBufferLength = len - sizeof(WCHAR);
3543 }
3544 return ERROR_SUCCESS;
3545 }
3546
3547 /***********************************************************************
3548 * HttpQueryInfoW (WININET.@)
3549 *
3550 * Queries for information about an HTTP request
3551 *
3552 * RETURNS
3553 * TRUE on success
3554 * FALSE on failure
3555 *
3556 */
3557 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3558 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3559 {
3560 http_request_t *request;
3561 DWORD res;
3562
3563 if (TRACE_ON(wininet)) {
3564 #define FE(x) { x, #x }
3565 static const wininet_flag_info query_flags[] = {
3566 FE(HTTP_QUERY_MIME_VERSION),
3567 FE(HTTP_QUERY_CONTENT_TYPE),
3568 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3569 FE(HTTP_QUERY_CONTENT_ID),
3570 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3571 FE(HTTP_QUERY_CONTENT_LENGTH),
3572 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3573 FE(HTTP_QUERY_ALLOW),
3574 FE(HTTP_QUERY_PUBLIC),
3575 FE(HTTP_QUERY_DATE),
3576 FE(HTTP_QUERY_EXPIRES),
3577 FE(HTTP_QUERY_LAST_MODIFIED),
3578 FE(HTTP_QUERY_MESSAGE_ID),
3579 FE(HTTP_QUERY_URI),
3580 FE(HTTP_QUERY_DERIVED_FROM),
3581 FE(HTTP_QUERY_COST),
3582 FE(HTTP_QUERY_LINK),
3583 FE(HTTP_QUERY_PRAGMA),
3584 FE(HTTP_QUERY_VERSION),
3585 FE(HTTP_QUERY_STATUS_CODE),
3586 FE(HTTP_QUERY_STATUS_TEXT),
3587 FE(HTTP_QUERY_RAW_HEADERS),
3588 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3589 FE(HTTP_QUERY_CONNECTION),
3590 FE(HTTP_QUERY_ACCEPT),
3591 FE(HTTP_QUERY_ACCEPT_CHARSET),
3592 FE(HTTP_QUERY_ACCEPT_ENCODING),
3593 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3594 FE(HTTP_QUERY_AUTHORIZATION),
3595 FE(HTTP_QUERY_CONTENT_ENCODING),
3596 FE(HTTP_QUERY_FORWARDED),
3597 FE(HTTP_QUERY_FROM),
3598 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3599 FE(HTTP_QUERY_LOCATION),
3600 FE(HTTP_QUERY_ORIG_URI),
3601 FE(HTTP_QUERY_REFERER),
3602 FE(HTTP_QUERY_RETRY_AFTER),
3603 FE(HTTP_QUERY_SERVER),
3604 FE(HTTP_QUERY_TITLE),
3605 FE(HTTP_QUERY_USER_AGENT),
3606 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3607 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3608 FE(HTTP_QUERY_ACCEPT_RANGES),
3609 FE(HTTP_QUERY_SET_COOKIE),
3610 FE(HTTP_QUERY_COOKIE),
3611 FE(HTTP_QUERY_REQUEST_METHOD),
3612 FE(HTTP_QUERY_REFRESH),
3613 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3614 FE(HTTP_QUERY_AGE),
3615 FE(HTTP_QUERY_CACHE_CONTROL),
3616 FE(HTTP_QUERY_CONTENT_BASE),
3617 FE(HTTP_QUERY_CONTENT_LOCATION),
3618 FE(HTTP_QUERY_CONTENT_MD5),
3619 FE(HTTP_QUERY_CONTENT_RANGE),
3620 FE(HTTP_QUERY_ETAG),
3621 FE(HTTP_QUERY_HOST),
3622 FE(HTTP_QUERY_IF_MATCH),
3623 FE(HTTP_QUERY_IF_NONE_MATCH),
3624 FE(HTTP_QUERY_IF_RANGE),
3625 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3626 FE(HTTP_QUERY_MAX_FORWARDS),
3627 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3628 FE(HTTP_QUERY_RANGE),
3629 FE(HTTP_QUERY_TRANSFER_ENCODING),
3630 FE(HTTP_QUERY_UPGRADE),
3631 FE(HTTP_QUERY_VARY),
3632 FE(HTTP_QUERY_VIA),
3633 FE(HTTP_QUERY_WARNING),
3634 FE(HTTP_QUERY_CUSTOM)
3635 };
3636 static const wininet_flag_info modifier_flags[] = {
3637 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3638 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3639 FE(HTTP_QUERY_FLAG_NUMBER),
3640 FE(HTTP_QUERY_FLAG_COALESCE)
3641 };
3642 #undef FE
3643 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3644 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3645 DWORD i;
3646
3647 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3648 TRACE(" Attribute:");
3649 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3650 if (query_flags[i].val == info) {
3651 TRACE(" %s", query_flags[i].name);
3652 break;
3653 }
3654 }
3655 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3656 TRACE(" Unknown (%08x)", info);
3657 }
3658
3659 TRACE(" Modifier:");
3660 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3661 if (modifier_flags[i].val & info_mod) {
3662 TRACE(" %s", modifier_flags[i].name);
3663 info_mod &= ~ modifier_flags[i].val;
3664 }
3665 }
3666
3667 if (info_mod) {
3668 TRACE(" Unknown (%08x)", info_mod);
3669 }
3670 TRACE("\n");
3671 }
3672
3673 request = (http_request_t*) get_handle_object( hHttpRequest );
3674 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3675 {
3676 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3677 goto lend;
3678 }
3679
3680 if (lpBuffer == NULL)
3681 *lpdwBufferLength = 0;
3682 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3683 lpBuffer, lpdwBufferLength, lpdwIndex);
3684
3685 lend:
3686 if( request )
3687 WININET_Release( &request->hdr );
3688
3689 TRACE("%u <--\n", res);
3690 if(res != ERROR_SUCCESS)
3691 SetLastError(res);
3692 return res == ERROR_SUCCESS;
3693 }
3694
3695 /***********************************************************************
3696 * HttpQueryInfoA (WININET.@)
3697 *
3698 * Queries for information about an HTTP request
3699 *
3700 * RETURNS
3701 * TRUE on success
3702 * FALSE on failure
3703 *
3704 */
3705 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3706 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3707 {
3708 BOOL result;
3709 DWORD len;
3710 WCHAR* bufferW;
3711
3712 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3713 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3714 {
3715 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3716 lpdwBufferLength, lpdwIndex );
3717 }
3718
3719 if (lpBuffer)
3720 {
3721 DWORD alloclen;
3722 len = (*lpdwBufferLength)*sizeof(WCHAR);
3723 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3724 {
3725 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3726 if (alloclen < len)
3727 alloclen = len;
3728 }
3729 else
3730 alloclen = len;
3731 bufferW = heap_alloc(alloclen);
3732 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3733 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3734 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3735 } else
3736 {
3737 bufferW = NULL;
3738 len = 0;
3739 }
3740
3741 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3742 &len, lpdwIndex );
3743 if( result )
3744 {
3745 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3746 lpBuffer, *lpdwBufferLength, NULL, NULL );
3747 *lpdwBufferLength = len - 1;
3748
3749 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3750 }
3751 else
3752 /* since the strings being returned from HttpQueryInfoW should be
3753 * only ASCII characters, it is reasonable to assume that all of
3754 * the Unicode characters can be reduced to a single byte */
3755 *lpdwBufferLength = len / sizeof(WCHAR);
3756
3757 heap_free( bufferW );
3758 return result;
3759 }
3760
3761 /***********************************************************************
3762 * HTTP_GetRedirectURL (internal)
3763 */
3764 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3765 {
3766 static WCHAR szHttp[] = {'h','t','t','p',0};
3767 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3768 http_session_t *session = request->session;
3769 URL_COMPONENTSW urlComponents;
3770 DWORD url_length = 0;
3771 LPWSTR orig_url;
3772 LPWSTR combined_url;
3773
3774 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3775 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3776 urlComponents.dwSchemeLength = 0;
3777 urlComponents.lpszHostName = session->hostName;
3778 urlComponents.dwHostNameLength = 0;
3779 urlComponents.nPort = session->hostPort;
3780 urlComponents.lpszUserName = session->userName;
3781 urlComponents.dwUserNameLength = 0;
3782 urlComponents.lpszPassword = NULL;
3783 urlComponents.dwPasswordLength = 0;
3784 urlComponents.lpszUrlPath = request->path;
3785 urlComponents.dwUrlPathLength = 0;
3786 urlComponents.lpszExtraInfo = NULL;
3787 urlComponents.dwExtraInfoLength = 0;
3788
3789 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3790 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3791 return NULL;
3792
3793 orig_url = heap_alloc(url_length);
3794
3795 /* convert from bytes to characters */
3796 url_length = url_length / sizeof(WCHAR) - 1;
3797 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3798 {
3799 heap_free(orig_url);
3800 return NULL;
3801 }
3802
3803 url_length = 0;
3804 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3805 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3806 {
3807 heap_free(orig_url);
3808 return NULL;
3809 }
3810 combined_url = heap_alloc(url_length * sizeof(WCHAR));
3811
3812 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3813 {
3814 heap_free(orig_url);
3815 heap_free(combined_url);
3816 return NULL;
3817 }
3818 heap_free(orig_url);
3819 return combined_url;
3820 }
3821
3822
3823 /***********************************************************************
3824 * HTTP_HandleRedirect (internal)
3825 */
3826 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3827 {
3828 http_session_t *session = request->session;
3829 appinfo_t *hIC = session->appInfo;
3830 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3831 WCHAR path[INTERNET_MAX_PATH_LENGTH];
3832 int index;
3833
3834 if(lpszUrl[0]=='/')
3835 {
3836 /* if it's an absolute path, keep the same session info */
3837 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3838 }
3839 else
3840 {
3841 URL_COMPONENTSW urlComponents;
3842 WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
3843 WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
3844 WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3845 BOOL custom_port = FALSE;
3846
3847 static WCHAR httpW[] = {'h','t','t','p',0};
3848 static WCHAR httpsW[] = {'h','t','t','p','s',0};
3849
3850 userName[0] = 0;
3851 hostName[0] = 0;
3852 protocol[0] = 0;
3853
3854 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3855 urlComponents.lpszScheme = protocol;
3856 urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3857 urlComponents.lpszHostName = hostName;
3858 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3859 urlComponents.lpszUserName = userName;
3860 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3861 urlComponents.lpszPassword = NULL;
3862 urlComponents.dwPasswordLength = 0;
3863 urlComponents.lpszUrlPath = path;
3864 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3865 urlComponents.lpszExtraInfo = NULL;
3866 urlComponents.dwExtraInfoLength = 0;
3867 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3868 return INTERNET_GetLastError();
3869
3870 if(!strcmpiW(protocol, httpW)) {
3871 if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
3872 TRACE("redirect from secure page to non-secure page\n");
3873 /* FIXME: warn about from secure redirect to non-secure page */
3874 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3875 }
3876
3877 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3878 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3879 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT)
3880 custom_port = TRUE;
3881 }else if(!strcmpiW(protocol, httpsW)) {
3882 if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
3883 TRACE("redirect from non-secure page to secure page\n");
3884 /* FIXME: notify about redirect to secure page */
3885 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3886 }
3887
3888 if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3889 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3890 else if(urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3891 custom_port = TRUE;
3892 }
3893
3894 heap_free(session->hostName);
3895
3896 if(custom_port) {
3897 int len;
3898 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3899 len = lstrlenW(hostName);
3900 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3901 session->hostName = heap_alloc(len*sizeof(WCHAR));
3902 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3903 }
3904 else
3905 session->hostName = heap_strdupW(hostName);
3906
3907 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3908
3909 heap_free(session->userName);
3910 session->userName = NULL;
3911 if (userName[0])
3912 session->userName = heap_strdupW(userName);
3913
3914 reset_data_stream(request);
3915
3916 if(!using_proxy) {
3917 if(strcmpiW(session->serverName, hostName)) {
3918 heap_free(session->serverName);
3919 session->serverName = heap_strdupW(hostName);
3920 }
3921 session->serverPort = urlComponents.nPort;
3922 }
3923 }
3924 heap_free(request->path);
3925 request->path=NULL;
3926 if (*path)
3927 {
3928 DWORD needed = 0;
3929 HRESULT rc;
3930
3931 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3932 if (rc != E_POINTER)
3933 needed = strlenW(path)+1;
3934 request->path = heap_alloc(needed*sizeof(WCHAR));
3935 rc = UrlEscapeW(path, request->path, &needed,
3936 URL_ESCAPE_SPACES_ONLY);
3937 if (rc != S_OK)
3938 {
3939 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3940 strcpyW(request->path,path);
3941 }
3942 }
3943
3944 /* Remove custom content-type/length headers on redirects. */
3945 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3946 if (0 <= index)
3947 HTTP_DeleteCustomHeader(request, index);
3948 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3949 if (0 <= index)
3950 HTTP_DeleteCustomHeader(request, index);
3951
3952 return ERROR_SUCCESS;
3953 }
3954
3955 /***********************************************************************
3956 * HTTP_build_req (internal)
3957 *
3958 * concatenate all the strings in the request together
3959 */
3960 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3961 {
3962 LPCWSTR *t;
3963 LPWSTR str;
3964
3965 for( t = list; *t ; t++ )
3966 len += strlenW( *t );
3967 len++;
3968
3969 str = heap_alloc(len*sizeof(WCHAR));
3970 *str = 0;
3971
3972 for( t = list; *t ; t++ )
3973 strcatW( str, *t );
3974
3975 return str;
3976 }
3977
3978 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3979 {
3980 LPWSTR lpszPath;
3981 LPWSTR requestString;
3982 INT len;
3983 INT cnt;
3984 INT responseLen;
3985 char *ascii_req;
3986 DWORD res;
3987 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3988 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
3989 http_session_t *session = request->session;
3990
3991 TRACE("\n");
3992
3993 lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
3994 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
3995 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
3996 heap_free( lpszPath );
3997
3998 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3999 NULL, 0, NULL, NULL );
4000 len--; /* the nul terminator isn't needed */
4001 ascii_req = heap_alloc(len);
4002 WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
4003 heap_free( requestString );
4004
4005 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
4006
4007 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4008 res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
4009 heap_free( ascii_req );
4010 if (res != ERROR_SUCCESS)
4011 return res;
4012
4013 responseLen = HTTP_GetResponseHeaders( request, TRUE );
4014 if (!responseLen)
4015 return ERROR_HTTP_INVALID_HEADER;
4016
4017 return ERROR_SUCCESS;
4018 }
4019
4020 static void HTTP_InsertCookies(http_request_t *request)
4021 {
4022 DWORD cookie_size, size, cnt = 0;
4023 HTTPHEADERW *host;
4024 WCHAR *cookies;
4025
4026 static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
4027
4028 host = HTTP_GetHeader(request, hostW);
4029 if(!host)
4030 return;
4031
4032 if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
4033 return;
4034
4035 size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
4036 if(!(cookies = heap_alloc(size)))
4037 return;
4038
4039 cnt += sprintfW(cookies, cookieW);
4040 get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
4041 strcatW(cookies, szCrLf);
4042
4043 HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
4044
4045 heap_free(cookies);
4046 }
4047
4048 static WORD HTTP_ParseWkday(LPCWSTR day)
4049 {
4050 static const WCHAR days[7][4] = {{ 's','u','n',0 },
4051 { 'm','o','n',0 },
4052 { 't','u','e',0 },
4053 { 'w','e','d',0 },
4054 { 't','h','u',0 },
4055 { 'f','r','i',0 },
4056 { 's','a','t',0 }};
4057 int i;
4058 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4059 if (!strcmpiW(day, days[i]))
4060 return i;
4061
4062 /* Invalid */
4063 return 7;
4064 }
4065
4066 static WORD HTTP_ParseMonth(LPCWSTR month)
4067 {
4068 static const WCHAR jan[] = { 'j','a','n',0 };
4069 static const WCHAR feb[] = { 'f','e','b',0 };
4070 static const WCHAR mar[] = { 'm','a','r',0 };
4071 static const WCHAR apr[] = { 'a','p','r',0 };
4072 static const WCHAR may[] = { 'm','a','y',0 };
4073 static const WCHAR jun[] = { 'j','u','n',0 };
4074 static const WCHAR jul[] = { 'j','u','l',0 };
4075 static const WCHAR aug[] = { 'a','u','g',0 };
4076 static const WCHAR sep[] = { 's','e','p',0 };
4077 static const WCHAR oct[] = { 'o','c','t',0 };
4078 static const WCHAR nov[] = { 'n','o','v',0 };
4079 static const WCHAR dec[] = { 'd','e','c',0 };
4080
4081 if (!strcmpiW(month, jan)) return 1;
4082 if (!strcmpiW(month, feb)) return 2;
4083 if (!strcmpiW(month, mar)) return 3;
4084 if (!strcmpiW(month, apr)) return 4;
4085 if (!strcmpiW(month, may)) return 5;
4086 if (!strcmpiW(month, jun)) return 6;
4087 if (!strcmpiW(month, jul)) return 7;
4088 if (!strcmpiW(month, aug)) return 8;
4089 if (!strcmpiW(month, sep)) return 9;
4090 if (!strcmpiW(month, oct)) return 10;
4091 if (!strcmpiW(month, nov)) return 11;
4092 if (!strcmpiW(month, dec)) return 12;
4093 /* Invalid */
4094 return 0;
4095 }
4096
4097 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4098 * optionally preceded by whitespace.
4099 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4100 * st, and sets *str to the first character after the time format.
4101 */
4102 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4103 {
4104 LPCWSTR ptr = *str;
4105 WCHAR *nextPtr;
4106 unsigned long num;
4107
4108 while (isspaceW(*ptr))
4109 ptr++;
4110
4111 num = strtoulW(ptr, &nextPtr, 10);
4112 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4113 {
4114 ERR("unexpected time format %s\n", debugstr_w(ptr));
4115 return FALSE;
4116 }
4117 if (num > 23)
4118 {
4119 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4120 return FALSE;
4121 }
4122 ptr = nextPtr + 1;
4123 st->wHour = (WORD)num;
4124 num = strtoulW(ptr, &nextPtr, 10);
4125 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4126 {
4127 ERR("unexpected time format %s\n", debugstr_w(ptr));
4128 return FALSE;
4129 }
4130 if (num > 59)
4131 {
4132 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
4133 return FALSE;
4134 }
4135 ptr = nextPtr + 1;
4136 st->wMinute = (WORD)num;
4137 num = strtoulW(ptr, &nextPtr, 10);
4138 if (!nextPtr || nextPtr <= ptr)
4139 {
4140 ERR("unexpected time format %s\n", debugstr_w(ptr));
4141 return FALSE;
4142 }
4143 if (num > 59)
4144 {
4145 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
4146 return FALSE;
4147 }
4148 ptr = nextPtr + 1;
4149 *str = ptr;
4150 st->wSecond = (WORD)num;
4151 return TRUE;
4152 }
4153
4154 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
4155 {
4156 static const WCHAR gmt[]= { 'G','M','T',0 };
4157 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
4158 LPCWSTR ptr;
4159 SYSTEMTIME st = { 0 };
4160 unsigned long num;
4161
4162 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
4163 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
4164 *dayPtr = *ptr;
4165 *dayPtr = 0;
4166 st.wDayOfWeek = HTTP_ParseWkday(day);
4167 if (st.wDayOfWeek >= 7)
4168 {
4169 ERR("unexpected weekday %s\n", debugstr_w(day));
4170 return FALSE;
4171 }
4172
4173 while (isspaceW(*ptr))
4174 ptr++;
4175
4176 for (monthPtr = month; !isspace(*ptr) &&
4177 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4178 monthPtr++, ptr++)
4179 *monthPtr = *ptr;
4180 *monthPtr = 0;
4181 st.wMonth = HTTP_ParseMonth(month);
4182 if (!st.wMonth || st.wMonth > 12)
4183 {
4184 ERR("unexpected month %s\n", debugstr_w(month));
4185 return FALSE;
4186 }
4187
4188 while (isspaceW(*ptr))
4189 ptr++;
4190
4191 num = strtoulW(ptr, &nextPtr, 10);
4192 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4193 {
4194 ERR("unexpected day %s\n", debugstr_w(ptr));
4195 return FALSE;
4196 }
4197 ptr = nextPtr;
4198 st.wDay = (WORD)num;
4199
4200 while (isspaceW(*ptr))
4201 ptr++;
4202
4203 if (!HTTP_ParseTime(&st, &ptr))
4204 return FALSE;
4205
4206 while (isspaceW(*ptr))
4207 ptr++;
4208
4209 num = strtoulW(ptr, &nextPtr, 10);
4210 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4211 {
4212 ERR("unexpected year %s\n", debugstr_w(ptr));
4213 return FALSE;
4214 }
4215 ptr = nextPtr;
4216 st.wYear = (WORD)num;
4217
4218 while (isspaceW(*ptr))
4219 ptr++;
4220
4221 /* asctime() doesn't report a timezone, but some web servers do, so accept
4222 * with or without GMT.
4223 */
4224 if (*ptr && strcmpW(ptr, gmt))
4225 {
4226 ERR("unexpected timezone %s\n", debugstr_w(ptr));
4227 return FALSE;
4228 }
4229 return SystemTimeToFileTime(&st, ft);
4230 }
4231
4232 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
4233 {
4234 static const WCHAR gmt[]= { 'G','M','T',0 };
4235 WCHAR *nextPtr, day[4], month[4], *monthPtr;
4236 LPCWSTR ptr;
4237 unsigned long num;
4238 SYSTEMTIME st = { 0 };
4239
4240 ptr = strchrW(value, ',');
4241 if (!ptr)
4242 return FALSE;
4243 if (ptr - value != 3)
4244 {
4245 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4246 return FALSE;
4247 }
4248 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4249 day[3] = 0;
4250 st.wDayOfWeek = HTTP_ParseWkday(day);
4251 if (st.wDayOfWeek > 6)
4252 {
4253 WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4254 return FALSE;
4255 }
4256 ptr++;
4257
4258 while (isspaceW(*ptr))
4259 ptr++;
4260
4261 num = strtoulW(ptr, &nextPtr, 10);
4262 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4263 {
4264 WARN("unexpected day %s\n", debugstr_w(value));
4265 return FALSE;
4266 }
4267 ptr = nextPtr;
4268 st.wDay = (WORD)num;
4269
4270 while (isspaceW(*ptr))
4271 ptr++;
4272
4273 for (monthPtr = month; !isspace(*ptr) &&
4274 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4275 monthPtr++, ptr++)
4276 *monthPtr = *ptr;
4277 *monthPtr = 0;
4278 st.wMonth = HTTP_ParseMonth(month);
4279 if (!st.wMonth || st.wMonth > 12)
4280 {
4281 WARN("unexpected month %s\n", debugstr_w(month));
4282 return FALSE;
4283 }
4284
4285 while (isspaceW(*ptr))
4286 ptr++;
4287
4288 num = strtoulW(ptr, &nextPtr, 10);
4289 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4290 {
4291 ERR("unexpected year %s\n", debugstr_w(value));
4292 return FALSE;
4293 }
4294 ptr = nextPtr;
4295 st.wYear = (WORD)num;
4296
4297 if (!HTTP_ParseTime(&st, &ptr))
4298 return FALSE;
4299
4300 while (isspaceW(*ptr))
4301 ptr++;
4302
4303 if (strcmpW(ptr, gmt))
4304 {
4305 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4306 return FALSE;
4307 }
4308 return SystemTimeToFileTime(&st, ft);
4309 }
4310
4311 static WORD HTTP_ParseWeekday(LPCWSTR day)
4312 {
4313 static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
4314 { 'm','o','n','d','a','y',0 },
4315 { 't','u','e','s','d','a','y',0 },
4316 { 'w','e','d','n','e','s','d','a','y',0 },
4317 { 't','h','u','r','s','d','a','y',0 },
4318 { 'f','r','i','d','a','y',0 },
4319 { 's','a','t','u','r','d','a','y',0 }};
4320 int i;
4321 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
4322 if (!strcmpiW(day, days[i]))
4323 return i;
4324
4325 /* Invalid */
4326 return 7;
4327 }
4328
4329 static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
4330 {
4331 static const WCHAR gmt[]= { 'G','M','T',0 };
4332 WCHAR *nextPtr, day[10], month[4], *monthPtr;
4333 LPCWSTR ptr;
4334 unsigned long num;
4335 SYSTEMTIME st = { 0 };
4336
4337 ptr = strchrW(value, ',');
4338 if (!ptr)
4339 return FALSE;
4340 if (ptr - value == 3)
4341 {
4342 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4343 day[3] = 0;
4344 st.wDayOfWeek = HTTP_ParseWkday(day);
4345 if (st.wDayOfWeek > 6)
4346 {
4347 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4348 return FALSE;
4349 }
4350 }
4351 else if (ptr - value < sizeof(day) / sizeof(day[0]))
4352 {
4353 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4354 day[ptr - value + 1] = 0;
4355 st.wDayOfWeek = HTTP_ParseWeekday(day);
4356 if (st.wDayOfWeek > 6)
4357 {
4358 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4359 return FALSE;
4360 }
4361 }
4362 else
4363 {
4364 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4365 return FALSE;
4366 }
4367 ptr++;
4368
4369 while (isspaceW(*ptr))
4370 ptr++;
4371
4372 num = strtoulW(ptr, &nextPtr, 10);
4373 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4374 {
4375 ERR("unexpected day %s\n", debugstr_w(value));
4376 return FALSE;
4377 }
4378 ptr = nextPtr;
4379 st.wDay = (WORD)num;
4380
4381 if (*ptr != '-')
4382 {
4383 ERR("unexpected month format %s\n", debugstr_w(ptr));
4384 return FALSE;
4385 }
4386 ptr++;
4387
4388 for (monthPtr = month; *ptr != '-' &&
4389 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4390 monthPtr++, ptr++)
4391 *monthPtr = *ptr;
4392 *monthPtr = 0;
4393 st.wMonth = HTTP_ParseMonth(month);
4394 if (!st.wMonth || st.wMonth > 12)
4395 {
4396 ERR("unexpected month %s\n", debugstr_w(month));
4397 return FALSE;
4398 }
4399
4400 if (*ptr != '-')
4401 {
4402 ERR("unexpected year format %s\n", debugstr_w(ptr));
4403 return FALSE;
4404 }
4405 ptr++;
4406
4407 num = strtoulW(ptr, &nextPtr, 10);
4408 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4409 {
4410 ERR("unexpected year %s\n", debugstr_w(value));
4411 return FALSE;
4412 }
4413 ptr = nextPtr;
4414 st.wYear = (WORD)num;
4415
4416 if (!HTTP_ParseTime(&st, &ptr))
4417 return FALSE;
4418
4419 while (isspaceW(*ptr))
4420 ptr++;
4421
4422 if (strcmpW(ptr, gmt))
4423 {
4424 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4425 return FALSE;
4426 }
4427 return SystemTimeToFileTime(&st, ft);
4428 }
4429
4430 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4431 {
4432 static const WCHAR zero[] = { '0',0 };
4433 BOOL ret;
4434
4435 if (!strcmpW(value, zero))
4436 {
4437 ft->dwLowDateTime = ft->dwHighDateTime = 0;
4438 ret = TRUE;
4439 }
4440 else if (strchrW(value, ','))
4441 {
4442 ret = HTTP_ParseRfc1123Date(value, ft);
4443 if (!ret)
4444 {
4445 ret = HTTP_ParseRfc850Date(value, ft);
4446 if (!ret)
4447 ERR("unexpected date format %s\n", debugstr_w(value));
4448 }
4449 }
4450 else
4451 {
4452 ret = HTTP_ParseDateAsAsctime(value, ft);
4453 if (!ret)
4454 ERR("unexpected date format %s\n", debugstr_w(value));
4455 }
4456 return ret;
4457 }
4458
4459 static void HTTP_ProcessExpires(http_request_t *request)
4460 {
4461 BOOL expirationFound = FALSE;
4462 int headerIndex;
4463
4464 /* Look for a Cache-Control header with a max-age directive, as it takes
4465 * precedence over the Expires header.
4466 */
4467 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4468 if (headerIndex != -1)
4469 {
4470 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4471 LPWSTR ptr;
4472
4473 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4474 {
4475 LPWSTR comma = strchrW(ptr, ','), end, equal;
4476
4477 if (comma)
4478 end = comma;
4479 else
4480 end = ptr + strlenW(ptr);
4481 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4482 ;
4483 if (*equal == '=')
4484 {
4485 static const WCHAR max_age[] = {
4486 'm','a','x','-','a','g','e',0 };
4487
4488 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4489 {
4490 LPWSTR nextPtr;
4491 unsigned long age;
4492
4493 age = strtoulW(equal + 1, &nextPtr, 10);
4494 if (nextPtr > equal + 1)
4495 {
4496 LARGE_INTEGER ft;
4497
4498 NtQuerySystemTime( &ft );
4499 /* Age is in seconds, FILETIME resolution is in
4500 * 100 nanosecond intervals.
4501 */
4502 ft.QuadPart += age * (ULONGLONG)1000000;
4503 request->expires.dwLowDateTime = ft.u.LowPart;
4504 request->expires.dwHighDateTime = ft.u.HighPart;
4505 expirationFound = TRUE;
4506 }
4507 }
4508 }
4509 if (comma)
4510 {
4511 ptr = comma + 1;
4512 while (isspaceW(*ptr))
4513 ptr++;
4514 }
4515 else
4516 ptr = NULL;
4517 }
4518 }
4519 if (!expirationFound)
4520 {
4521 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4522 if (headerIndex != -1)
4523 {
4524 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4525 FILETIME ft;
4526
4527 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4528 {
4529 expirationFound = TRUE;
4530 request->expires = ft;
4531 }
4532 }
4533 }
4534 if (!expirationFound)
4535 {
4536 LARGE_INTEGER t;
4537
4538 /* With no known age, default to 10 minutes until expiration. */
4539 NtQuerySystemTime( &t );
4540 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4541 request->expires.dwLowDateTime = t.u.LowPart;
4542 request->expires.dwHighDateTime = t.u.HighPart;
4543 }
4544 }
4545
4546 static void HTTP_ProcessLastModified(http_request_t *request)
4547 {
4548 int headerIndex;
4549
4550 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4551 if (headerIndex != -1)
4552 {
4553 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4554 FILETIME ft;
4555
4556 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4557 request->last_modified = ft;
4558 }
4559 }
4560
4561 static void http_process_keep_alive(http_request_t *req)
4562 {
4563 int index;
4564
4565 index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
4566 if(index != -1)
4567 req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
4568 else
4569 req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
4570 }
4571
4572 static void HTTP_CacheRequest(http_request_t *request)
4573 {
4574 WCHAR url[INTERNET_MAX_URL_LENGTH];
4575 WCHAR cacheFileName[MAX_PATH+1];
4576 BOOL b;
4577
4578 b = HTTP_GetRequestURL(request, url);
4579 if(!b) {
4580 WARN("Could not get URL\n");
4581 return;
4582 }
4583
4584 b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4585 if(b) {
4586 heap_free(request->cacheFile);
4587 CloseHandle(request->hCacheFile);
4588
4589 request->cacheFile = heap_strdupW(cacheFileName);
4590 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4591 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4592 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4593 WARN("Could not create file: %u\n", GetLastError());
4594 request->hCacheFile = NULL;
4595 }
4596 }else {
4597 WARN("Could not create cache entry: %08x\n", GetLastError());
4598 }
4599 }
4600
4601 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
4602 {
4603 const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
4604 http_session_t *session = request->session;
4605 netconn_t *netconn = NULL;
4606 server_t *server;
4607 DWORD res;
4608
4609 assert(!request->netconn);
4610 reset_data_stream(request);
4611
4612 server = get_server(session->serverName, session->serverPort);
4613 if(!server)
4614 return ERROR_OUTOFMEMORY;
4615
4616 res = HTTP_ResolveName(request, server);
4617 if(res != ERROR_SUCCESS) {
4618 server_release(server);
4619 return res;
4620 }
4621
4622 EnterCriticalSection(&connection_pool_cs);
4623
4624 while(!list_empty(&server->conn_pool)) {
4625 netconn = LIST_ENTRY(list_head(&server->conn_pool), netconn_t, pool_entry);
4626 list_remove(&netconn->pool_entry);
4627
4628 if(NETCON_is_alive(netconn))
4629 break;
4630
4631 TRACE("connection %p closed during idle\n", netconn);
4632 free_netconn(netconn);
4633 netconn = NULL;
4634 }
4635
4636 LeaveCriticalSection(&connection_pool_cs);
4637
4638 if(netconn) {
4639 TRACE("<-- reusing %p netconn\n", netconn);
4640 request->netconn = netconn;
4641 *reusing = TRUE;
4642 return ERROR_SUCCESS;
4643 }
4644
4645 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4646 INTERNET_STATUS_CONNECTING_TO_SERVER,
4647 server->addr_str,
4648 strlen(server->addr_str)+1);
4649
4650 res = create_netconn(is_https, server, request->security_flags, request->connect_timeout, &netconn);
4651 server_release(server);
4652 if(res != ERROR_SUCCESS) {
4653 ERR("create_netconn failed: %u\n", res);
4654 return res;
4655 }
4656
4657 request->netconn = netconn;
4658
4659 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4660 INTERNET_STATUS_CONNECTED_TO_SERVER,
4661 server->addr_str, strlen(server->addr_str)+1);
4662
4663 if(is_https) {
4664 /* Note: we differ from Microsoft's WinINet here. they seem to have
4665 * a bug that causes no status callbacks to be sent when starting
4666 * a tunnel to a proxy server using the CONNECT verb. i believe our
4667 * behaviour to be more correct and to not cause any incompatibilities
4668 * because using a secure connection through a proxy server is a rare
4669 * case that would be hard for anyone to depend on */
4670 if(session->appInfo->proxy)
4671 res = HTTP_SecureProxyConnect(request);
4672 if(res == ERROR_SUCCESS)
4673 res = NETCON_secure_connect(request->netconn);
4674 if(res != ERROR_SUCCESS)
4675 {
4676 WARN("Couldn't connect securely to host\n");
4677
4678 if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4679 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4680 || res == ERROR_INTERNET_INVALID_CA
4681 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4682 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4683 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4684 || res == ERROR_INTERNET_SEC_INVALID_CERT
4685 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4686 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4687 }
4688 }
4689
4690 if(res != ERROR_SUCCESS) {
4691 http_release_netconn(request, FALSE);
4692 return res;
4693 }
4694
4695 *reusing = FALSE;
4696 TRACE("Created connection to %s: %p\n", debugstr_w(server->name), netconn);
4697 return ERROR_SUCCESS;
4698 }
4699
4700 /***********************************************************************
4701 * HTTP_HttpSendRequestW (internal)
4702 *
4703 * Sends the specified request to the HTTP server
4704 *
4705 * RETURNS
4706 * ERROR_SUCCESS on success
4707 * win32 error code on failure
4708 *
4709 */
4710 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4711 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4712 DWORD dwContentLength, BOOL bEndRequest)
4713 {
4714 INT cnt;
4715 BOOL redirected = FALSE;
4716 LPWSTR requestString = NULL;
4717 INT responseLen;
4718 BOOL loop_next;
4719 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4720 static const WCHAR szContentLength[] =
4721 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4722 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4723 DWORD res;
4724
4725 TRACE("--> %p\n", request);
4726
4727 assert(request->hdr.htype == WH_HHTTPREQ);
4728
4729 /* if the verb is NULL default to GET */
4730 if (!request->verb)
4731 request->verb = heap_strdupW(szGET);
4732
4733 if (dwContentLength || strcmpW(request->verb, szGET))
4734 {
4735 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4736 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4737 request->bytesToWrite = dwContentLength;
4738 }
4739 if (request->session->appInfo->agent)
4740 {
4741 WCHAR *agent_header;
4742 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4743 int len;
4744
4745 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4746 agent_header = heap_alloc(len * sizeof(WCHAR));
4747 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4748
4749 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4750 heap_free(agent_header);
4751 }
4752 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4753 {
4754 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4755 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4756 }
4757 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4758 {
4759 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4760 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4761 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4762 }
4763
4764 /* add the headers the caller supplied */
4765 if( lpszHeaders && dwHeaderLength )
4766 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4767
4768 do
4769 {
4770 DWORD len;
4771 BOOL reusing_connection;
4772 char *ascii_req;
4773
4774 loop_next = FALSE;
4775
4776 /* like native, just in case the caller forgot to call InternetReadFile
4777 * for all the data */
4778 drain_content(request);
4779 if(redirected) {
4780 request->contentLength = ~0u;
4781 request->bytesToWrite = 0;
4782 }
4783
4784 if (TRACE_ON(wininet))
4785 {
4786 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4787 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4788 }
4789
4790 HTTP_FixURL(request);
4791 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4792 {
4793 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4794 }
4795 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4796 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4797
4798 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4799 HTTP_InsertCookies(request);
4800
4801 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4802 {
4803 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4804 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4805 heap_free(url);
4806 }
4807 else
4808 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4809
4810
4811 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4812
4813 if ((res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4814 break;
4815
4816 /* send the request as ASCII, tack on the optional data */
4817 if (!lpOptional || redirected)
4818 dwOptionalLength = 0;
4819 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4820 NULL, 0, NULL, NULL );
4821 ascii_req = heap_alloc(len + dwOptionalLength);
4822 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4823 ascii_req, len, NULL, NULL );
4824 if( lpOptional )
4825 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4826 len = (len + dwOptionalLength - 1);
4827 ascii_req[len] = 0;
4828 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4829
4830 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4831 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4832
4833 NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4834 res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4835 heap_free( ascii_req );
4836 if(res != ERROR_SUCCESS) {
4837 TRACE("send failed: %u\n", res);
4838 if(!reusing_connection)
4839 break;
4840 http_release_netconn(request, FALSE);
4841 loop_next = TRUE;
4842 continue;
4843 }
4844
4845 request->bytesWritten = dwOptionalLength;
4846
4847 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4848 INTERNET_STATUS_REQUEST_SENT,
4849 &len, sizeof(DWORD));
4850
4851 if (bEndRequest)
4852 {
4853 DWORD dwBufferSize;
4854
4855 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4856 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4857
4858 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4859 /* FIXME: We should know that connection is closed before sending
4860 * headers. Otherwise wrong callbacks are executed */
4861 if(!responseLen && reusing_connection) {
4862 TRACE("Connection closed by server, reconnecting\n");
4863 http_release_netconn(request, FALSE);
4864 loop_next = TRUE;
4865 continue;
4866 }
4867
4868 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4869 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4870 sizeof(DWORD));
4871
4872 http_process_keep_alive(request);
4873 HTTP_ProcessCookies(request);
4874 HTTP_ProcessExpires(request);
4875 HTTP_ProcessLastModified(request);
4876
4877 res = set_content_length(request);
4878 if(res != ERROR_SUCCESS)
4879 goto lend;
4880 if(!request->contentLength)
4881 http_release_netconn(request, TRUE);
4882
4883 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4884 {
4885 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4886 dwBufferSize=sizeof(szNewLocation);
4887 switch(request->status_code) {
4888 case HTTP_STATUS_REDIRECT:
4889 case HTTP_STATUS_MOVED:
4890 case HTTP_STATUS_REDIRECT_KEEP_VERB:
4891 case HTTP_STATUS_REDIRECT_METHOD:
4892 if(HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) != ERROR_SUCCESS)
4893 break;
4894
4895 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4896 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
4897 {
4898 heap_free(request->verb);
4899 request->verb = heap_strdupW(szGET);
4900 }
4901 drain_content(request);
4902 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4903 {
4904 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4905 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4906 res = HTTP_HandleRedirect(request, new_url);
4907 if (res == ERROR_SUCCESS)
4908 {
4909 heap_free(requestString);
4910 loop_next = TRUE;
4911 }
4912 heap_free( new_url );
4913 }
4914 redirected = TRUE;
4915 }
4916 }
4917 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4918 {
4919 WCHAR szAuthValue[2048];
4920 dwBufferSize=2048;
4921 if (request->status_code == HTTP_STATUS_DENIED)
4922 {
4923 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4924 DWORD dwIndex = 0;
4925 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4926 {
4927 if (HTTP_DoAuthorization(request, szAuthValue,
4928 &request->authInfo,
4929 request->session->userName,
4930 request->session->password,
4931 Host->lpszValue))
4932 {
4933 heap_free(requestString);
4934 loop_next = TRUE;
4935 break;
4936 }
4937 }
4938
4939 if(!loop_next) {
4940 TRACE("Cleaning wrong authorization data\n");
4941 destroy_authinfo(request->authInfo);
4942 request->authInfo = NULL;
4943 }
4944 }
4945 if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
4946 {
4947 DWORD dwIndex = 0;
4948 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4949 {
4950 if (HTTP_DoAuthorization(request, szAuthValue,
4951 &request->proxyAuthInfo,
4952 request->session->appInfo->proxyUsername,
4953 request->session->appInfo->proxyPassword,
4954 NULL))
4955 {
4956 loop_next = TRUE;
4957 break;
4958 }
4959 }
4960
4961 if(!loop_next) {
4962 TRACE("Cleaning wrong proxy authorization data\n");
4963 destroy_authinfo(request->proxyAuthInfo);
4964 request->proxyAuthInfo = NULL;
4965 }
4966 }
4967 }
4968 }
4969 else
4970 res = ERROR_SUCCESS;
4971 }
4972 while (loop_next);
4973
4974 if(res == ERROR_SUCCESS)
4975 HTTP_CacheRequest(request);
4976
4977 lend:
4978 heap_free(requestString);
4979
4980 /* TODO: send notification for P3P header */
4981
4982 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4983 {
4984 if (res == ERROR_SUCCESS) {
4985 if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
4986 HTTP_ReceiveRequestData(request, TRUE);
4987 else
4988 send_request_complete(request,
4989 request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
4990 }else {
4991 send_request_complete(request, 0, res);
4992 }
4993 }
4994
4995 TRACE("<--\n");
4996 return res;
4997 }
4998
4999 /***********************************************************************
5000 *
5001 * Helper functions for the HttpSendRequest(Ex) functions
5002 *
5003 */
5004 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
5005 {
5006 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
5007 http_request_t *request = (http_request_t*) workRequest->hdr;
5008
5009 TRACE("%p\n", request);
5010
5011 HTTP_HttpSendRequestW(request, req->lpszHeader,
5012 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
5013 req->dwContentLength, req->bEndRequest);
5014
5015 heap_free(req->lpszHeader);
5016 }
5017
5018
5019 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
5020 {
5021 DWORD dwBufferSize;
5022 INT responseLen;
5023 DWORD res = ERROR_SUCCESS;
5024
5025 if(!request->netconn) {
5026 WARN("Not connected\n");
5027 send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
5028 return ERROR_INTERNET_OPERATION_CANCELLED;
5029 }
5030
5031 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5032 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
5033
5034 responseLen = HTTP_GetResponseHeaders(request, TRUE);
5035 if (!responseLen)
5036 res = ERROR_HTTP_HEADER_NOT_FOUND;
5037
5038 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5039 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
5040
5041 /* process cookies here. Is this right? */
5042 http_process_keep_alive(request);
5043 HTTP_ProcessCookies(request);
5044 HTTP_ProcessExpires(request);
5045 HTTP_ProcessLastModified(request);
5046
5047 if ((res = set_content_length(request)) == ERROR_SUCCESS) {
5048 if(!request->contentLength)
5049 http_release_netconn(request, TRUE);
5050 }
5051
5052 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
5053 {
5054 switch(request->status_code) {
5055 case HTTP_STATUS_REDIRECT:
5056 case HTTP_STATUS_MOVED:
5057 case HTTP_STATUS_REDIRECT_METHOD:
5058 case HTTP_STATUS_REDIRECT_KEEP_VERB: {
5059 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
5060 dwBufferSize=sizeof(szNewLocation);
5061 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) != ERROR_SUCCESS)
5062 break;
5063
5064 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
5065 request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5066 {
5067 heap_free(request->verb);
5068 request->verb = heap_strdupW(szGET);
5069 }
5070 drain_content(request);
5071 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
5072 {
5073 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
5074 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
5075 res = HTTP_HandleRedirect(request, new_url);
5076 if (res == ERROR_SUCCESS)
5077 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
5078 heap_free( new_url );
5079 }
5080 }
5081 }
5082 }
5083
5084 if (res == ERROR_SUCCESS && request->contentLength)
5085 HTTP_ReceiveRequestData(request, TRUE);
5086 else
5087 send_request_complete(request, res == ERROR_SUCCESS, res);
5088
5089 return res;
5090 }
5091
5092 /***********************************************************************
5093 * HttpEndRequestA (WININET.@)
5094 *
5095 * Ends an HTTP request that was started by HttpSendRequestEx
5096 *
5097 * RETURNS
5098 * TRUE if successful
5099 * FALSE on failure
5100 *
5101 */
5102 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
5103 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5104 {
5105 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5106
5107 if (lpBuffersOut)
5108 {
5109 SetLastError(ERROR_INVALID_PARAMETER);
5110 return FALSE;
5111 }
5112
5113 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
5114 }
5115
5116 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
5117 {
5118 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
5119 http_request_t *request = (http_request_t*)work->hdr;
5120
5121 TRACE("%p\n", request);
5122
5123 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
5124 }
5125
5126 /***********************************************************************
5127 * HttpEndRequestW (WININET.@)
5128 *
5129 * Ends an HTTP request that was started by HttpSendRequestEx
5130 *
5131 * RETURNS
5132 * TRUE if successful
5133 * FALSE on failure
5134 *
5135 */
5136 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
5137 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
5138 {
5139 http_request_t *request;
5140 DWORD res;
5141
5142 TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5143
5144 if (lpBuffersOut)
5145 {
5146 SetLastError(ERROR_INVALID_PARAMETER);
5147 return FALSE;
5148 }
5149
5150 request = (http_request_t*) get_handle_object( hRequest );
5151
5152 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5153 {
5154 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5155 if (request)
5156 WININET_Release( &request->hdr );
5157 return FALSE;
5158 }
5159 request->hdr.dwFlags |= dwFlags;
5160
5161 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5162 {
5163 WORKREQUEST work;
5164 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5165
5166 work.asyncproc = AsyncHttpEndRequestProc;
5167 work.hdr = WININET_AddRef( &request->hdr );
5168
5169 work_endrequest = &work.u.HttpEndRequestW;
5170 work_endrequest->dwFlags = dwFlags;
5171 work_endrequest->dwContext = dwContext;
5172
5173 INTERNET_AsyncCall(&work);
5174 res = ERROR_IO_PENDING;
5175 }
5176 else
5177 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5178
5179 WININET_Release( &request->hdr );
5180 TRACE("%u <--\n", res);
5181 if(res != ERROR_SUCCESS)
5182 SetLastError(res);
5183 return res == ERROR_SUCCESS;
5184 }
5185
5186 /***********************************************************************
5187 * HttpSendRequestExA (WININET.@)
5188 *
5189 * Sends the specified request to the HTTP server and allows chunked
5190 * transfers.
5191 *
5192 * RETURNS
5193 * Success: TRUE
5194 * Failure: FALSE, call GetLastError() for more information.
5195 */
5196 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
5197 LPINTERNET_BUFFERSA lpBuffersIn,
5198 LPINTERNET_BUFFERSA lpBuffersOut,
5199 DWORD dwFlags, DWORD_PTR dwContext)
5200 {
5201 INTERNET_BUFFERSW BuffersInW;
5202 BOOL rc = FALSE;
5203 DWORD headerlen;
5204 LPWSTR header = NULL;
5205
5206 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5207 lpBuffersOut, dwFlags, dwContext);
5208
5209 if (lpBuffersIn)
5210 {
5211 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
5212 if (lpBuffersIn->lpcszHeader)
5213 {
5214 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
5215 lpBuffersIn->dwHeadersLength,0,0);
5216 header = heap_alloc(headerlen*sizeof(WCHAR));
5217 if (!(BuffersInW.lpcszHeader = header))
5218 {
5219 SetLastError(ERROR_OUTOFMEMORY);
5220 return FALSE;
5221 }
5222 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
5223 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5224 header, headerlen);
5225 }
5226 else
5227 BuffersInW.lpcszHeader = NULL;
5228 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
5229 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
5230 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
5231 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
5232 BuffersInW.Next = NULL;
5233 }
5234
5235 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
5236
5237 heap_free(header);
5238 return rc;
5239 }
5240
5241 /***********************************************************************
5242 * HttpSendRequestExW (WININET.@)
5243 *
5244 * Sends the specified request to the HTTP server and allows chunked
5245 * transfers
5246 *
5247 * RETURNS
5248 * Success: TRUE
5249 * Failure: FALSE, call GetLastError() for more information.
5250 */
5251 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
5252 LPINTERNET_BUFFERSW lpBuffersIn,
5253 LPINTERNET_BUFFERSW lpBuffersOut,
5254 DWORD dwFlags, DWORD_PTR dwContext)
5255 {
5256 http_request_t *request;
5257 http_session_t *session;
5258 appinfo_t *hIC;
5259 DWORD res;
5260
5261 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
5262 lpBuffersOut, dwFlags, dwContext);
5263
5264 request = (http_request_t*) get_handle_object( hRequest );
5265
5266 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5267 {
5268 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5269 goto lend;
5270 }
5271
5272 session = request->session;
5273 assert(session->hdr.htype == WH_HHTTPSESSION);
5274 hIC = session->appInfo;
5275 assert(hIC->hdr.htype == WH_HINIT);
5276
5277 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5278 {
5279 WORKREQUEST workRequest;
5280 struct WORKREQ_HTTPSENDREQUESTW *req;
5281
5282 workRequest.asyncproc = AsyncHttpSendRequestProc;
5283 workRequest.hdr = WININET_AddRef( &request->hdr );
5284 req = &workRequest.u.HttpSendRequestW;
5285 if (lpBuffersIn)
5286 {
5287 DWORD size = 0;
5288
5289 if (lpBuffersIn->lpcszHeader)
5290 {
5291 if (lpBuffersIn->dwHeadersLength == ~0u)
5292 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
5293 else
5294 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
5295
5296 req->lpszHeader = heap_alloc(size);
5297 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
5298 }
5299 else req->lpszHeader = NULL;
5300
5301 req->dwHeaderLength = size / sizeof(WCHAR);
5302 req->lpOptional = lpBuffersIn->lpvBuffer;
5303 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
5304 req->dwContentLength = lpBuffersIn->dwBufferTotal;
5305 }
5306 else
5307 {
5308 req->lpszHeader = NULL;
5309 req->dwHeaderLength = 0;
5310 req->lpOptional = NULL;
5311 req->dwOptionalLength = 0;
5312 req->dwContentLength = 0;
5313 }
5314
5315 req->bEndRequest = FALSE;
5316
5317 INTERNET_AsyncCall(&workRequest);
5318 /*
5319 * This is from windows.
5320 */
5321 res = ERROR_IO_PENDING;
5322 }
5323 else
5324 {
5325 if (lpBuffersIn)
5326 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5327 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
5328 lpBuffersIn->dwBufferTotal, FALSE);
5329 else
5330 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5331 }
5332
5333 lend:
5334 if ( request )
5335 WININET_Release( &request->hdr );
5336
5337 TRACE("<---\n");
5338 SetLastError(res);
5339 return res == ERROR_SUCCESS;
5340 }
5341
5342 /***********************************************************************
5343 * HttpSendRequestW (WININET.@)
5344 *
5345 * Sends the specified request to the HTTP server
5346 *
5347 * RETURNS
5348 * TRUE on success
5349 * FALSE on failure
5350 *
5351 */
5352 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
5353 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5354 {
5355 http_request_t *request;
5356 http_session_t *session = NULL;
5357 appinfo_t *hIC = NULL;
5358 DWORD res = ERROR_SUCCESS;
5359
5360 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
5361 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
5362
5363 request = (http_request_t*) get_handle_object( hHttpRequest );
5364 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5365 {
5366 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5367 goto lend;
5368 }
5369
5370 session = request->session;
5371 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
5372 {
5373 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5374 goto lend;
5375 }
5376
5377 hIC = session->appInfo;
5378 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
5379 {
5380 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5381 goto lend;
5382 }
5383
5384 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5385 {
5386 WORKREQUEST workRequest;
5387 struct WORKREQ_HTTPSENDREQUESTW *req;
5388
5389 workRequest.asyncproc = AsyncHttpSendRequestProc;
5390 workRequest.hdr = WININET_AddRef( &request->hdr );
5391 req = &workRequest.u.HttpSendRequestW;
5392 if (lpszHeaders)
5393 {
5394 DWORD size;
5395
5396 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
5397 else size = dwHeaderLength * sizeof(WCHAR);
5398
5399 req->lpszHeader = heap_alloc(size);
5400 memcpy(req->lpszHeader, lpszHeaders, size);
5401 }
5402 else
5403 req->lpszHeader = 0;
5404 req->dwHeaderLength = dwHeaderLength;
5405 req->lpOptional = lpOptional;
5406 req->dwOptionalLength = dwOptionalLength;
5407 req->dwContentLength = dwOptionalLength;
5408 req->bEndRequest = TRUE;
5409
5410 INTERNET_AsyncCall(&workRequest);
5411 /*
5412 * This is from windows.
5413 */
5414 res = ERROR_IO_PENDING;
5415 }
5416 else
5417 {
5418 res = HTTP_HttpSendRequestW(request, lpszHeaders,
5419 dwHeaderLength, lpOptional, dwOptionalLength,
5420 dwOptionalLength, TRUE);
5421 }
5422 lend:
5423 if( request )
5424 WININET_Release( &request->hdr );
5425
5426 SetLastError(res);
5427 return res == ERROR_SUCCESS;
5428 }
5429
5430 /***********************************************************************
5431 * HttpSendRequestA (WININET.@)
5432 *
5433 * Sends the specified request to the HTTP server
5434 *
5435 * RETURNS
5436 * TRUE on success
5437 * FALSE on failure
5438 *
5439 */
5440 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
5441 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
5442 {
5443 BOOL result;
5444 LPWSTR szHeaders=NULL;
5445 DWORD nLen=dwHeaderLength;
5446 if(lpszHeaders!=NULL)
5447 {
5448 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5449 szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5450 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
5451 }
5452 result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
5453 heap_free(szHeaders);
5454 return result;
5455 }
5456
5457 /***********************************************************************
5458 * HTTPSESSION_Destroy (internal)
5459 *
5460 * Deallocate session handle
5461 *
5462 */
5463 static void HTTPSESSION_Destroy(object_header_t *hdr)
5464 {
5465 http_session_t *session = (http_session_t*) hdr;
5466
5467 TRACE("%p\n", session);
5468
5469 WININET_Release(&session->appInfo->hdr);
5470
5471 heap_free(session->hostName);
5472 heap_free(session->serverName);
5473 heap_free(session->password);
5474 heap_free(session->userName);
5475 }
5476
5477 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5478 {
5479 http_session_t *ses = (http_session_t *)hdr;
5480
5481 switch(option) {
5482 case INTERNET_OPTION_HANDLE_TYPE:
5483 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5484
5485 if (*size < sizeof(ULONG))
5486 return ERROR_INSUFFICIENT_BUFFER;
5487
5488 *size = sizeof(DWORD);
5489 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5490 return ERROR_SUCCESS;
5491 case INTERNET_OPTION_CONNECT_TIMEOUT:
5492 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5493
5494 if (*size < sizeof(DWORD))
5495 return ERROR_INSUFFICIENT_BUFFER;
5496
5497 *size = sizeof(DWORD);
5498 *(DWORD *)buffer = ses->connect_timeout;
5499 return ERROR_SUCCESS;
5500
5501 case INTERNET_OPTION_SEND_TIMEOUT:
5502 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5503
5504 if (*size < sizeof(DWORD))
5505 return ERROR_INSUFFICIENT_BUFFER;
5506
5507 *size = sizeof(DWORD);
5508 *(DWORD *)buffer = ses->send_timeout;
5509 return ERROR_SUCCESS;
5510
5511 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5512 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5513
5514 if (*size < sizeof(DWORD))
5515 return ERROR_INSUFFICIENT_BUFFER;
5516
5517 *size = sizeof(DWORD);
5518 *(DWORD *)buffer = ses->receive_timeout;
5519 return ERROR_SUCCESS;
5520 }
5521
5522 return INET_QueryOption(hdr, option, buffer, size, unicode);
5523 }
5524
5525 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5526 {
5527 http_session_t *ses = (http_session_t*)hdr;
5528
5529 switch(option) {
5530 case INTERNET_OPTION_USERNAME:
5531 {
5532 heap_free(ses->userName);
5533 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5534 return ERROR_SUCCESS;
5535 }
5536 case INTERNET_OPTION_PASSWORD:
5537 {
5538 heap_free(ses->password);
5539 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5540 return ERROR_SUCCESS;
5541 }
5542 case INTERNET_OPTION_CONNECT_TIMEOUT:
5543 {
5544 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5545 ses->connect_timeout = *(DWORD *)buffer;
5546 return ERROR_SUCCESS;
5547 }
5548 case INTERNET_OPTION_SEND_TIMEOUT:
5549 {
5550 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5551 ses->send_timeout = *(DWORD *)buffer;
5552 return ERROR_SUCCESS;
5553 }
5554 case INTERNET_OPTION_RECEIVE_TIMEOUT:
5555 {
5556 if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5557 ses->receive_timeout = *(DWORD *)buffer;
5558 return ERROR_SUCCESS;
5559 }
5560 default: break;
5561 }
5562
5563 return INET_SetOption(hdr, option, buffer, size);
5564 }
5565
5566 static const object_vtbl_t HTTPSESSIONVtbl = {
5567 HTTPSESSION_Destroy,
5568 NULL,
5569 HTTPSESSION_QueryOption,
5570 HTTPSESSION_SetOption,
5571 NULL,
5572 NULL,
5573 NULL,
5574 NULL,
5575 NULL
5576 };
5577
5578
5579 /***********************************************************************
5580 * HTTP_Connect (internal)
5581 *
5582 * Create http session handle
5583 *
5584 * RETURNS
5585 * HINTERNET a session handle on success
5586 * NULL on failure
5587 *
5588 */
5589 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5590 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5591 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5592 DWORD dwInternalFlags, HINTERNET *ret)
5593 {
5594 http_session_t *session = NULL;
5595
5596 TRACE("-->\n");
5597
5598 if (!lpszServerName || !lpszServerName[0])
5599 return ERROR_INVALID_PARAMETER;
5600
5601 assert( hIC->hdr.htype == WH_HINIT );
5602
5603 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5604 if (!session)
5605 return ERROR_OUTOFMEMORY;
5606
5607 /*
5608 * According to my tests. The name is not resolved until a request is sent
5609 */
5610
5611 session->hdr.htype = WH_HHTTPSESSION;
5612 session->hdr.dwFlags = dwFlags;
5613 session->hdr.dwContext = dwContext;
5614 session->hdr.dwInternalFlags |= dwInternalFlags;
5615
5616 WININET_AddRef( &hIC->hdr );
5617 session->appInfo = hIC;
5618 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5619
5620 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5621 if(hIC->proxyBypass)
5622 FIXME("Proxy bypass is ignored.\n");
5623 }
5624 session->serverName = heap_strdupW(lpszServerName);
5625 session->hostName = heap_strdupW(lpszServerName);
5626 if (lpszUserName && lpszUserName[0])
5627 session->userName = heap_strdupW(lpszUserName);
5628 if (lpszPassword && lpszPassword[0])
5629 session->password = heap_strdupW(lpszPassword);
5630 session->serverPort = serverPort;
5631 session->hostPort = serverPort;
5632 session->connect_timeout = INFINITE;
5633 session->send_timeout = INFINITE;
5634 session->receive_timeout = INFINITE;
5635
5636 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5637 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5638 {
5639 INTERNET_SendCallback(&hIC->hdr, dwContext,
5640 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5641 sizeof(HINTERNET));
5642 }
5643
5644 /*
5645 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5646 * windows
5647 */
5648
5649 TRACE("%p --> %p\n", hIC, session);
5650
5651 *ret = session->hdr.hInternet;
5652 return ERROR_SUCCESS;
5653 }
5654
5655 /***********************************************************************
5656 * HTTP_clear_response_headers (internal)
5657 *
5658 * clear out any old response headers
5659 */
5660 static void HTTP_clear_response_headers( http_request_t *request )
5661 {
5662 DWORD i;
5663
5664 for( i=0; i<request->nCustHeaders; i++)
5665 {
5666 if( !request->custHeaders[i].lpszField )
5667 continue;
5668 if( !request->custHeaders[i].lpszValue )
5669 continue;
5670 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5671 continue;
5672 HTTP_DeleteCustomHeader( request, i );
5673 i--;
5674 }
5675 }
5676
5677 /***********************************************************************
5678 * HTTP_GetResponseHeaders (internal)
5679 *
5680 * Read server response
5681 *
5682 * RETURNS
5683 *
5684 * TRUE on success
5685 * FALSE on error
5686 */
5687 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5688 {
5689 INT cbreaks = 0;
5690 WCHAR buffer[MAX_REPLY_LEN];
5691 DWORD buflen = MAX_REPLY_LEN;
5692 BOOL bSuccess = FALSE;
5693 INT rc = 0;
5694 char bufferA[MAX_REPLY_LEN];
5695 LPWSTR status_code = NULL, status_text = NULL;
5696 DWORD cchMaxRawHeaders = 1024;
5697 LPWSTR lpszRawHeaders = NULL;
5698 LPWSTR temp;
5699 DWORD cchRawHeaders = 0;
5700 BOOL codeHundred = FALSE;
5701
5702 TRACE("-->\n");
5703
5704 if(!request->netconn)
5705 goto lend;
5706
5707 NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5708 do {
5709 static const WCHAR szHundred[] = {'1','0','0',0};
5710 /*
5711 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5712 */
5713 buflen = MAX_REPLY_LEN;
5714 if (!read_line(request, bufferA, &buflen))
5715 goto lend;
5716
5717 /* clear old response headers (eg. from a redirect response) */
5718 if (clear) {
5719 HTTP_clear_response_headers( request );
5720 clear = FALSE;
5721 }
5722
5723 rc += buflen;
5724 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5725 /* check is this a status code line? */
5726 if (!strncmpW(buffer, g_szHttp1_0, 4))
5727 {
5728 /* split the version from the status code */
5729 status_code = strchrW( buffer, ' ' );
5730 if( !status_code )
5731 goto lend;
5732 *status_code++=0;
5733
5734 /* split the status code from the status text */
5735 status_text = strchrW( status_code, ' ' );
5736 if( !status_text )
5737 goto lend;
5738 *status_text++=0;
5739
5740 request->status_code = atoiW(status_code);
5741
5742 TRACE("version [%s] status code [%s] status text [%s]\n",
5743 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5744
5745 codeHundred = (!strcmpW(status_code, szHundred));
5746 }
5747 else if (!codeHundred)
5748 {
5749 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5750
5751 heap_free(request->version);
5752 heap_free(request->statusText);
5753
5754 request->status_code = HTTP_STATUS_OK;
5755 request->version = heap_strdupW(g_szHttp1_0);
5756 request->statusText = heap_strdupW(szOK);
5757
5758 heap_free(request->rawHeaders);
5759 request->rawHeaders = heap_strdupW(szDefaultHeader);
5760
5761 bSuccess = TRUE;
5762 goto lend;
5763 }
5764 } while (codeHundred);
5765
5766 /* Add status code */
5767 HTTP_ProcessHeader(request, szStatus, status_code,
5768 HTTP_ADDHDR_FLAG_REPLACE);
5769
5770 heap_free(request->version);
5771 heap_free(request->statusText);
5772
5773 request->version = heap_strdupW(buffer);
5774 request->statusText = heap_strdupW(status_text);
5775
5776 /* Restore the spaces */
5777 *(status_code-1) = ' ';
5778 *(status_text-1) = ' ';
5779
5780 /* regenerate raw headers */
5781 lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5782 if (!lpszRawHeaders) goto lend;
5783
5784 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5785 cchMaxRawHeaders *= 2;
5786 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5787 if (temp == NULL) goto lend;
5788 lpszRawHeaders = temp;
5789 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5790 cchRawHeaders += (buflen-1);
5791 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5792 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5793 lpszRawHeaders[cchRawHeaders] = '\0';
5794
5795 /* Parse each response line */
5796 do
5797 {
5798 buflen = MAX_REPLY_LEN;
5799 if (read_line(request, bufferA, &buflen))
5800 {
5801 LPWSTR * pFieldAndValue;
5802
5803 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5804
5805 if (!bufferA[0]) break;
5806 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5807
5808 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5809 if (pFieldAndValue)
5810 {
5811 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5812 cchMaxRawHeaders *= 2;
5813 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5814 if (temp == NULL) goto lend;
5815 lpszRawHeaders = temp;
5816 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5817 cchRawHeaders += (buflen-1);
5818 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5819 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5820 lpszRawHeaders[cchRawHeaders] = '\0';
5821
5822 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5823 HTTP_ADDREQ_FLAG_ADD );
5824
5825 HTTP_FreeTokens(pFieldAndValue);
5826 }
5827 }
5828 else
5829 {
5830 cbreaks++;
5831 if (cbreaks >= 2)
5832 break;
5833 }
5834 }while(1);
5835
5836 /* make sure the response header is terminated with an empty line. Some apps really
5837 truly care about that empty line being there for some reason. Just add it to the
5838 header. */
5839 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5840 {
5841 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5842 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5843 if (temp == NULL) goto lend;
5844 lpszRawHeaders = temp;
5845 }
5846
5847 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5848
5849 heap_free(request->rawHeaders);
5850 request->rawHeaders = lpszRawHeaders;
5851 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5852 bSuccess = TRUE;
5853
5854 lend:
5855
5856 TRACE("<--\n");
5857 if (bSuccess)
5858 return rc;
5859 else
5860 {
5861 heap_free(lpszRawHeaders);
5862 return 0;
5863 }
5864 }
5865
5866 /***********************************************************************
5867 * HTTP_InterpretHttpHeader (internal)
5868 *
5869 * Parse server response
5870 *
5871 * RETURNS
5872 *
5873 * Pointer to array of field, value, NULL on success.
5874 * NULL on error.
5875 */
5876 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5877 {
5878 LPWSTR * pTokenPair;
5879 LPWSTR pszColon;
5880 INT len;
5881
5882 pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5883
5884 pszColon = strchrW(buffer, ':');
5885 /* must have two tokens */
5886 if (!pszColon)
5887 {
5888 HTTP_FreeTokens(pTokenPair);
5889 if (buffer[0])
5890 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5891 return NULL;
5892 }
5893
5894 pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5895 if (!pTokenPair[0])
5896 {
5897 HTTP_FreeTokens(pTokenPair);
5898 return NULL;
5899 }
5900 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5901 pTokenPair[0][pszColon - buffer] = '\0';
5902
5903 /* skip colon */
5904 pszColon++;
5905 len = strlenW(pszColon);
5906 pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5907 if (!pTokenPair[1])
5908 {
5909 HTTP_FreeTokens(pTokenPair);
5910 return NULL;
5911 }
5912 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5913
5914 strip_spaces(pTokenPair[0]);
5915 strip_spaces(pTokenPair[1]);
5916
5917 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5918 return pTokenPair;
5919 }
5920
5921 /***********************************************************************
5922 * HTTP_ProcessHeader (internal)
5923 *
5924 * Stuff header into header tables according to <dwModifier>
5925 *
5926 */
5927
5928 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5929
5930 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5931 {
5932 LPHTTPHEADERW lphttpHdr = NULL;
5933 INT index = -1;
5934 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5935 DWORD res = ERROR_HTTP_INVALID_HEADER;
5936
5937 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5938
5939 /* REPLACE wins out over ADD */
5940 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5941 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5942
5943 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5944 index = -1;
5945 else
5946 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5947
5948 if (index >= 0)
5949 {
5950 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5951 return ERROR_HTTP_INVALID_HEADER;
5952 lphttpHdr = &request->custHeaders[index];
5953 }
5954 else if (value)
5955 {
5956 HTTPHEADERW hdr;
5957
5958 hdr.lpszField = (LPWSTR)field;
5959 hdr.lpszValue = (LPWSTR)value;
5960 hdr.wFlags = hdr.wCount = 0;
5961
5962 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5963 hdr.wFlags |= HDR_ISREQUEST;
5964
5965 return HTTP_InsertCustomHeader(request, &hdr);
5966 }
5967 /* no value to delete */
5968 else return ERROR_SUCCESS;
5969
5970 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5971 lphttpHdr->wFlags |= HDR_ISREQUEST;
5972 else
5973 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5974
5975 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5976 {
5977 HTTP_DeleteCustomHeader( request, index );
5978
5979 if (value)
5980 {
5981 HTTPHEADERW hdr;
5982
5983 hdr.lpszField = (LPWSTR)field;
5984 hdr.lpszValue = (LPWSTR)value;
5985 hdr.wFlags = hdr.wCount = 0;
5986
5987 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5988 hdr.wFlags |= HDR_ISREQUEST;
5989
5990 return HTTP_InsertCustomHeader(request, &hdr);
5991 }
5992
5993 return ERROR_SUCCESS;
5994 }
5995 else if (dwModifier & COALESCEFLAGS)
5996 {
5997 LPWSTR lpsztmp;
5998 WCHAR ch = 0;
5999 INT len = 0;
6000 INT origlen = strlenW(lphttpHdr->lpszValue);
6001 INT valuelen = strlenW(value);
6002
6003 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
6004 {
6005 ch = ',';
6006 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6007 }
6008 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
6009 {
6010 ch = ';';
6011 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
6012 }
6013
6014 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
6015
6016 lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
6017 if (lpsztmp)
6018 {
6019 lphttpHdr->lpszValue = lpsztmp;
6020 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
6021 if (ch > 0)
6022 {
6023 lphttpHdr->lpszValue[origlen] = ch;
6024 origlen++;
6025 lphttpHdr->lpszValue[origlen] = ' ';
6026 origlen++;
6027 }
6028
6029 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
6030 lphttpHdr->lpszValue[len] = '\0';
6031 res = ERROR_SUCCESS;
6032 }
6033 else
6034 {
6035 WARN("heap_realloc (%d bytes) failed\n",len+1);
6036 res = ERROR_OUTOFMEMORY;
6037 }
6038 }
6039 TRACE("<-- %d\n", res);
6040 return res;
6041 }
6042
6043 /***********************************************************************
6044 * HTTP_GetCustomHeaderIndex (internal)
6045 *
6046 * Return index of custom header from header array
6047 *
6048 */
6049 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
6050 int requested_index, BOOL request_only)
6051 {
6052 DWORD index;
6053
6054 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
6055
6056 for (index = 0; index < request->nCustHeaders; index++)
6057 {
6058 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
6059 continue;
6060
6061 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6062 continue;
6063
6064 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6065 continue;
6066
6067 if (requested_index == 0)
6068 break;
6069 requested_index --;
6070 }
6071
6072 if (index >= request->nCustHeaders)
6073 index = -1;
6074
6075 TRACE("Return: %d\n", index);
6076 return index;
6077 }
6078
6079
6080 /***********************************************************************
6081 * HTTP_InsertCustomHeader (internal)
6082 *
6083 * Insert header into array
6084 *
6085 */
6086 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6087 {
6088 INT count;
6089 LPHTTPHEADERW lph = NULL;
6090
6091 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6092 count = request->nCustHeaders + 1;
6093 if (count > 1)
6094 lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6095 else
6096 lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6097
6098 if (!lph)
6099 return ERROR_OUTOFMEMORY;
6100
6101 request->custHeaders = lph;
6102 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
6103 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
6104 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
6105 request->custHeaders[count-1].wCount= lpHdr->wCount;
6106 request->nCustHeaders++;
6107
6108 return ERROR_SUCCESS;
6109 }
6110
6111
6112 /***********************************************************************
6113 * HTTP_DeleteCustomHeader (internal)
6114 *
6115 * Delete header from array
6116 * If this function is called, the indexs may change.
6117 */
6118 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6119 {
6120 if( request->nCustHeaders <= 0 )
6121 return FALSE;
6122 if( index >= request->nCustHeaders )
6123 return FALSE;
6124 request->nCustHeaders--;
6125
6126 heap_free(request->custHeaders[index].lpszField);
6127 heap_free(request->custHeaders[index].lpszValue);
6128
6129 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6130 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6131 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6132
6133 return TRUE;
6134 }
6135
6136
6137 /***********************************************************************
6138 * HTTP_VerifyValidHeader (internal)
6139 *
6140 * Verify the given header is not invalid for the given http request
6141 *
6142 */
6143 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6144 {
6145 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6146 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6147 return ERROR_HTTP_INVALID_HEADER;
6148
6149 return ERROR_SUCCESS;
6150 }
6151
6152 /***********************************************************************
6153 * IsHostInProxyBypassList (@)
6154 *
6155 * Undocumented
6156 *
6157 */
6158 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
6159 {
6160 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6161 return FALSE;
6162 }
6163
6164 /***********************************************************************
6165 * InternetShowSecurityInfoByURLA (@)
6166 */
6167 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
6168 {
6169 FIXME("stub: %s %p\n", url, window);
6170 return FALSE;
6171 }
6172
6173 /***********************************************************************
6174 * InternetShowSecurityInfoByURLW (@)
6175 */
6176 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
6177 {
6178 FIXME("stub: %s %p\n", debugstr_w(url), window);
6179 return FALSE;
6180 }
6181
6182 /***********************************************************************
6183 * ShowX509EncodedCertificate (@)
6184 */
6185 DWORD WINAPI ShowX509EncodedCertificate(HWND parent, LPBYTE cert, DWORD len)
6186 {
6187 PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING,
6188 cert, len);
6189 DWORD ret;
6190
6191 if (certContext)
6192 {
6193 CRYPTUI_VIEWCERTIFICATE_STRUCTW view;
6194
6195 memset(&view, 0, sizeof(view));
6196 view.hwndParent = parent;
6197 view.pCertContext = certContext;
6198 if (CryptUIDlgViewCertificateW(&view, NULL))
6199 ret = ERROR_SUCCESS;
6200 else
6201 ret = GetLastError();
6202 CertFreeCertificateContext(certContext);
6203 }
6204 else
6205 ret = GetLastError();
6206 return ret;
6207 }