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