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