mmdevapi/tests: Add tests for IAudioClient::GetCurrentPadding.
[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         return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1999                     *(DWORD*)buffer);
2000
2001     case INTERNET_OPTION_USERNAME:
2002         HeapFree(GetProcessHeap(), 0, req->session->userName);
2003         if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2004         return ERROR_SUCCESS;
2005
2006     case INTERNET_OPTION_PASSWORD:
2007         HeapFree(GetProcessHeap(), 0, req->session->password);
2008         if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2009         return ERROR_SUCCESS;
2010     case INTERNET_OPTION_HTTP_DECODING:
2011         if(size != sizeof(BOOL))
2012             return ERROR_INVALID_PARAMETER;
2013         req->decoding = *(BOOL*)buffer;
2014         return ERROR_SUCCESS;
2015     }
2016
2017     return ERROR_INTERNET_INVALID_OPTION;
2018 }
2019
2020 /* read some more data into the read buffer (the read section must be held) */
2021 static DWORD read_more_data( http_request_t *req, int maxlen )
2022 {
2023     DWORD res;
2024     int len;
2025
2026     if (req->read_pos)
2027     {
2028         /* move existing data to the start of the buffer */
2029         if(req->read_size)
2030             memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2031         req->read_pos = 0;
2032     }
2033
2034     if (maxlen == -1) maxlen = sizeof(req->read_buf);
2035
2036     res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
2037                        maxlen - req->read_size, 0, &len );
2038     if(res == ERROR_SUCCESS)
2039         req->read_size += len;
2040
2041     return res;
2042 }
2043
2044 /* remove some amount of data from the read buffer (the read section must be held) */
2045 static void remove_data( http_request_t *req, int count )
2046 {
2047     if (!(req->read_size -= count)) req->read_pos = 0;
2048     else req->read_pos += count;
2049 }
2050
2051 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2052 {
2053     int count, bytes_read, pos = 0;
2054     DWORD res;
2055
2056     EnterCriticalSection( &req->read_section );
2057     for (;;)
2058     {
2059         BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2060
2061         if (eol)
2062         {
2063             count = eol - (req->read_buf + req->read_pos);
2064             bytes_read = count + 1;
2065         }
2066         else count = bytes_read = req->read_size;
2067
2068         count = min( count, *len - pos );
2069         memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2070         pos += count;
2071         remove_data( req, bytes_read );
2072         if (eol) break;
2073
2074         if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2075         {
2076             *len = 0;
2077             TRACE( "returning empty string\n" );
2078             LeaveCriticalSection( &req->read_section );
2079             INTERNET_SetLastError(res);
2080             return FALSE;
2081         }
2082     }
2083     LeaveCriticalSection( &req->read_section );
2084
2085     if (pos < *len)
2086     {
2087         if (pos && buffer[pos - 1] == '\r') pos--;
2088         *len = pos + 1;
2089     }
2090     buffer[*len - 1] = 0;
2091     TRACE( "returning %s\n", debugstr_a(buffer));
2092     return TRUE;
2093 }
2094
2095 /* check if we have reached the end of the data to read (the read section must be held) */
2096 static BOOL end_of_read_data( http_request_t *req )
2097 {
2098     return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2099 }
2100
2101 /* fetch some more data into the read buffer (the read section must be held) */
2102 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode)
2103 {
2104     DWORD res, read=0;
2105
2106     if(req->read_size == sizeof(req->read_buf))
2107         return ERROR_SUCCESS;
2108
2109     if(req->read_pos) {
2110         if(req->read_size)
2111             memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2112         req->read_pos = 0;
2113     }
2114
2115     res = req->data_stream->vtbl->read(req->data_stream, req, req->read_buf+req->read_size,
2116             sizeof(req->read_buf)-req->read_size, &read, read_mode);
2117     req->read_size += read;
2118
2119     TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2120     return res;
2121 }
2122
2123 /* return the size of data available to be read immediately (the read section must be held) */
2124 static DWORD get_avail_data( http_request_t *req )
2125 {
2126     return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2127 }
2128
2129 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2130 {
2131     DWORD avail = 0;
2132
2133     NETCON_query_data_available(&req->netConnection, &avail);
2134     return avail;
2135 }
2136
2137 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2138 {
2139     netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2140     return netconn_stream->content_read == netconn_stream->content_length || !NETCON_connected(&req->netConnection);
2141 }
2142
2143 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2144         DWORD *read, read_mode_t read_mode)
2145 {
2146     netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2147     DWORD ret_read = 0;
2148     int len;
2149
2150     size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2151
2152     if(read_mode == READMODE_NOBLOCK)
2153         size = min(size, netconn_get_avail_data(stream, req));
2154
2155     if(size) {
2156         if(NETCON_recv(&req->netConnection, buf + ret_read, size,
2157                 read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2158             ret_read = len;
2159     }
2160
2161     netconn_stream->content_read += *read = ret_read;
2162     TRACE("read %u bytes\n", ret_read);
2163     return ERROR_SUCCESS;
2164 }
2165
2166 static void netconn_destroy(data_stream_t *stream)
2167 {
2168 }
2169
2170 static const data_stream_vtbl_t netconn_stream_vtbl = {
2171     netconn_get_avail_data,
2172     netconn_end_of_data,
2173     netconn_read,
2174     netconn_destroy
2175 };
2176
2177 /* read some more data into the read buffer (the read section must be held) */
2178 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2179 {
2180     DWORD res;
2181     int len;
2182
2183     if (stream->buf_pos)
2184     {
2185         /* move existing data to the start of the buffer */
2186         if(stream->buf_size)
2187             memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2188         stream->buf_pos = 0;
2189     }
2190
2191     if (maxlen == -1) maxlen = sizeof(stream->buf);
2192
2193     res = NETCON_recv( &req->netConnection, stream->buf + stream->buf_size,
2194                        maxlen - stream->buf_size, 0, &len );
2195     if(res == ERROR_SUCCESS)
2196         stream->buf_size += len;
2197
2198     return res;
2199 }
2200
2201 /* remove some amount of data from the read buffer (the read section must be held) */
2202 static void remove_chunked_data(chunked_stream_t *stream, int count)
2203 {
2204     if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2205     else stream->buf_pos += count;
2206 }
2207
2208 /* discard data contents until we reach end of line (the read section must be held) */
2209 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2210 {
2211     DWORD res;
2212
2213     do
2214     {
2215         BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2216         if (eol)
2217         {
2218             remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2219             break;
2220         }
2221         stream->buf_pos = stream->buf_size = 0;  /* discard everything */
2222         if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2223     } while (stream->buf_size);
2224     return ERROR_SUCCESS;
2225 }
2226
2227 /* read the size of the next chunk (the read section must be held) */
2228 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2229 {
2230     /* TODOO */
2231     DWORD chunk_size = 0, res;
2232
2233     if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2234         return res;
2235
2236     for (;;)
2237     {
2238         while (stream->buf_size)
2239         {
2240             char ch = stream->buf[stream->buf_pos];
2241             if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2242             else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2243             else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2244             else if (ch == ';' || ch == '\r' || ch == '\n')
2245             {
2246                 TRACE( "reading %u byte chunk\n", chunk_size );
2247                 stream->chunk_size = chunk_size;
2248                 req->contentLength += chunk_size;
2249                 return discard_chunked_eol(stream, req);
2250             }
2251             remove_chunked_data(stream, 1);
2252         }
2253         if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2254         if (!stream->buf_size)
2255         {
2256             stream->chunk_size = 0;
2257             return ERROR_SUCCESS;
2258         }
2259     }
2260 }
2261
2262 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2263 {
2264     /* Allow reading only from read buffer */
2265     return 0;
2266 }
2267
2268 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2269 {
2270     chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2271     return !chunked_stream->chunk_size;
2272 }
2273
2274 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2275         DWORD *read, read_mode_t read_mode)
2276 {
2277     chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2278     DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2279
2280     if(chunked_stream->chunk_size == ~0u) {
2281         res = start_next_chunk(chunked_stream, req);
2282         if(res != ERROR_SUCCESS)
2283             return res;
2284     }
2285
2286     while(size && chunked_stream->chunk_size) {
2287         if(chunked_stream->buf_size) {
2288             read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2289
2290             /* this could block */
2291             if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2292                 break;
2293
2294             memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2295             remove_chunked_data(chunked_stream, read_bytes);
2296         }else {
2297             read_bytes = min(size, chunked_stream->chunk_size);
2298
2299             if(read_mode == READMODE_NOBLOCK) {
2300                 DWORD avail;
2301
2302                 if(!NETCON_query_data_available(&req->netConnection, &avail) || !avail)
2303                     break;
2304                 if(read_bytes > avail)
2305                     read_bytes = avail;
2306
2307                 /* this could block */
2308                 if(read_bytes == chunked_stream->chunk_size)
2309                     break;
2310             }
2311
2312             res = NETCON_recv(&req->netConnection, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2313             if(res != ERROR_SUCCESS)
2314                 break;
2315         }
2316
2317         chunked_stream->chunk_size -= read_bytes;
2318         size -= read_bytes;
2319         ret_read += read_bytes;
2320         if(!chunked_stream->chunk_size) {
2321             assert(read_mode != READMODE_NOBLOCK);
2322             res = start_next_chunk(chunked_stream, req);
2323             if(res != ERROR_SUCCESS)
2324                 break;
2325         }
2326
2327         if(read_mode == READMODE_ASYNC)
2328             read_mode = READMODE_NOBLOCK;
2329     }
2330
2331     TRACE("read %u bytes\n", ret_read);
2332     *read = ret_read;
2333     return res;
2334 }
2335
2336 static void chunked_destroy(data_stream_t *stream)
2337 {
2338     chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2339     heap_free(chunked_stream);
2340 }
2341
2342 static const data_stream_vtbl_t chunked_stream_vtbl = {
2343     chunked_get_avail_data,
2344     chunked_end_of_data,
2345     chunked_read,
2346     chunked_destroy
2347 };
2348
2349 /* set the request content length based on the headers */
2350 static DWORD set_content_length(http_request_t *request)
2351 {
2352     static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2353     WCHAR encoding[20];
2354     DWORD size;
2355
2356     size = sizeof(request->contentLength);
2357     if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2358                             &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2359         request->contentLength = ~0u;
2360     request->netconn_stream.content_length = request->contentLength;
2361     request->netconn_stream.content_read = request->read_size;
2362
2363     size = sizeof(encoding);
2364     if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2365         !strcmpiW(encoding, szChunked))
2366     {
2367         chunked_stream_t *chunked_stream;
2368
2369         chunked_stream = heap_alloc(sizeof(*chunked_stream));
2370         if(!chunked_stream)
2371             return ERROR_OUTOFMEMORY;
2372
2373         chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2374         chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2375         chunked_stream->chunk_size = ~0u;
2376
2377         if(request->read_size) {
2378             memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2379             chunked_stream->buf_size = request->read_size;
2380             request->read_size = request->read_pos = 0;
2381         }
2382
2383         request->data_stream = &chunked_stream->data_stream;
2384         request->contentLength = ~0u;
2385         request->read_chunked = TRUE;
2386     }
2387
2388     if(request->decoding) {
2389         int encoding_idx;
2390
2391         static const WCHAR gzipW[] = {'g','z','i','p',0};
2392
2393         encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2394         if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2395             return init_gzip_stream(request);
2396     }
2397
2398     return ERROR_SUCCESS;
2399 }
2400
2401 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2402 {
2403     INTERNET_ASYNC_RESULT iar;
2404     DWORD res;
2405
2406     TRACE("%p\n", req);
2407
2408     EnterCriticalSection( &req->read_section );
2409     if ((res = refill_read_buffer(req, READMODE_ASYNC)) == ERROR_SUCCESS) {
2410         iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2411         iar.dwError = first_notif ? 0 : get_avail_data(req);
2412         if(!first_notif && !iar.dwError)
2413             ERR("No data reported!\n");
2414     }else {
2415         iar.dwResult = 0;
2416         iar.dwError = res;
2417     }
2418     LeaveCriticalSection( &req->read_section );
2419
2420     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2421                           sizeof(INTERNET_ASYNC_RESULT));
2422 }
2423
2424 /* read data from the http connection (the read section must be held) */
2425 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2426 {
2427     DWORD current_read = 0, ret_read = 0;
2428     read_mode_t read_mode;
2429     DWORD res = ERROR_SUCCESS;
2430
2431     read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2432
2433     EnterCriticalSection( &req->read_section );
2434
2435     if(req->read_size) {
2436         ret_read = min(size, req->read_size);
2437         memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2438         req->read_size -= ret_read;
2439         req->read_pos += ret_read;
2440         if(read_mode == READMODE_ASYNC)
2441             read_mode = READMODE_NOBLOCK;
2442     }
2443
2444     if(ret_read < size) {
2445         res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, read_mode);
2446         ret_read += current_read;
2447     }
2448
2449     LeaveCriticalSection( &req->read_section );
2450
2451     *read = ret_read;
2452     TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2453
2454     if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
2455         BOOL res;
2456         DWORD written;
2457
2458         res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
2459         if(!res)
2460             WARN("WriteFile failed: %u\n", GetLastError());
2461     }
2462
2463     if(end_of_read_data(req))
2464         HTTP_FinishedReading(req);
2465
2466     return res;
2467 }
2468
2469
2470 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2471 {
2472     http_request_t *req = (http_request_t*)hdr;
2473     DWORD res;
2474
2475     EnterCriticalSection( &req->read_section );
2476     if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2477         hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2478
2479     res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2480     if(res == ERROR_SUCCESS)
2481         res = hdr->dwError;
2482     LeaveCriticalSection( &req->read_section );
2483
2484     return res;
2485 }
2486
2487 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2488 {
2489     struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2490     http_request_t *req = (http_request_t*)workRequest->hdr;
2491     INTERNET_ASYNC_RESULT iar;
2492     DWORD res;
2493
2494     TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2495
2496     res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2497             data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2498
2499     iar.dwResult = res == ERROR_SUCCESS;
2500     iar.dwError = res;
2501
2502     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2503                           INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2504                           sizeof(INTERNET_ASYNC_RESULT));
2505 }
2506
2507 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2508         DWORD flags, DWORD_PTR context)
2509 {
2510     http_request_t *req = (http_request_t*)hdr;
2511     DWORD res, size, read, error = ERROR_SUCCESS;
2512
2513     if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2514         FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2515
2516     if (buffers->dwStructSize != sizeof(*buffers))
2517         return ERROR_INVALID_PARAMETER;
2518
2519     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2520
2521     if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2522     {
2523         WORKREQUEST workRequest;
2524
2525         if (TryEnterCriticalSection( &req->read_section ))
2526         {
2527             if (get_avail_data(req))
2528             {
2529                 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2530                                    &buffers->dwBufferLength, FALSE);
2531                 size = buffers->dwBufferLength;
2532                 LeaveCriticalSection( &req->read_section );
2533                 goto done;
2534             }
2535             LeaveCriticalSection( &req->read_section );
2536         }
2537
2538         workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2539         workRequest.hdr = WININET_AddRef(&req->hdr);
2540         workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2541
2542         INTERNET_AsyncCall(&workRequest);
2543
2544         return ERROR_IO_PENDING;
2545     }
2546
2547     read = 0;
2548     size = buffers->dwBufferLength;
2549
2550     EnterCriticalSection( &req->read_section );
2551     if(hdr->dwError == ERROR_SUCCESS)
2552         hdr->dwError = INTERNET_HANDLE_IN_USE;
2553     else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2554         hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2555
2556     while(1) {
2557         res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2558                 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2559         if(res != ERROR_SUCCESS)
2560             break;
2561
2562         read += buffers->dwBufferLength;
2563         if(read == size || end_of_read_data(req))
2564             break;
2565
2566         LeaveCriticalSection( &req->read_section );
2567
2568         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2569                 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2570         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2571                 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2572
2573         EnterCriticalSection( &req->read_section );
2574     }
2575
2576     if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2577         hdr->dwError = ERROR_SUCCESS;
2578     else
2579         error = hdr->dwError;
2580
2581     LeaveCriticalSection( &req->read_section );
2582     size = buffers->dwBufferLength;
2583     buffers->dwBufferLength = read;
2584
2585 done:
2586     if (res == ERROR_SUCCESS) {
2587         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2588                 &size, sizeof(size));
2589     }
2590
2591     return res==ERROR_SUCCESS ? error : res;
2592 }
2593
2594 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2595 {
2596     struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2597     http_request_t *req = (http_request_t*)workRequest->hdr;
2598     INTERNET_ASYNC_RESULT iar;
2599     DWORD res;
2600
2601     TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2602
2603     res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2604             data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2605
2606     iar.dwResult = res == ERROR_SUCCESS;
2607     iar.dwError = res;
2608
2609     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2610                           INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2611                           sizeof(INTERNET_ASYNC_RESULT));
2612 }
2613
2614 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2615         DWORD flags, DWORD_PTR context)
2616 {
2617
2618     http_request_t *req = (http_request_t*)hdr;
2619     DWORD res, size, read, error = ERROR_SUCCESS;
2620
2621     if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2622         FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2623
2624     if (buffers->dwStructSize != sizeof(*buffers))
2625         return ERROR_INVALID_PARAMETER;
2626
2627     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2628
2629     if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2630     {
2631         WORKREQUEST workRequest;
2632
2633         if (TryEnterCriticalSection( &req->read_section ))
2634         {
2635             if (get_avail_data(req))
2636             {
2637                 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2638                                    &buffers->dwBufferLength, FALSE);
2639                 size = buffers->dwBufferLength;
2640                 LeaveCriticalSection( &req->read_section );
2641                 goto done;
2642             }
2643             LeaveCriticalSection( &req->read_section );
2644         }
2645
2646         workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2647         workRequest.hdr = WININET_AddRef(&req->hdr);
2648         workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2649
2650         INTERNET_AsyncCall(&workRequest);
2651
2652         return ERROR_IO_PENDING;
2653     }
2654
2655     read = 0;
2656     size = buffers->dwBufferLength;
2657
2658     EnterCriticalSection( &req->read_section );
2659     if(hdr->dwError == ERROR_SUCCESS)
2660         hdr->dwError = INTERNET_HANDLE_IN_USE;
2661     else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2662         hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2663
2664     while(1) {
2665         res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2666                 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2667         if(res != ERROR_SUCCESS)
2668             break;
2669
2670         read += buffers->dwBufferLength;
2671         if(read == size || end_of_read_data(req))
2672             break;
2673
2674         LeaveCriticalSection( &req->read_section );
2675
2676         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2677                 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2678         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2679                 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2680
2681         EnterCriticalSection( &req->read_section );
2682     }
2683
2684     if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2685         hdr->dwError = ERROR_SUCCESS;
2686     else
2687         error = hdr->dwError;
2688
2689     LeaveCriticalSection( &req->read_section );
2690     size = buffers->dwBufferLength;
2691     buffers->dwBufferLength = read;
2692
2693 done:
2694     if (res == ERROR_SUCCESS) {
2695         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2696                 &size, sizeof(size));
2697     }
2698
2699     return res==ERROR_SUCCESS ? error : res;
2700 }
2701
2702 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2703 {
2704     DWORD res;
2705     http_request_t *request = (http_request_t*)hdr;
2706
2707     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2708
2709     *written = 0;
2710     res = NETCON_send(&request->netConnection, buffer, size, 0, (LPINT)written);
2711     if (res == ERROR_SUCCESS)
2712         request->bytesWritten += *written;
2713
2714     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2715     return res;
2716 }
2717
2718 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2719 {
2720     http_request_t *req = (http_request_t*)workRequest->hdr;
2721
2722     HTTP_ReceiveRequestData(req, FALSE);
2723 }
2724
2725 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2726 {
2727     http_request_t *req = (http_request_t*)hdr;
2728
2729     TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2730
2731     if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2732     {
2733         WORKREQUEST workRequest;
2734
2735         /* never wait, if we can't enter the section we queue an async request right away */
2736         if (TryEnterCriticalSection( &req->read_section ))
2737         {
2738             refill_read_buffer(req, READMODE_NOBLOCK);
2739             if ((*available = get_avail_data( req ))) goto done;
2740             if (end_of_read_data( req )) goto done;
2741             LeaveCriticalSection( &req->read_section );
2742         }
2743
2744         workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2745         workRequest.hdr = WININET_AddRef( &req->hdr );
2746
2747         INTERNET_AsyncCall(&workRequest);
2748
2749         return ERROR_IO_PENDING;
2750     }
2751
2752     EnterCriticalSection( &req->read_section );
2753
2754     if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2755     {
2756         refill_read_buffer( req, READMODE_ASYNC );
2757         *available = get_avail_data( req );
2758     }
2759
2760 done:
2761     LeaveCriticalSection( &req->read_section );
2762
2763     TRACE( "returning %u\n", *available );
2764     return ERROR_SUCCESS;
2765 }
2766
2767 static const object_vtbl_t HTTPREQVtbl = {
2768     HTTPREQ_Destroy,
2769     HTTPREQ_CloseConnection,
2770     HTTPREQ_QueryOption,
2771     HTTPREQ_SetOption,
2772     HTTPREQ_ReadFile,
2773     HTTPREQ_ReadFileExA,
2774     HTTPREQ_ReadFileExW,
2775     HTTPREQ_WriteFile,
2776     HTTPREQ_QueryDataAvailable,
2777     NULL
2778 };
2779
2780 /***********************************************************************
2781  *           HTTP_HttpOpenRequestW (internal)
2782  *
2783  * Open a HTTP request handle
2784  *
2785  * RETURNS
2786  *    HINTERNET  a HTTP request handle on success
2787  *    NULL       on failure
2788  *
2789  */
2790 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
2791         LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2792         LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2793         DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2794 {
2795     appinfo_t *hIC = NULL;
2796     http_request_t *request;
2797     LPWSTR lpszHostName = NULL;
2798     static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2799     DWORD len, res;
2800
2801     TRACE("-->\n");
2802
2803     assert( session->hdr.htype == WH_HHTTPSESSION );
2804     hIC = session->appInfo;
2805
2806     request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
2807     if(!request)
2808         return ERROR_OUTOFMEMORY;
2809
2810     request->hdr.htype = WH_HHTTPREQ;
2811     request->hdr.dwFlags = dwFlags;
2812     request->hdr.dwContext = dwContext;
2813     request->contentLength = ~0u;
2814
2815     request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
2816     request->data_stream = &request->netconn_stream.data_stream;
2817
2818     InitializeCriticalSection( &request->read_section );
2819
2820     WININET_AddRef( &session->hdr );
2821     request->session = session;
2822     list_add_head( &session->hdr.children, &request->hdr.entry );
2823
2824     lpszHostName = heap_alloc(sizeof(WCHAR) * (strlenW(session->hostName) + 7 /* length of ":65535" + 1 */));
2825     if (NULL == lpszHostName)
2826     {
2827         res = ERROR_OUTOFMEMORY;
2828         goto lend;
2829     }
2830
2831     if ((res = NETCON_init(&request->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2832         goto lend;
2833     if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
2834         request->netConnection.security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
2835     if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
2836         request->netConnection.security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
2837
2838     if (lpszObjectName && *lpszObjectName) {
2839         HRESULT rc;
2840
2841         len = 0;
2842         rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2843         if (rc != E_POINTER)
2844             len = strlenW(lpszObjectName)+1;
2845         request->path = heap_alloc(len*sizeof(WCHAR));
2846         rc = UrlEscapeW(lpszObjectName, request->path, &len,
2847                    URL_ESCAPE_SPACES_ONLY);
2848         if (rc != S_OK)
2849         {
2850             ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2851             strcpyW(request->path,lpszObjectName);
2852         }
2853     }else {
2854         static const WCHAR slashW[] = {'/',0};
2855
2856         request->path = heap_strdupW(slashW);
2857     }
2858
2859     if (lpszReferrer && *lpszReferrer)
2860         HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2861
2862     if (lpszAcceptTypes)
2863     {
2864         int i;
2865         for (i = 0; lpszAcceptTypes[i]; i++)
2866         {
2867             if (!*lpszAcceptTypes[i]) continue;
2868             HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
2869                                HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2870                                HTTP_ADDHDR_FLAG_REQ |
2871                                (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2872         }
2873     }
2874
2875     request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2876     request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2877
2878     if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
2879         session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
2880         session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
2881     {
2882         sprintfW(lpszHostName, szHostForm, session->hostName, session->hostPort);
2883         HTTP_ProcessHeader(request, hostW, lpszHostName,
2884                 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2885     }
2886     else
2887         HTTP_ProcessHeader(request, hostW, session->hostName,
2888                 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2889
2890     if (session->serverPort == INTERNET_INVALID_PORT_NUMBER)
2891         session->serverPort = (dwFlags & INTERNET_FLAG_SECURE ?
2892                         INTERNET_DEFAULT_HTTPS_PORT :
2893                         INTERNET_DEFAULT_HTTP_PORT);
2894
2895     if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
2896         session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2897                         INTERNET_DEFAULT_HTTPS_PORT :
2898                         INTERNET_DEFAULT_HTTP_PORT);
2899
2900     if (NULL != hIC->proxy && hIC->proxy[0] != 0)
2901         HTTP_DealWithProxy( hIC, session, request );
2902
2903     INTERNET_SendCallback(&session->hdr, dwContext,
2904                           INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
2905                           sizeof(HINTERNET));
2906
2907 lend:
2908     TRACE("<-- %u (%p)\n", res, request);
2909
2910     HeapFree(GetProcessHeap(), 0, lpszHostName);
2911     if(res != ERROR_SUCCESS) {
2912         WININET_Release( &request->hdr );
2913         *ret = NULL;
2914         return res;
2915     }
2916
2917     *ret = request->hdr.hInternet;
2918     return ERROR_SUCCESS;
2919 }
2920
2921 /***********************************************************************
2922  *           HttpOpenRequestW (WININET.@)
2923  *
2924  * Open a HTTP request handle
2925  *
2926  * RETURNS
2927  *    HINTERNET  a HTTP request handle on success
2928  *    NULL       on failure
2929  *
2930  */
2931 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2932         LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2933         LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2934         DWORD dwFlags, DWORD_PTR dwContext)
2935 {
2936     http_session_t *session;
2937     HINTERNET handle = NULL;
2938     DWORD res;
2939
2940     TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2941           debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2942           debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2943           dwFlags, dwContext);
2944     if(lpszAcceptTypes!=NULL)
2945     {
2946         int i;
2947         for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2948             TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2949     }
2950
2951     session = (http_session_t*) get_handle_object( hHttpSession );
2952     if (NULL == session ||  session->hdr.htype != WH_HHTTPSESSION)
2953     {
2954         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2955         goto lend;
2956     }
2957
2958     /*
2959      * My tests seem to show that the windows version does not
2960      * become asynchronous until after this point. And anyhow
2961      * if this call was asynchronous then how would you get the
2962      * necessary HINTERNET pointer returned by this function.
2963      *
2964      */
2965     res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
2966                                 lpszVersion, lpszReferrer, lpszAcceptTypes,
2967                                 dwFlags, dwContext, &handle);
2968 lend:
2969     if( session )
2970         WININET_Release( &session->hdr );
2971     TRACE("returning %p\n", handle);
2972     if(res != ERROR_SUCCESS)
2973         SetLastError(res);
2974     return handle;
2975 }
2976
2977 /* read any content returned by the server so that the connection can be
2978  * reused */
2979 static void HTTP_DrainContent(http_request_t *req)
2980 {
2981     DWORD bytes_read;
2982
2983     if (!NETCON_connected(&req->netConnection)) return;
2984
2985     if (req->contentLength == -1)
2986     {
2987         NETCON_close(&req->netConnection);
2988         return;
2989     }
2990     if (!strcmpW(req->verb, szHEAD)) return;
2991
2992     do
2993     {
2994         char buffer[2048];
2995         if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2996             return;
2997     } while (bytes_read);
2998 }
2999
3000 static const LPCWSTR header_lookup[] = {
3001     szMime_Version,             /* HTTP_QUERY_MIME_VERSION = 0 */
3002     szContent_Type,             /* HTTP_QUERY_CONTENT_TYPE = 1 */
3003     szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3004     szContent_ID,               /* HTTP_QUERY_CONTENT_ID = 3 */
3005     NULL,                       /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3006     szContent_Length,           /* HTTP_QUERY_CONTENT_LENGTH =  5 */
3007     szContent_Language,         /* HTTP_QUERY_CONTENT_LANGUAGE =  6 */
3008     szAllow,                    /* HTTP_QUERY_ALLOW = 7 */
3009     szPublic,                   /* HTTP_QUERY_PUBLIC = 8 */
3010     szDate,                     /* HTTP_QUERY_DATE = 9 */
3011     szExpires,                  /* HTTP_QUERY_EXPIRES = 10 */
3012     szLast_Modified,            /* HTTP_QUERY_LAST_MODIFIED = 11 */
3013     NULL,                       /* HTTP_QUERY_MESSAGE_ID = 12 */
3014     szURI,                      /* HTTP_QUERY_URI = 13 */
3015     szFrom,                     /* HTTP_QUERY_DERIVED_FROM = 14 */
3016     NULL,                       /* HTTP_QUERY_COST = 15 */
3017     NULL,                       /* HTTP_QUERY_LINK = 16 */
3018     szPragma,                   /* HTTP_QUERY_PRAGMA = 17 */
3019     NULL,                       /* HTTP_QUERY_VERSION = 18 */
3020     szStatus,                   /* HTTP_QUERY_STATUS_CODE = 19 */
3021     NULL,                       /* HTTP_QUERY_STATUS_TEXT = 20 */
3022     NULL,                       /* HTTP_QUERY_RAW_HEADERS = 21 */
3023     NULL,                       /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3024     szConnection,               /* HTTP_QUERY_CONNECTION = 23 */
3025     szAccept,                   /* HTTP_QUERY_ACCEPT = 24 */
3026     szAccept_Charset,           /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3027     szAccept_Encoding,          /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3028     szAccept_Language,          /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3029     szAuthorization,            /* HTTP_QUERY_AUTHORIZATION = 28 */
3030     szContent_Encoding,         /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3031     NULL,                       /* HTTP_QUERY_FORWARDED = 30 */
3032     NULL,                       /* HTTP_QUERY_FROM = 31 */
3033     szIf_Modified_Since,        /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3034     szLocation,                 /* HTTP_QUERY_LOCATION = 33 */
3035     NULL,                       /* HTTP_QUERY_ORIG_URI = 34 */
3036     szReferer,                  /* HTTP_QUERY_REFERER = 35 */
3037     szRetry_After,              /* HTTP_QUERY_RETRY_AFTER = 36 */
3038     szServer,                   /* HTTP_QUERY_SERVER = 37 */
3039     NULL,                       /* HTTP_TITLE = 38 */
3040     szUser_Agent,               /* HTTP_QUERY_USER_AGENT = 39 */
3041     szWWW_Authenticate,         /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3042     szProxy_Authenticate,       /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3043     szAccept_Ranges,            /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3044     szSet_Cookie,               /* HTTP_QUERY_SET_COOKIE = 43 */
3045     szCookie,                   /* HTTP_QUERY_COOKIE = 44 */
3046     NULL,                       /* HTTP_QUERY_REQUEST_METHOD = 45 */
3047     NULL,                       /* HTTP_QUERY_REFRESH = 46 */
3048     NULL,                       /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3049     szAge,                      /* HTTP_QUERY_AGE = 48 */
3050     szCache_Control,            /* HTTP_QUERY_CACHE_CONTROL = 49 */
3051     szContent_Base,             /* HTTP_QUERY_CONTENT_BASE = 50 */
3052     szContent_Location,         /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3053     szContent_MD5,              /* HTTP_QUERY_CONTENT_MD5 = 52 */
3054     szContent_Range,            /* HTTP_QUERY_CONTENT_RANGE = 53 */
3055     szETag,                     /* HTTP_QUERY_ETAG = 54 */
3056     hostW,                      /* HTTP_QUERY_HOST = 55 */
3057     szIf_Match,                 /* HTTP_QUERY_IF_MATCH = 56 */
3058     szIf_None_Match,            /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3059     szIf_Range,                 /* HTTP_QUERY_IF_RANGE = 58 */
3060     szIf_Unmodified_Since,      /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3061     szMax_Forwards,             /* HTTP_QUERY_MAX_FORWARDS = 60 */
3062     szProxy_Authorization,      /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3063     szRange,                    /* HTTP_QUERY_RANGE = 62 */
3064     szTransfer_Encoding,        /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3065     szUpgrade,                  /* HTTP_QUERY_UPGRADE = 64 */
3066     szVary,                     /* HTTP_QUERY_VARY = 65 */
3067     szVia,                      /* HTTP_QUERY_VIA = 66 */
3068     szWarning,                  /* HTTP_QUERY_WARNING = 67 */
3069     szExpect,                   /* HTTP_QUERY_EXPECT = 68 */
3070     szProxy_Connection,         /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3071     szUnless_Modified_Since,    /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3072 };
3073
3074 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3075
3076 /***********************************************************************
3077  *           HTTP_HttpQueryInfoW (internal)
3078  */
3079 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3080         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3081 {
3082     LPHTTPHEADERW lphttpHdr = NULL;
3083     BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3084     INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3085     DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3086     INT index = -1;
3087
3088     /* Find requested header structure */
3089     switch (level)
3090     {
3091     case HTTP_QUERY_CUSTOM:
3092         if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3093         index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3094         break;
3095     case HTTP_QUERY_RAW_HEADERS_CRLF:
3096         {
3097             LPWSTR headers;
3098             DWORD len = 0;
3099             DWORD res = ERROR_INVALID_PARAMETER;
3100
3101             if (request_only)
3102                 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3103             else
3104                 headers = request->rawHeaders;
3105
3106             if (headers)
3107                 len = strlenW(headers) * sizeof(WCHAR);
3108
3109             if (len + sizeof(WCHAR) > *lpdwBufferLength)
3110             {
3111                 len += sizeof(WCHAR);
3112                 res = ERROR_INSUFFICIENT_BUFFER;
3113             }
3114             else if (lpBuffer)
3115             {
3116                 if (headers)
3117                     memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3118                 else
3119                 {
3120                     len = strlenW(szCrLf) * sizeof(WCHAR);
3121                     memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3122                 }
3123                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3124                 res = ERROR_SUCCESS;
3125             }
3126             *lpdwBufferLength = len;
3127
3128             if (request_only)
3129                 HeapFree(GetProcessHeap(), 0, headers);
3130             return res;
3131         }
3132     case HTTP_QUERY_RAW_HEADERS:
3133         {
3134             LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3135             DWORD i, size = 0;
3136             LPWSTR pszString = lpBuffer;
3137
3138             for (i = 0; ppszRawHeaderLines[i]; i++)
3139                 size += strlenW(ppszRawHeaderLines[i]) + 1;
3140
3141             if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3142             {
3143                 HTTP_FreeTokens(ppszRawHeaderLines);
3144                 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3145                 return ERROR_INSUFFICIENT_BUFFER;
3146             }
3147             if (pszString)
3148             {
3149                 for (i = 0; ppszRawHeaderLines[i]; i++)
3150                 {
3151                     DWORD len = strlenW(ppszRawHeaderLines[i]);
3152                     memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3153                     pszString += len+1;
3154                 }
3155                 *pszString = '\0';
3156                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3157             }
3158             *lpdwBufferLength = size * sizeof(WCHAR);
3159             HTTP_FreeTokens(ppszRawHeaderLines);
3160
3161             return ERROR_SUCCESS;
3162         }
3163     case HTTP_QUERY_STATUS_TEXT:
3164         if (request->statusText)
3165         {
3166             DWORD len = strlenW(request->statusText);
3167             if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3168             {
3169                 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3170                 return ERROR_INSUFFICIENT_BUFFER;
3171             }
3172             if (lpBuffer)
3173             {
3174                 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3175                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3176             }
3177             *lpdwBufferLength = len * sizeof(WCHAR);
3178             return ERROR_SUCCESS;
3179         }
3180         break;
3181     case HTTP_QUERY_VERSION:
3182         if (request->version)
3183         {
3184             DWORD len = strlenW(request->version);
3185             if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3186             {
3187                 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3188                 return ERROR_INSUFFICIENT_BUFFER;
3189             }
3190             if (lpBuffer)
3191             {
3192                 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3193                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3194             }
3195             *lpdwBufferLength = len * sizeof(WCHAR);
3196             return ERROR_SUCCESS;
3197         }
3198         break;
3199     case HTTP_QUERY_CONTENT_ENCODING:
3200         index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3201                 requested_index,request_only);
3202         break;
3203     default:
3204         assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3205
3206         if (level < LAST_TABLE_HEADER && header_lookup[level])
3207             index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3208                                               requested_index,request_only);
3209     }
3210
3211     if (index >= 0)
3212         lphttpHdr = &request->custHeaders[index];
3213
3214     /* Ensure header satisfies requested attributes */
3215     if (!lphttpHdr ||
3216         ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3217          (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3218     {
3219         return ERROR_HTTP_HEADER_NOT_FOUND;
3220     }
3221
3222     if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
3223
3224     /* coalesce value to requested type */
3225     if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3226     {
3227         *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3228         TRACE(" returning number: %d\n", *(int *)lpBuffer);
3229      }
3230     else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3231     {
3232         time_t tmpTime;
3233         struct tm tmpTM;
3234         SYSTEMTIME *STHook;
3235
3236         tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3237
3238         tmpTM = *gmtime(&tmpTime);
3239         STHook = (SYSTEMTIME *)lpBuffer;
3240         STHook->wDay = tmpTM.tm_mday;
3241         STHook->wHour = tmpTM.tm_hour;
3242         STHook->wMilliseconds = 0;
3243         STHook->wMinute = tmpTM.tm_min;
3244         STHook->wDayOfWeek = tmpTM.tm_wday;
3245         STHook->wMonth = tmpTM.tm_mon + 1;
3246         STHook->wSecond = tmpTM.tm_sec;
3247         STHook->wYear = tmpTM.tm_year;
3248
3249         TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3250               STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3251               STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3252     }
3253     else if (lphttpHdr->lpszValue)
3254     {
3255         DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3256
3257         if (len > *lpdwBufferLength)
3258         {
3259             *lpdwBufferLength = len;
3260             return ERROR_INSUFFICIENT_BUFFER;
3261         }
3262         if (lpBuffer)
3263         {
3264             memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3265             TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3266         }
3267         *lpdwBufferLength = len - sizeof(WCHAR);
3268     }
3269     return ERROR_SUCCESS;
3270 }
3271
3272 /***********************************************************************
3273  *           HttpQueryInfoW (WININET.@)
3274  *
3275  * Queries for information about an HTTP request
3276  *
3277  * RETURNS
3278  *    TRUE  on success
3279  *    FALSE on failure
3280  *
3281  */
3282 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3283         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3284 {
3285     http_request_t *request;
3286     DWORD res;
3287
3288     if (TRACE_ON(wininet)) {
3289 #define FE(x) { x, #x }
3290         static const wininet_flag_info query_flags[] = {
3291             FE(HTTP_QUERY_MIME_VERSION),
3292             FE(HTTP_QUERY_CONTENT_TYPE),
3293             FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3294             FE(HTTP_QUERY_CONTENT_ID),
3295             FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3296             FE(HTTP_QUERY_CONTENT_LENGTH),
3297             FE(HTTP_QUERY_CONTENT_LANGUAGE),
3298             FE(HTTP_QUERY_ALLOW),
3299             FE(HTTP_QUERY_PUBLIC),
3300             FE(HTTP_QUERY_DATE),
3301             FE(HTTP_QUERY_EXPIRES),
3302             FE(HTTP_QUERY_LAST_MODIFIED),
3303             FE(HTTP_QUERY_MESSAGE_ID),
3304             FE(HTTP_QUERY_URI),
3305             FE(HTTP_QUERY_DERIVED_FROM),
3306             FE(HTTP_QUERY_COST),
3307             FE(HTTP_QUERY_LINK),
3308             FE(HTTP_QUERY_PRAGMA),
3309             FE(HTTP_QUERY_VERSION),
3310             FE(HTTP_QUERY_STATUS_CODE),
3311             FE(HTTP_QUERY_STATUS_TEXT),
3312             FE(HTTP_QUERY_RAW_HEADERS),
3313             FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3314             FE(HTTP_QUERY_CONNECTION),
3315             FE(HTTP_QUERY_ACCEPT),
3316             FE(HTTP_QUERY_ACCEPT_CHARSET),
3317             FE(HTTP_QUERY_ACCEPT_ENCODING),
3318             FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3319             FE(HTTP_QUERY_AUTHORIZATION),
3320             FE(HTTP_QUERY_CONTENT_ENCODING),
3321             FE(HTTP_QUERY_FORWARDED),
3322             FE(HTTP_QUERY_FROM),
3323             FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3324             FE(HTTP_QUERY_LOCATION),
3325             FE(HTTP_QUERY_ORIG_URI),
3326             FE(HTTP_QUERY_REFERER),
3327             FE(HTTP_QUERY_RETRY_AFTER),
3328             FE(HTTP_QUERY_SERVER),
3329             FE(HTTP_QUERY_TITLE),
3330             FE(HTTP_QUERY_USER_AGENT),
3331             FE(HTTP_QUERY_WWW_AUTHENTICATE),
3332             FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3333             FE(HTTP_QUERY_ACCEPT_RANGES),
3334         FE(HTTP_QUERY_SET_COOKIE),
3335         FE(HTTP_QUERY_COOKIE),
3336             FE(HTTP_QUERY_REQUEST_METHOD),
3337             FE(HTTP_QUERY_REFRESH),
3338             FE(HTTP_QUERY_CONTENT_DISPOSITION),
3339             FE(HTTP_QUERY_AGE),
3340             FE(HTTP_QUERY_CACHE_CONTROL),
3341             FE(HTTP_QUERY_CONTENT_BASE),
3342             FE(HTTP_QUERY_CONTENT_LOCATION),
3343             FE(HTTP_QUERY_CONTENT_MD5),
3344             FE(HTTP_QUERY_CONTENT_RANGE),
3345             FE(HTTP_QUERY_ETAG),
3346             FE(HTTP_QUERY_HOST),
3347             FE(HTTP_QUERY_IF_MATCH),
3348             FE(HTTP_QUERY_IF_NONE_MATCH),
3349             FE(HTTP_QUERY_IF_RANGE),
3350             FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3351             FE(HTTP_QUERY_MAX_FORWARDS),
3352             FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3353             FE(HTTP_QUERY_RANGE),
3354             FE(HTTP_QUERY_TRANSFER_ENCODING),
3355             FE(HTTP_QUERY_UPGRADE),
3356             FE(HTTP_QUERY_VARY),
3357             FE(HTTP_QUERY_VIA),
3358             FE(HTTP_QUERY_WARNING),
3359             FE(HTTP_QUERY_CUSTOM)
3360         };
3361         static const wininet_flag_info modifier_flags[] = {
3362             FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3363             FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3364             FE(HTTP_QUERY_FLAG_NUMBER),
3365             FE(HTTP_QUERY_FLAG_COALESCE)
3366         };
3367 #undef FE
3368         DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3369         DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3370         DWORD i;
3371
3372         TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3373         TRACE("  Attribute:");
3374         for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3375             if (query_flags[i].val == info) {
3376                 TRACE(" %s", query_flags[i].name);
3377                 break;
3378             }
3379         }
3380         if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3381             TRACE(" Unknown (%08x)", info);
3382         }
3383
3384         TRACE(" Modifier:");
3385         for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3386             if (modifier_flags[i].val & info_mod) {
3387                 TRACE(" %s", modifier_flags[i].name);
3388                 info_mod &= ~ modifier_flags[i].val;
3389             }
3390         }
3391         
3392         if (info_mod) {
3393             TRACE(" Unknown (%08x)", info_mod);
3394         }
3395         TRACE("\n");
3396     }
3397     
3398     request = (http_request_t*) get_handle_object( hHttpRequest );
3399     if (NULL == request ||  request->hdr.htype != WH_HHTTPREQ)
3400     {
3401         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3402         goto lend;
3403     }
3404
3405     if (lpBuffer == NULL)
3406         *lpdwBufferLength = 0;
3407     res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3408                                lpBuffer, lpdwBufferLength, lpdwIndex);
3409
3410 lend:
3411     if( request )
3412          WININET_Release( &request->hdr );
3413
3414     TRACE("%u <--\n", res);
3415     if(res != ERROR_SUCCESS)
3416         SetLastError(res);
3417     return res == ERROR_SUCCESS;
3418 }
3419
3420 /***********************************************************************
3421  *           HttpQueryInfoA (WININET.@)
3422  *
3423  * Queries for information about an HTTP request
3424  *
3425  * RETURNS
3426  *    TRUE  on success
3427  *    FALSE on failure
3428  *
3429  */
3430 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3431         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3432 {
3433     BOOL result;
3434     DWORD len;
3435     WCHAR* bufferW;
3436
3437     if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3438        (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3439     {
3440         return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3441                                lpdwBufferLength, lpdwIndex );
3442     }
3443
3444     if (lpBuffer)
3445     {
3446         DWORD alloclen;
3447         len = (*lpdwBufferLength)*sizeof(WCHAR);
3448         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3449         {
3450             alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3451             if (alloclen < len)
3452                 alloclen = len;
3453         }
3454         else
3455             alloclen = len;
3456         bufferW = heap_alloc(alloclen);
3457         /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3458         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3459             MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3460     } else
3461     {
3462         bufferW = NULL;
3463         len = 0;
3464     }
3465
3466     result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3467                            &len, lpdwIndex );
3468     if( result )
3469     {
3470         len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3471                                      lpBuffer, *lpdwBufferLength, NULL, NULL );
3472         *lpdwBufferLength = len - 1;
3473
3474         TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3475     }
3476     else
3477         /* since the strings being returned from HttpQueryInfoW should be
3478          * only ASCII characters, it is reasonable to assume that all of
3479          * the Unicode characters can be reduced to a single byte */
3480         *lpdwBufferLength = len / sizeof(WCHAR);
3481
3482     HeapFree(GetProcessHeap(), 0, bufferW );
3483
3484     return result;
3485 }
3486
3487 /***********************************************************************
3488  *           HTTP_GetRedirectURL (internal)
3489  */
3490 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3491 {
3492     static WCHAR szHttp[] = {'h','t','t','p',0};
3493     static WCHAR szHttps[] = {'h','t','t','p','s',0};
3494     http_session_t *session = request->session;
3495     URL_COMPONENTSW urlComponents;
3496     DWORD url_length = 0;
3497     LPWSTR orig_url;
3498     LPWSTR combined_url;
3499
3500     urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3501     urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3502     urlComponents.dwSchemeLength = 0;
3503     urlComponents.lpszHostName = session->hostName;
3504     urlComponents.dwHostNameLength = 0;
3505     urlComponents.nPort = session->hostPort;
3506     urlComponents.lpszUserName = session->userName;
3507     urlComponents.dwUserNameLength = 0;
3508     urlComponents.lpszPassword = NULL;
3509     urlComponents.dwPasswordLength = 0;
3510     urlComponents.lpszUrlPath = request->path;
3511     urlComponents.dwUrlPathLength = 0;
3512     urlComponents.lpszExtraInfo = NULL;
3513     urlComponents.dwExtraInfoLength = 0;
3514
3515     if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3516         (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3517         return NULL;
3518
3519     orig_url = heap_alloc(url_length);
3520
3521     /* convert from bytes to characters */
3522     url_length = url_length / sizeof(WCHAR) - 1;
3523     if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3524     {
3525         HeapFree(GetProcessHeap(), 0, orig_url);
3526         return NULL;
3527     }
3528
3529     url_length = 0;
3530     if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3531         (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3532     {
3533         HeapFree(GetProcessHeap(), 0, orig_url);
3534         return NULL;
3535     }
3536     combined_url = heap_alloc(url_length * sizeof(WCHAR));
3537
3538     if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3539     {
3540         HeapFree(GetProcessHeap(), 0, orig_url);
3541         HeapFree(GetProcessHeap(), 0, combined_url);
3542         return NULL;
3543     }
3544     HeapFree(GetProcessHeap(), 0, orig_url);
3545     return combined_url;
3546 }
3547
3548
3549 /***********************************************************************
3550  *           HTTP_HandleRedirect (internal)
3551  */
3552 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3553 {
3554     http_session_t *session = request->session;
3555     appinfo_t *hIC = session->appInfo;
3556     BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3557     WCHAR path[INTERNET_MAX_URL_LENGTH];
3558     int index;
3559
3560     if(lpszUrl[0]=='/')
3561     {
3562         /* if it's an absolute path, keep the same session info */
3563         lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3564     }
3565     else
3566     {
3567         URL_COMPONENTSW urlComponents;
3568         WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3569         static WCHAR szHttp[] = {'h','t','t','p',0};
3570         static WCHAR szHttps[] = {'h','t','t','p','s',0};
3571
3572         userName[0] = 0;
3573         hostName[0] = 0;
3574         protocol[0] = 0;
3575
3576         urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3577         urlComponents.lpszScheme = protocol;
3578         urlComponents.dwSchemeLength = 32;
3579         urlComponents.lpszHostName = hostName;
3580         urlComponents.dwHostNameLength = MAXHOSTNAME;
3581         urlComponents.lpszUserName = userName;
3582         urlComponents.dwUserNameLength = 1024;
3583         urlComponents.lpszPassword = NULL;
3584         urlComponents.dwPasswordLength = 0;
3585         urlComponents.lpszUrlPath = path;
3586         urlComponents.dwUrlPathLength = 2048;
3587         urlComponents.lpszExtraInfo = NULL;
3588         urlComponents.dwExtraInfoLength = 0;
3589         if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3590             return INTERNET_GetLastError();
3591
3592         if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3593             (request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3594         {
3595             TRACE("redirect from secure page to non-secure page\n");
3596             /* FIXME: warn about from secure redirect to non-secure page */
3597             request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3598         }
3599         if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3600             !(request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3601         {
3602             TRACE("redirect from non-secure page to secure page\n");
3603             /* FIXME: notify about redirect to secure page */
3604             request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3605         }
3606
3607         if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3608         {
3609             if (lstrlenW(protocol)>4) /*https*/
3610                 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3611             else /*http*/
3612                 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3613         }
3614
3615 #if 0
3616         /*
3617          * This upsets redirects to binary files on sourceforge.net 
3618          * and gives an html page instead of the target file
3619          * Examination of the HTTP request sent by native wininet.dll
3620          * reveals that it doesn't send a referrer in that case.
3621          * Maybe there's a flag that enables this, or maybe a referrer
3622          * shouldn't be added in case of a redirect.
3623          */
3624
3625         /* consider the current host as the referrer */
3626         if (session->lpszServerName && *session->lpszServerName)
3627             HTTP_ProcessHeader(request, HTTP_REFERER, session->lpszServerName,
3628                            HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3629                            HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3630 #endif
3631         
3632         HeapFree(GetProcessHeap(), 0, session->hostName);
3633         if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3634             urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3635         {
3636             int len;
3637             static const WCHAR fmt[] = {'%','s',':','%','u',0};
3638             len = lstrlenW(hostName);
3639             len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3640             session->hostName = heap_alloc(len*sizeof(WCHAR));
3641             sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3642         }
3643         else
3644             session->hostName = heap_strdupW(hostName);
3645
3646         HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3647
3648         HeapFree(GetProcessHeap(), 0, session->userName);
3649         session->userName = NULL;
3650         if (userName[0])
3651             session->userName = heap_strdupW(userName);
3652
3653         if (!using_proxy)
3654         {
3655             if (strcmpiW(session->serverName, hostName) || session->serverPort != urlComponents.nPort)
3656             {
3657                 DWORD res;
3658
3659                 HeapFree(GetProcessHeap(), 0, session->serverName);
3660                 session->serverName = heap_strdupW(hostName);
3661                 session->serverPort = urlComponents.nPort;
3662
3663                 NETCON_close(&request->netConnection);
3664                 if ((res = HTTP_ResolveName(request)) != ERROR_SUCCESS)
3665                     return res;
3666
3667                 res = NETCON_init(&request->netConnection, request->hdr.dwFlags & INTERNET_FLAG_SECURE);
3668                 if (res != ERROR_SUCCESS)
3669                     return res;
3670
3671                 reset_data_stream(request);
3672             }
3673         }
3674         else
3675             TRACE("Redirect through proxy\n");
3676     }
3677
3678     HeapFree(GetProcessHeap(), 0, request->path);
3679     request->path=NULL;
3680     if (*path)
3681     {
3682         DWORD needed = 0;
3683         HRESULT rc;
3684
3685         rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3686         if (rc != E_POINTER)
3687             needed = strlenW(path)+1;
3688         request->path = heap_alloc(needed*sizeof(WCHAR));
3689         rc = UrlEscapeW(path, request->path, &needed,
3690                         URL_ESCAPE_SPACES_ONLY);
3691         if (rc != S_OK)
3692         {
3693             ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3694             strcpyW(request->path,path);
3695         }
3696     }
3697
3698     /* Remove custom content-type/length headers on redirects.  */
3699     index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3700     if (0 <= index)
3701         HTTP_DeleteCustomHeader(request, index);
3702     index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3703     if (0 <= index)
3704         HTTP_DeleteCustomHeader(request, index);
3705
3706     return ERROR_SUCCESS;
3707 }
3708
3709 /***********************************************************************
3710  *           HTTP_build_req (internal)
3711  *
3712  *  concatenate all the strings in the request together
3713  */
3714 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3715 {
3716     LPCWSTR *t;
3717     LPWSTR str;
3718
3719     for( t = list; *t ; t++  )
3720         len += strlenW( *t );
3721     len++;
3722
3723     str = heap_alloc(len*sizeof(WCHAR));
3724     *str = 0;
3725
3726     for( t = list; *t ; t++ )
3727         strcatW( str, *t );
3728
3729     return str;
3730 }
3731
3732 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3733 {
3734     LPWSTR lpszPath;
3735     LPWSTR requestString;
3736     INT len;
3737     INT cnt;
3738     INT responseLen;
3739     char *ascii_req;
3740     DWORD res;
3741     static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3742     static const WCHAR szFormat[] = {'%','s',':','%','u',0};
3743     http_session_t *session = request->session;
3744
3745     TRACE("\n");
3746
3747     lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
3748     sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
3749     requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
3750     HeapFree( GetProcessHeap(), 0, lpszPath );
3751
3752     len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3753                                 NULL, 0, NULL, NULL );
3754     len--; /* the nul terminator isn't needed */
3755     ascii_req = heap_alloc(len);
3756     WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3757                             ascii_req, len, NULL, NULL );
3758     HeapFree( GetProcessHeap(), 0, requestString );
3759
3760     TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3761
3762     res = NETCON_send( &request->netConnection, ascii_req, len, 0, &cnt );
3763     HeapFree( GetProcessHeap(), 0, ascii_req );
3764     if (res != ERROR_SUCCESS)
3765         return res;
3766
3767     responseLen = HTTP_GetResponseHeaders( request, TRUE );
3768     if (!responseLen)
3769         return ERROR_HTTP_INVALID_HEADER;
3770
3771     return ERROR_SUCCESS;
3772 }
3773
3774 static void HTTP_InsertCookies(http_request_t *request)
3775 {
3776     static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3777     LPWSTR lpszCookies, lpszUrl = NULL;
3778     DWORD nCookieSize, size;
3779     LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
3780
3781     size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(request->path)) * sizeof(WCHAR);
3782     if (!(lpszUrl = heap_alloc(size))) return;
3783     sprintfW( lpszUrl, szUrlForm, Host->lpszValue, request->path);
3784
3785     if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3786     {
3787         int cnt = 0;
3788         static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3789
3790         size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3791         if ((lpszCookies = heap_alloc(size)))
3792         {
3793             cnt += sprintfW(lpszCookies, szCookie);
3794             InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3795             strcatW(lpszCookies, szCrLf);
3796
3797             HTTP_HttpAddRequestHeadersW(request, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3798             HeapFree(GetProcessHeap(), 0, lpszCookies);
3799         }
3800     }
3801     HeapFree(GetProcessHeap(), 0, lpszUrl);
3802 }
3803
3804 static WORD HTTP_ParseDay(LPCWSTR day)
3805 {
3806     static const WCHAR days[7][4] = {{ 's','u','n',0 },
3807                                      { 'm','o','n',0 },
3808                                      { 't','u','e',0 },
3809                                      { 'w','e','d',0 },
3810                                      { 't','h','u',0 },
3811                                      { 'f','r','i',0 },
3812                                      { 's','a','t',0 }};
3813     int i;
3814     for (i = 0; i < sizeof(days)/sizeof(*days); i++)
3815         if (!strcmpiW(day, days[i]))
3816             return i;
3817
3818     /* Invalid */
3819     return 7;
3820 }
3821
3822 static WORD HTTP_ParseMonth(LPCWSTR month)
3823 {
3824     static const WCHAR jan[] = { 'j','a','n',0 };
3825     static const WCHAR feb[] = { 'f','e','b',0 };
3826     static const WCHAR mar[] = { 'm','a','r',0 };
3827     static const WCHAR apr[] = { 'a','p','r',0 };
3828     static const WCHAR may[] = { 'm','a','y',0 };
3829     static const WCHAR jun[] = { 'j','u','n',0 };
3830     static const WCHAR jul[] = { 'j','u','l',0 };
3831     static const WCHAR aug[] = { 'a','u','g',0 };
3832     static const WCHAR sep[] = { 's','e','p',0 };
3833     static const WCHAR oct[] = { 'o','c','t',0 };
3834     static const WCHAR nov[] = { 'n','o','v',0 };
3835     static const WCHAR dec[] = { 'd','e','c',0 };
3836
3837     if (!strcmpiW(month, jan)) return 1;
3838     if (!strcmpiW(month, feb)) return 2;
3839     if (!strcmpiW(month, mar)) return 3;
3840     if (!strcmpiW(month, apr)) return 4;
3841     if (!strcmpiW(month, may)) return 5;
3842     if (!strcmpiW(month, jun)) return 6;
3843     if (!strcmpiW(month, jul)) return 7;
3844     if (!strcmpiW(month, aug)) return 8;
3845     if (!strcmpiW(month, sep)) return 9;
3846     if (!strcmpiW(month, oct)) return 10;
3847     if (!strcmpiW(month, nov)) return 11;
3848     if (!strcmpiW(month, dec)) return 12;
3849     /* Invalid */
3850     return 0;
3851 }
3852
3853 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
3854  * optionally preceded by whitespace.
3855  * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
3856  * st, and sets *str to the first character after the time format.
3857  */
3858 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
3859 {
3860     LPCWSTR ptr = *str;
3861     WCHAR *nextPtr;
3862     unsigned long num;
3863
3864     while (isspaceW(*ptr))
3865         ptr++;
3866
3867     num = strtoulW(ptr, &nextPtr, 10);
3868     if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
3869     {
3870         ERR("unexpected time format %s\n", debugstr_w(ptr));
3871         return FALSE;
3872     }
3873     if (num > 23)
3874     {
3875         ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
3876         return FALSE;
3877     }
3878     ptr = nextPtr + 1;
3879     st->wHour = (WORD)num;
3880     num = strtoulW(ptr, &nextPtr, 10);
3881     if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
3882     {
3883         ERR("unexpected time format %s\n", debugstr_w(ptr));
3884         return FALSE;
3885     }
3886     if (num > 59)
3887     {
3888         ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
3889         return FALSE;
3890     }
3891     ptr = nextPtr + 1;
3892     st->wMinute = (WORD)num;
3893     num = strtoulW(ptr, &nextPtr, 10);
3894     if (!nextPtr || nextPtr <= ptr)
3895     {
3896         ERR("unexpected time format %s\n", debugstr_w(ptr));
3897         return FALSE;
3898     }
3899     if (num > 59)
3900     {
3901         ERR("unexpected second in time format %s\n", debugstr_w(ptr));
3902         return FALSE;
3903     }
3904     ptr = nextPtr + 1;
3905     *str = ptr;
3906     st->wSecond = (WORD)num;
3907     return TRUE;
3908 }
3909
3910 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
3911 {
3912     static const WCHAR gmt[]= { 'G','M','T',0 };
3913     WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
3914     LPCWSTR ptr;
3915     SYSTEMTIME st = { 0 };
3916     unsigned long num;
3917
3918     for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
3919          dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
3920         *dayPtr = *ptr;
3921     *dayPtr = 0;
3922     st.wDayOfWeek = HTTP_ParseDay(day);
3923     if (st.wDayOfWeek >= 7)
3924     {
3925         ERR("unexpected weekday %s\n", debugstr_w(day));
3926         return FALSE;
3927     }
3928
3929     while (isspaceW(*ptr))
3930         ptr++;
3931
3932     for (monthPtr = month; !isspace(*ptr) &&
3933          monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
3934          monthPtr++, ptr++)
3935         *monthPtr = *ptr;
3936     *monthPtr = 0;
3937     st.wMonth = HTTP_ParseMonth(month);
3938     if (!st.wMonth || st.wMonth > 12)
3939     {
3940         ERR("unexpected month %s\n", debugstr_w(month));
3941         return FALSE;
3942     }
3943
3944     while (isspaceW(*ptr))
3945         ptr++;
3946
3947     num = strtoulW(ptr, &nextPtr, 10);
3948     if (!nextPtr || nextPtr <= ptr || !num || num > 31)
3949     {
3950         ERR("unexpected day %s\n", debugstr_w(ptr));
3951         return FALSE;
3952     }
3953     ptr = nextPtr;
3954     st.wDay = (WORD)num;
3955
3956     while (isspaceW(*ptr))
3957         ptr++;
3958
3959     if (!HTTP_ParseTime(&st, &ptr))
3960         return FALSE;
3961
3962     while (isspaceW(*ptr))
3963         ptr++;
3964
3965     num = strtoulW(ptr, &nextPtr, 10);
3966     if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
3967     {
3968         ERR("unexpected year %s\n", debugstr_w(ptr));
3969         return FALSE;
3970     }
3971     ptr = nextPtr;
3972     st.wYear = (WORD)num;
3973
3974     while (isspaceW(*ptr))
3975         ptr++;
3976
3977     /* asctime() doesn't report a timezone, but some web servers do, so accept
3978      * with or without GMT.
3979      */
3980     if (*ptr && strcmpW(ptr, gmt))
3981     {
3982         ERR("unexpected timezone %s\n", debugstr_w(ptr));
3983         return FALSE;
3984     }
3985     return SystemTimeToFileTime(&st, ft);
3986 }
3987
3988 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
3989 {
3990     static const WCHAR gmt[]= { 'G','M','T',0 };
3991     WCHAR *nextPtr, day[4], month[4], *monthPtr;
3992     LPCWSTR ptr;
3993     unsigned long num;
3994     SYSTEMTIME st = { 0 };
3995
3996     ptr = strchrW(value, ',');
3997     if (!ptr)
3998         return FALSE;
3999     if (ptr - value != 3)
4000     {
4001         ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4002         return FALSE;
4003     }
4004     memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4005     day[3] = 0;
4006     st.wDayOfWeek = HTTP_ParseDay(day);
4007     if (st.wDayOfWeek > 6)
4008     {
4009         ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4010         return FALSE;
4011     }
4012     ptr++;
4013
4014     while (isspaceW(*ptr))
4015         ptr++;
4016
4017     num = strtoulW(ptr, &nextPtr, 10);
4018     if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4019     {
4020         ERR("unexpected day %s\n", debugstr_w(value));
4021         return FALSE;
4022     }
4023     ptr = nextPtr;
4024     st.wDay = (WORD)num;
4025
4026     while (isspaceW(*ptr))
4027         ptr++;
4028
4029     for (monthPtr = month; !isspace(*ptr) &&
4030          monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4031          monthPtr++, ptr++)
4032         *monthPtr = *ptr;
4033     *monthPtr = 0;
4034     st.wMonth = HTTP_ParseMonth(month);
4035     if (!st.wMonth || st.wMonth > 12)
4036     {
4037         ERR("unexpected month %s\n", debugstr_w(month));
4038         return FALSE;
4039     }
4040
4041     while (isspaceW(*ptr))
4042         ptr++;
4043
4044     num = strtoulW(ptr, &nextPtr, 10);
4045     if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4046     {
4047         ERR("unexpected year %s\n", debugstr_w(value));
4048         return FALSE;
4049     }
4050     ptr = nextPtr;
4051     st.wYear = (WORD)num;
4052
4053     if (!HTTP_ParseTime(&st, &ptr))
4054         return FALSE;
4055
4056     while (isspaceW(*ptr))
4057         ptr++;
4058
4059     if (strcmpW(ptr, gmt))
4060     {
4061         ERR("unexpected time zone %s\n", debugstr_w(ptr));
4062         return FALSE;
4063     }
4064     return SystemTimeToFileTime(&st, ft);
4065 }
4066
4067 /* FIXME: only accepts dates in RFC 1123 format and asctime() format,
4068  * which may not be the only formats actually seen in the wild.
4069  * http://www.hackcraft.net/web/datetime/ suggests at least RFC 850 dates
4070  * should be accepted as well.
4071  */
4072 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4073 {
4074     BOOL ret;
4075
4076     if (strchrW(value, ','))
4077         ret = HTTP_ParseRfc1123Date(value, ft);
4078     else
4079     {
4080         ret = HTTP_ParseDateAsAsctime(value, ft);
4081         if (!ret)
4082             ERR("unexpected date format %s\n", debugstr_w(value));
4083     }
4084     return ret;
4085 }
4086
4087 static void HTTP_ProcessExpires(http_request_t *request)
4088 {
4089     BOOL expirationFound = FALSE;
4090     int headerIndex;
4091
4092     /* Look for a Cache-Control header with a max-age directive, as it takes
4093      * precedence over the Expires header.
4094      */
4095     headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4096     if (headerIndex != -1)
4097     {
4098         LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4099         LPWSTR ptr;
4100
4101         for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4102         {
4103             LPWSTR comma = strchrW(ptr, ','), end, equal;
4104
4105             if (comma)
4106                 end = comma;
4107             else
4108                 end = ptr + strlenW(ptr);
4109             for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4110                 ;
4111             if (*equal == '=')
4112             {
4113                 static const WCHAR max_age[] = {
4114                     'm','a','x','-','a','g','e',0 };
4115
4116                 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4117                 {
4118                     LPWSTR nextPtr;
4119                     unsigned long age;
4120
4121                     age = strtoulW(equal + 1, &nextPtr, 10);
4122                     if (nextPtr > equal + 1)
4123                     {
4124                         LARGE_INTEGER ft;
4125
4126                         NtQuerySystemTime( &ft );
4127                         /* Age is in seconds, FILETIME resolution is in
4128                          * 100 nanosecond intervals.
4129                          */
4130                         ft.QuadPart += age * (ULONGLONG)1000000;
4131                         request->expires.dwLowDateTime = ft.u.LowPart;
4132                         request->expires.dwHighDateTime = ft.u.HighPart;
4133                         expirationFound = TRUE;
4134                     }
4135                 }
4136             }
4137             if (comma)
4138             {
4139                 ptr = comma + 1;
4140                 while (isspaceW(*ptr))
4141                     ptr++;
4142             }
4143             else
4144                 ptr = NULL;
4145         }
4146     }
4147     if (!expirationFound)
4148     {
4149         headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4150         if (headerIndex != -1)
4151         {
4152             LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4153             FILETIME ft;
4154
4155             if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4156             {
4157                 expirationFound = TRUE;
4158                 request->expires = ft;
4159             }
4160         }
4161     }
4162     if (!expirationFound)
4163     {
4164         LARGE_INTEGER t;
4165
4166         /* With no known age, default to 10 minutes until expiration. */
4167         NtQuerySystemTime( &t );
4168         t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4169         request->expires.dwLowDateTime = t.u.LowPart;
4170         request->expires.dwHighDateTime = t.u.HighPart;
4171     }
4172 }
4173
4174 static void HTTP_ProcessLastModified(http_request_t *request)
4175 {
4176     int headerIndex;
4177
4178     headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4179     if (headerIndex != -1)
4180     {
4181         LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4182         FILETIME ft;
4183
4184         if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4185             request->last_modified = ft;
4186     }
4187 }
4188
4189 static void HTTP_CacheRequest(http_request_t *request)
4190 {
4191     WCHAR url[INTERNET_MAX_URL_LENGTH];
4192     WCHAR cacheFileName[MAX_PATH+1];
4193     BOOL b;
4194
4195     b = HTTP_GetRequestURL(request, url);
4196     if(!b) {
4197         WARN("Could not get URL\n");
4198         return;
4199     }
4200
4201     b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4202     if(b) {
4203         HeapFree(GetProcessHeap(), 0, request->cacheFile);
4204         CloseHandle(request->hCacheFile);
4205
4206         request->cacheFile = heap_strdupW(cacheFileName);
4207         request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4208                   NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4209         if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4210             WARN("Could not create file: %u\n", GetLastError());
4211             request->hCacheFile = NULL;
4212         }
4213     }else {
4214         WARN("Could not create cache entry: %08x\n", GetLastError());
4215     }
4216 }
4217
4218 /***********************************************************************
4219  *           HTTP_HttpSendRequestW (internal)
4220  *
4221  * Sends the specified request to the HTTP server
4222  *
4223  * RETURNS
4224  *    ERROR_SUCCESS on success
4225  *    win32 error code on failure
4226  *
4227  */
4228 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4229         DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4230         DWORD dwContentLength, BOOL bEndRequest)
4231 {
4232     INT cnt;
4233     BOOL redirected = FALSE;
4234     LPWSTR requestString = NULL;
4235     INT responseLen;
4236     BOOL loop_next;
4237     INTERNET_ASYNC_RESULT iar;
4238     static const WCHAR szPost[] = { 'P','O','S','T',0 };
4239     static const WCHAR szContentLength[] =
4240         { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4241     WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4242     DWORD res;
4243
4244     TRACE("--> %p\n", request);
4245
4246     assert(request->hdr.htype == WH_HHTTPREQ);
4247
4248     /* if the verb is NULL default to GET */
4249     if (!request->verb)
4250         request->verb = heap_strdupW(szGET);
4251
4252     if (dwContentLength || strcmpW(request->verb, szGET))
4253     {
4254         sprintfW(contentLengthStr, szContentLength, dwContentLength);
4255         HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4256         request->bytesToWrite = dwContentLength;
4257     }
4258     if (request->session->appInfo->agent)
4259     {
4260         WCHAR *agent_header;
4261         static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4262         int len;
4263
4264         len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4265         agent_header = heap_alloc(len * sizeof(WCHAR));
4266         sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4267
4268         HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4269         HeapFree(GetProcessHeap(), 0, agent_header);
4270     }
4271     if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4272     {
4273         static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4274         HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4275     }
4276     if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4277     {
4278         static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4279                                               ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4280         HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4281     }
4282
4283     do
4284     {
4285         DWORD len;
4286         BOOL reusing_connection;
4287         char *ascii_req;
4288
4289         loop_next = FALSE;
4290
4291         /* like native, just in case the caller forgot to call InternetReadFile
4292          * for all the data */
4293         HTTP_DrainContent(request);
4294         if(redirected) {
4295             request->contentLength = ~0u;
4296             request->bytesToWrite = 0;
4297         }
4298
4299         if (TRACE_ON(wininet))
4300         {
4301             LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4302             TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4303         }
4304
4305         HTTP_FixURL(request);
4306         if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4307         {
4308             HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4309         }
4310         HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4311         HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4312
4313         if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4314             HTTP_InsertCookies(request);
4315
4316         /* add the headers the caller supplied */
4317         if( lpszHeaders && dwHeaderLength )
4318         {
4319             HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength,
4320                         HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4321         }
4322
4323         if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4324         {
4325             WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4326             requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4327             HeapFree(GetProcessHeap(), 0, url);
4328         }
4329         else
4330             requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4331
4332  
4333         TRACE("Request header -> %s\n", debugstr_w(requestString) );
4334
4335         /* Send the request and store the results */
4336         if(NETCON_connected(&request->netConnection))
4337             reusing_connection = TRUE;
4338         else
4339             reusing_connection = FALSE;
4340
4341         if ((res = HTTP_OpenConnection(request)) != ERROR_SUCCESS)
4342             goto lend;
4343
4344         /* send the request as ASCII, tack on the optional data */
4345         if (!lpOptional || redirected)
4346             dwOptionalLength = 0;
4347         len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4348                                    NULL, 0, NULL, NULL );
4349         ascii_req = heap_alloc(len + dwOptionalLength);
4350         WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4351                              ascii_req, len, NULL, NULL );
4352         if( lpOptional )
4353             memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4354         len = (len + dwOptionalLength - 1);
4355         ascii_req[len] = 0;
4356         TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4357
4358         INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4359                               INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4360
4361         res = NETCON_send(&request->netConnection, ascii_req, len, 0, &cnt);
4362         HeapFree( GetProcessHeap(), 0, ascii_req );
4363
4364         request->bytesWritten = dwOptionalLength;
4365
4366         INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4367                               INTERNET_STATUS_REQUEST_SENT,
4368                               &len, sizeof(DWORD));
4369
4370         if (bEndRequest)
4371         {
4372             DWORD dwBufferSize;
4373             DWORD dwStatusCode;
4374
4375             INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4376                                 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4377     
4378             if (res != ERROR_SUCCESS)
4379                 goto lend;
4380     
4381             responseLen = HTTP_GetResponseHeaders(request, TRUE);
4382             /* FIXME: We should know that connection is closed before sending
4383              * headers. Otherwise wrong callbacks are executed */
4384             if(!responseLen && reusing_connection) {
4385                 TRACE("Connection closed by server, reconnecting\n");
4386                 loop_next = TRUE;
4387                 continue;
4388             }
4389     
4390             INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4391                                 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4392                                 sizeof(DWORD));
4393
4394             HTTP_ProcessCookies(request);
4395             HTTP_ProcessExpires(request);
4396             HTTP_ProcessLastModified(request);
4397
4398             res = set_content_length(request);
4399             if(res != ERROR_SUCCESS)
4400                 goto lend;
4401             if(!request->contentLength)
4402                 HTTP_FinishedReading(request);
4403
4404             dwBufferSize = sizeof(dwStatusCode);
4405             if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4406                                     &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
4407                 dwStatusCode = 0;
4408
4409             if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4410             {
4411                 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4412                 dwBufferSize=sizeof(szNewLocation);
4413                 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
4414                      dwStatusCode == HTTP_STATUS_MOVED ||
4415                      dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
4416                     HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
4417                 {
4418                     if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD))
4419                     {
4420                         HeapFree(GetProcessHeap(), 0, request->verb);
4421                         request->verb = heap_strdupW(szGET);
4422                     }
4423                     HTTP_DrainContent(request);
4424                     if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4425                     {
4426                         INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4427                                               new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4428                         res = HTTP_HandleRedirect(request, new_url);
4429                         if (res == ERROR_SUCCESS)
4430                         {
4431                             HeapFree(GetProcessHeap(), 0, requestString);
4432                             loop_next = TRUE;
4433                         }
4434                         HeapFree( GetProcessHeap(), 0, new_url );
4435                     }
4436                     redirected = TRUE;
4437                 }
4438             }
4439             if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4440             {
4441                 WCHAR szAuthValue[2048];
4442                 dwBufferSize=2048;
4443                 if (dwStatusCode == HTTP_STATUS_DENIED)
4444                 {
4445                     LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4446                     DWORD dwIndex = 0;
4447                     while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4448                     {
4449                         if (HTTP_DoAuthorization(request, szAuthValue,
4450                                                  &request->authInfo,
4451                                                  request->session->userName,
4452                                                  request->session->password,
4453                                                  Host->lpszValue))
4454                         {
4455                             HeapFree(GetProcessHeap(), 0, requestString);
4456                             loop_next = TRUE;
4457                             break;
4458                         }
4459                     }
4460
4461                     if(!loop_next) {
4462                         TRACE("Cleaning wrong authorization data\n");
4463                         destroy_authinfo(request->authInfo);
4464                         request->authInfo = NULL;
4465                     }
4466                 }
4467                 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
4468                 {
4469                     DWORD dwIndex = 0;
4470                     while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4471                     {
4472                         if (HTTP_DoAuthorization(request, szAuthValue,
4473                                                  &request->proxyAuthInfo,
4474                                                  request->session->appInfo->proxyUsername,
4475                                                  request->session->appInfo->proxyPassword,
4476                                                  NULL))
4477                         {
4478                             loop_next = TRUE;
4479                             break;
4480                         }
4481                     }
4482
4483                     if(!loop_next) {
4484                         TRACE("Cleaning wrong proxy authorization data\n");
4485                         destroy_authinfo(request->proxyAuthInfo);
4486                         request->proxyAuthInfo = NULL;
4487                     }
4488                 }
4489             }
4490         }
4491         else
4492             res = ERROR_SUCCESS;
4493     }
4494     while (loop_next);
4495
4496     if(res == ERROR_SUCCESS)
4497         HTTP_CacheRequest(request);
4498
4499 lend:
4500
4501     HeapFree(GetProcessHeap(), 0, requestString);
4502
4503     /* TODO: send notification for P3P header */
4504
4505     if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4506     {
4507         if (res == ERROR_SUCCESS && request->bytesWritten == request->bytesToWrite)
4508             HTTP_ReceiveRequestData(request, TRUE);
4509         else
4510         {
4511             iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)request->hdr.hInternet : 0);
4512             iar.dwError = res;
4513
4514             INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4515                                   INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4516                                   sizeof(INTERNET_ASYNC_RESULT));
4517         }
4518     }
4519
4520     TRACE("<--\n");
4521     return res;
4522 }
4523
4524 /***********************************************************************
4525  *
4526  * Helper functions for the HttpSendRequest(Ex) functions
4527  *
4528  */
4529 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
4530 {
4531     struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4532     http_request_t *request = (http_request_t*) workRequest->hdr;
4533
4534     TRACE("%p\n", request);
4535
4536     HTTP_HttpSendRequestW(request, req->lpszHeader,
4537             req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
4538             req->dwContentLength, req->bEndRequest);
4539
4540     HeapFree(GetProcessHeap(), 0, req->lpszHeader);
4541 }
4542
4543
4544 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
4545 {
4546     INT responseLen;
4547     DWORD dwBufferSize;
4548     DWORD res = ERROR_SUCCESS;
4549
4550     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4551                   INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4552
4553     responseLen = HTTP_GetResponseHeaders(request, TRUE);
4554     if (!responseLen)
4555         res = ERROR_HTTP_HEADER_NOT_FOUND;
4556
4557     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4558                   INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
4559
4560     /* process cookies here. Is this right? */
4561     HTTP_ProcessCookies(request);
4562     HTTP_ProcessExpires(request);
4563     HTTP_ProcessLastModified(request);
4564
4565     if ((res = set_content_length( request )) == ERROR_SUCCESS) {
4566         if(!request->contentLength)
4567             HTTP_FinishedReading(request);
4568     }
4569
4570     if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
4571     {
4572         DWORD dwCode,dwCodeLength = sizeof(DWORD);
4573         if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
4574             && (dwCode == 302 || dwCode == 301 || dwCode == 303))
4575         {
4576             WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4577             dwBufferSize=sizeof(szNewLocation);
4578             if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
4579             {
4580                 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD))
4581                 {
4582                     HeapFree(GetProcessHeap(), 0, request->verb);
4583                     request->verb = heap_strdupW(szGET);
4584                 }
4585                 HTTP_DrainContent(request);
4586                 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4587                 {
4588                     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4589                                           new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4590                     res = HTTP_HandleRedirect(request, new_url);
4591                     if (res == ERROR_SUCCESS)
4592                         res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
4593                     HeapFree( GetProcessHeap(), 0, new_url );
4594                 }
4595             }
4596         }
4597     }
4598
4599     if (res == ERROR_SUCCESS) {
4600         HTTP_ReceiveRequestData(request, TRUE);
4601     }else {
4602         INTERNET_ASYNC_RESULT iar = {0, res};
4603
4604         INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4605                               INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4606                               sizeof(INTERNET_ASYNC_RESULT));
4607     }
4608
4609     return res;
4610 }
4611
4612 /***********************************************************************
4613  *           HttpEndRequestA (WININET.@)
4614  *
4615  * Ends an HTTP request that was started by HttpSendRequestEx
4616  *
4617  * RETURNS
4618  *    TRUE      if successful
4619  *    FALSE     on failure
4620  *
4621  */
4622 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
4623         LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4624 {
4625     TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
4626
4627     if (lpBuffersOut)
4628     {
4629         SetLastError(ERROR_INVALID_PARAMETER);
4630         return FALSE;
4631     }
4632
4633     return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
4634 }
4635
4636 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
4637 {
4638     struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
4639     http_request_t *request = (http_request_t*)work->hdr;
4640
4641     TRACE("%p\n", request);
4642
4643     HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
4644 }
4645
4646 /***********************************************************************
4647  *           HttpEndRequestW (WININET.@)
4648  *
4649  * Ends an HTTP request that was started by HttpSendRequestEx
4650  *
4651  * RETURNS
4652  *    TRUE      if successful
4653  *    FALSE     on failure
4654  *
4655  */
4656 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
4657         LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4658 {
4659     http_request_t *request;
4660     DWORD res;
4661
4662     TRACE("-->\n");
4663
4664     if (lpBuffersOut)
4665     {
4666         SetLastError(ERROR_INVALID_PARAMETER);
4667         return FALSE;
4668     }
4669
4670     request = (http_request_t*) get_handle_object( hRequest );
4671
4672     if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
4673     {
4674         SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
4675         if (request)
4676             WININET_Release( &request->hdr );
4677         return FALSE;
4678     }
4679     request->hdr.dwFlags |= dwFlags;
4680
4681     if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4682     {
4683         WORKREQUEST work;
4684         struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
4685
4686         work.asyncproc = AsyncHttpEndRequestProc;
4687         work.hdr = WININET_AddRef( &request->hdr );
4688
4689         work_endrequest = &work.u.HttpEndRequestW;
4690         work_endrequest->dwFlags = dwFlags;
4691         work_endrequest->dwContext = dwContext;
4692
4693         INTERNET_AsyncCall(&work);
4694         res = ERROR_IO_PENDING;
4695     }
4696     else
4697         res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
4698
4699     WININET_Release( &request->hdr );
4700     TRACE("%u <--\n", res);
4701     if(res != ERROR_SUCCESS)
4702         SetLastError(res);
4703     return res == ERROR_SUCCESS;
4704 }
4705
4706 /***********************************************************************
4707  *           HttpSendRequestExA (WININET.@)
4708  *
4709  * Sends the specified request to the HTTP server and allows chunked
4710  * transfers.
4711  *
4712  * RETURNS
4713  *  Success: TRUE
4714  *  Failure: FALSE, call GetLastError() for more information.
4715  */
4716 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
4717                                LPINTERNET_BUFFERSA lpBuffersIn,
4718                                LPINTERNET_BUFFERSA lpBuffersOut,
4719                                DWORD dwFlags, DWORD_PTR dwContext)
4720 {
4721     INTERNET_BUFFERSW BuffersInW;
4722     BOOL rc = FALSE;
4723     DWORD headerlen;
4724     LPWSTR header = NULL;
4725
4726     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4727             lpBuffersOut, dwFlags, dwContext);
4728
4729     if (lpBuffersIn)
4730     {
4731         BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
4732         if (lpBuffersIn->lpcszHeader)
4733         {
4734             headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
4735                     lpBuffersIn->dwHeadersLength,0,0);
4736             header = heap_alloc(headerlen*sizeof(WCHAR));
4737             if (!(BuffersInW.lpcszHeader = header))
4738             {
4739                 SetLastError(ERROR_OUTOFMEMORY);
4740                 return FALSE;
4741             }
4742             BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
4743                     lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4744                     header, headerlen);
4745         }
4746         else
4747             BuffersInW.lpcszHeader = NULL;
4748         BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
4749         BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
4750         BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
4751         BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
4752         BuffersInW.Next = NULL;
4753     }
4754
4755     rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
4756
4757     HeapFree(GetProcessHeap(),0,header);
4758
4759     return rc;
4760 }
4761
4762 /***********************************************************************
4763  *           HttpSendRequestExW (WININET.@)
4764  *
4765  * Sends the specified request to the HTTP server and allows chunked
4766  * transfers
4767  *
4768  * RETURNS
4769  *  Success: TRUE
4770  *  Failure: FALSE, call GetLastError() for more information.
4771  */
4772 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
4773                    LPINTERNET_BUFFERSW lpBuffersIn,
4774                    LPINTERNET_BUFFERSW lpBuffersOut,
4775                    DWORD dwFlags, DWORD_PTR dwContext)
4776 {
4777     http_request_t *request;
4778     http_session_t *session;
4779     appinfo_t *hIC;
4780     DWORD res;
4781
4782     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4783             lpBuffersOut, dwFlags, dwContext);
4784
4785     request = (http_request_t*) get_handle_object( hRequest );
4786
4787     if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
4788     {
4789         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4790         goto lend;
4791     }
4792
4793     session = request->session;
4794     assert(session->hdr.htype == WH_HHTTPSESSION);
4795     hIC = session->appInfo;
4796     assert(hIC->hdr.htype == WH_HINIT);
4797
4798     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4799     {
4800         WORKREQUEST workRequest;
4801         struct WORKREQ_HTTPSENDREQUESTW *req;
4802
4803         workRequest.asyncproc = AsyncHttpSendRequestProc;
4804         workRequest.hdr = WININET_AddRef( &request->hdr );
4805         req = &workRequest.u.HttpSendRequestW;
4806         if (lpBuffersIn)
4807         {
4808             DWORD size = 0;
4809
4810             if (lpBuffersIn->lpcszHeader)
4811             {
4812                 if (lpBuffersIn->dwHeadersLength == ~0u)
4813                     size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
4814                 else
4815                     size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
4816
4817                 req->lpszHeader = heap_alloc(size);
4818                 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
4819             }
4820             else req->lpszHeader = NULL;
4821
4822             req->dwHeaderLength = size / sizeof(WCHAR);
4823             req->lpOptional = lpBuffersIn->lpvBuffer;
4824             req->dwOptionalLength = lpBuffersIn->dwBufferLength;
4825             req->dwContentLength = lpBuffersIn->dwBufferTotal;
4826         }
4827         else
4828         {
4829             req->lpszHeader = NULL;
4830             req->dwHeaderLength = 0;
4831             req->lpOptional = NULL;
4832             req->dwOptionalLength = 0;
4833             req->dwContentLength = 0;
4834         }
4835
4836         req->bEndRequest = FALSE;
4837
4838         INTERNET_AsyncCall(&workRequest);
4839         /*
4840          * This is from windows.
4841          */
4842         res = ERROR_IO_PENDING;
4843     }
4844     else
4845     {
4846         if (lpBuffersIn)
4847             res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4848                                         lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
4849                                         lpBuffersIn->dwBufferTotal, FALSE);
4850         else
4851             res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
4852     }
4853
4854 lend:
4855     if ( request )
4856         WININET_Release( &request->hdr );
4857
4858     TRACE("<---\n");
4859     SetLastError(res);
4860     return res == ERROR_SUCCESS;
4861 }
4862
4863 /***********************************************************************
4864  *           HttpSendRequestW (WININET.@)
4865  *
4866  * Sends the specified request to the HTTP server
4867  *
4868  * RETURNS
4869  *    TRUE  on success
4870  *    FALSE on failure
4871  *
4872  */
4873 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4874         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4875 {
4876     http_request_t *request;
4877     http_session_t *session = NULL;
4878     appinfo_t *hIC = NULL;
4879     DWORD res = ERROR_SUCCESS;
4880
4881     TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4882             debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4883
4884     request = (http_request_t*) get_handle_object( hHttpRequest );
4885     if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
4886     {
4887         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4888         goto lend;
4889     }
4890
4891     session = request->session;
4892     if (NULL == session ||  session->hdr.htype != WH_HHTTPSESSION)
4893     {
4894         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4895         goto lend;
4896     }
4897
4898     hIC = session->appInfo;
4899     if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT)
4900     {
4901         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4902         goto lend;
4903     }
4904
4905     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4906     {
4907         WORKREQUEST workRequest;
4908         struct WORKREQ_HTTPSENDREQUESTW *req;
4909
4910         workRequest.asyncproc = AsyncHttpSendRequestProc;
4911         workRequest.hdr = WININET_AddRef( &request->hdr );
4912         req = &workRequest.u.HttpSendRequestW;
4913         if (lpszHeaders)
4914         {
4915             DWORD size;
4916
4917             if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4918             else size = dwHeaderLength * sizeof(WCHAR);
4919
4920             req->lpszHeader = heap_alloc(size);
4921             memcpy(req->lpszHeader, lpszHeaders, size);
4922         }
4923         else
4924             req->lpszHeader = 0;
4925         req->dwHeaderLength = dwHeaderLength;
4926         req->lpOptional = lpOptional;
4927         req->dwOptionalLength = dwOptionalLength;
4928         req->dwContentLength = dwOptionalLength;
4929         req->bEndRequest = TRUE;
4930
4931         INTERNET_AsyncCall(&workRequest);
4932         /*
4933          * This is from windows.
4934          */
4935         res = ERROR_IO_PENDING;
4936     }
4937     else
4938     {
4939         res = HTTP_HttpSendRequestW(request, lpszHeaders,
4940                 dwHeaderLength, lpOptional, dwOptionalLength,
4941                 dwOptionalLength, TRUE);
4942     }
4943 lend:
4944     if( request )
4945         WININET_Release( &request->hdr );
4946
4947     SetLastError(res);
4948     return res == ERROR_SUCCESS;
4949 }
4950
4951 /***********************************************************************
4952  *           HttpSendRequestA (WININET.@)
4953  *
4954  * Sends the specified request to the HTTP server
4955  *
4956  * RETURNS
4957  *    TRUE  on success
4958  *    FALSE on failure
4959  *
4960  */
4961 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4962         DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4963 {
4964     BOOL result;
4965     LPWSTR szHeaders=NULL;
4966     DWORD nLen=dwHeaderLength;
4967     if(lpszHeaders!=NULL)
4968     {
4969         nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4970         szHeaders = heap_alloc(nLen*sizeof(WCHAR));
4971         MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4972     }
4973     result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4974     HeapFree(GetProcessHeap(),0,szHeaders);
4975     return result;
4976 }
4977
4978 /***********************************************************************
4979  *           HTTPSESSION_Destroy (internal)
4980  *
4981  * Deallocate session handle
4982  *
4983  */
4984 static void HTTPSESSION_Destroy(object_header_t *hdr)
4985 {
4986     http_session_t *session = (http_session_t*) hdr;
4987
4988     TRACE("%p\n", session);
4989
4990     WININET_Release(&session->appInfo->hdr);
4991
4992     HeapFree(GetProcessHeap(), 0, session->hostName);
4993     HeapFree(GetProcessHeap(), 0, session->serverName);
4994     HeapFree(GetProcessHeap(), 0, session->password);
4995     HeapFree(GetProcessHeap(), 0, session->userName);
4996 }
4997
4998 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4999 {
5000     switch(option) {
5001     case INTERNET_OPTION_HANDLE_TYPE:
5002         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5003
5004         if (*size < sizeof(ULONG))
5005             return ERROR_INSUFFICIENT_BUFFER;
5006
5007         *size = sizeof(DWORD);
5008         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5009         return ERROR_SUCCESS;
5010     }
5011
5012     return INET_QueryOption(hdr, option, buffer, size, unicode);
5013 }
5014
5015 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5016 {
5017     http_session_t *ses = (http_session_t*)hdr;
5018
5019     switch(option) {
5020     case INTERNET_OPTION_USERNAME:
5021     {
5022         HeapFree(GetProcessHeap(), 0, ses->userName);
5023         if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5024         return ERROR_SUCCESS;
5025     }
5026     case INTERNET_OPTION_PASSWORD:
5027     {
5028         HeapFree(GetProcessHeap(), 0, ses->password);
5029         if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5030         return ERROR_SUCCESS;
5031     }
5032     default: break;
5033     }
5034
5035     return ERROR_INTERNET_INVALID_OPTION;
5036 }
5037
5038 static const object_vtbl_t HTTPSESSIONVtbl = {
5039     HTTPSESSION_Destroy,
5040     NULL,
5041     HTTPSESSION_QueryOption,
5042     HTTPSESSION_SetOption,
5043     NULL,
5044     NULL,
5045     NULL,
5046     NULL,
5047     NULL
5048 };
5049
5050
5051 /***********************************************************************
5052  *           HTTP_Connect  (internal)
5053  *
5054  * Create http session handle
5055  *
5056  * RETURNS
5057  *   HINTERNET a session handle on success
5058  *   NULL on failure
5059  *
5060  */
5061 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5062         INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5063         LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5064         DWORD dwInternalFlags, HINTERNET *ret)
5065 {
5066     http_session_t *session = NULL;
5067
5068     TRACE("-->\n");
5069
5070     if (!lpszServerName || !lpszServerName[0])
5071         return ERROR_INVALID_PARAMETER;
5072
5073     assert( hIC->hdr.htype == WH_HINIT );
5074
5075     session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5076     if (!session)
5077         return ERROR_OUTOFMEMORY;
5078
5079    /*
5080     * According to my tests. The name is not resolved until a request is sent
5081     */
5082
5083     session->hdr.htype = WH_HHTTPSESSION;
5084     session->hdr.dwFlags = dwFlags;
5085     session->hdr.dwContext = dwContext;
5086     session->hdr.dwInternalFlags |= dwInternalFlags;
5087
5088     WININET_AddRef( &hIC->hdr );
5089     session->appInfo = hIC;
5090     list_add_head( &hIC->hdr.children, &session->hdr.entry );
5091
5092     if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5093         if(hIC->proxyBypass)
5094             FIXME("Proxy bypass is ignored.\n");
5095     }
5096     session->serverName = heap_strdupW(lpszServerName);
5097     session->hostName = heap_strdupW(lpszServerName);
5098     if (lpszUserName && lpszUserName[0])
5099         session->userName = heap_strdupW(lpszUserName);
5100     if (lpszPassword && lpszPassword[0])
5101         session->password = heap_strdupW(lpszPassword);
5102     session->serverPort = serverPort;
5103     session->hostPort = serverPort;
5104
5105     /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5106     if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5107     {
5108         INTERNET_SendCallback(&hIC->hdr, dwContext,
5109                               INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5110                               sizeof(HINTERNET));
5111     }
5112
5113 /*
5114  * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5115  * windows
5116  */
5117
5118     TRACE("%p --> %p\n", hIC, session);
5119
5120     *ret = session->hdr.hInternet;
5121     return ERROR_SUCCESS;
5122 }
5123
5124
5125 /***********************************************************************
5126  *           HTTP_OpenConnection (internal)
5127  *
5128  * Connect to a web server
5129  *
5130  * RETURNS
5131  *
5132  *   TRUE  on success
5133  *   FALSE on failure
5134  */
5135 static DWORD HTTP_OpenConnection(http_request_t *request)
5136 {
5137     http_session_t *session;
5138     appinfo_t *hIC = NULL;
5139     char szaddr[INET6_ADDRSTRLEN];
5140     const void *addr;
5141     DWORD res = ERROR_SUCCESS;
5142
5143     TRACE("-->\n");
5144
5145
5146     if (request->hdr.htype != WH_HHTTPREQ)
5147     {
5148         res = ERROR_INVALID_PARAMETER;
5149         goto lend;
5150     }
5151
5152     if (NETCON_connected(&request->netConnection))
5153         goto lend;
5154     if ((res = HTTP_ResolveName(request)) != ERROR_SUCCESS) goto lend;
5155
5156     session = request->session;
5157
5158     hIC = session->appInfo;
5159     switch (session->socketAddress.ss_family)
5160     {
5161     case AF_INET:
5162         addr = &((struct sockaddr_in *)&session->socketAddress)->sin_addr;
5163         break;
5164     case AF_INET6:
5165         addr = &((struct sockaddr_in6 *)&session->socketAddress)->sin6_addr;
5166         break;
5167     default:
5168         WARN("unsupported family %d\n", session->socketAddress.ss_family);
5169         return ERROR_INTERNET_NAME_NOT_RESOLVED;
5170     }
5171     inet_ntop(session->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
5172     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5173                           INTERNET_STATUS_CONNECTING_TO_SERVER,
5174                           szaddr,
5175                           strlen(szaddr)+1);
5176
5177     res = NETCON_create(&request->netConnection, session->socketAddress.ss_family, SOCK_STREAM, 0);
5178     if (res != ERROR_SUCCESS)
5179     {
5180         WARN("Socket creation failed: %u\n", res);
5181         goto lend;
5182     }
5183
5184     res = NETCON_connect(&request->netConnection, (struct sockaddr *)&session->socketAddress,
5185                          session->sa_len);
5186     if(res != ERROR_SUCCESS)
5187        goto lend;
5188
5189     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5190             INTERNET_STATUS_CONNECTED_TO_SERVER,
5191             szaddr, strlen(szaddr)+1);
5192
5193     if (request->hdr.dwFlags & INTERNET_FLAG_SECURE)
5194     {
5195         /* Note: we differ from Microsoft's WinINet here. they seem to have
5196          * a bug that causes no status callbacks to be sent when starting
5197          * a tunnel to a proxy server using the CONNECT verb. i believe our
5198          * behaviour to be more correct and to not cause any incompatibilities
5199          * because using a secure connection through a proxy server is a rare
5200          * case that would be hard for anyone to depend on */
5201         if (hIC->proxy && (res = HTTP_SecureProxyConnect(request)) != ERROR_SUCCESS) {
5202             HTTPREQ_CloseConnection(&request->hdr);
5203             goto lend;
5204         }
5205
5206         res = NETCON_secure_connect(&request->netConnection, session->hostName);
5207         if(res != ERROR_SUCCESS)
5208         {
5209             WARN("Couldn't connect securely to host\n");
5210
5211             if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
5212                     res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
5213                     || res == ERROR_INTERNET_INVALID_CA
5214                     || res == ERROR_INTERNET_SEC_CERT_NO_REV
5215                     || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
5216                     || res == ERROR_INTERNET_SEC_CERT_REVOKED
5217                     || res == ERROR_INTERNET_SEC_INVALID_CERT
5218                     || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
5219                 res = ERROR_INTERNET_SEC_CERT_ERRORS;
5220
5221             HTTPREQ_CloseConnection(&request->hdr);
5222             goto lend;
5223         }
5224     }
5225
5226
5227 lend:
5228     reset_data_stream(request);
5229
5230     TRACE("%d <--\n", res);
5231     return res;
5232 }
5233
5234
5235 /***********************************************************************
5236  *           HTTP_clear_response_headers (internal)
5237  *
5238  * clear out any old response headers
5239  */
5240 static void HTTP_clear_response_headers( http_request_t *request )
5241 {
5242     DWORD i;
5243
5244     for( i=0; i<request->nCustHeaders; i++)
5245     {
5246         if( !request->custHeaders[i].lpszField )
5247             continue;
5248         if( !request->custHeaders[i].lpszValue )
5249             continue;
5250         if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5251             continue;
5252         HTTP_DeleteCustomHeader( request, i );
5253         i--;
5254     }
5255 }
5256
5257 /***********************************************************************
5258  *           HTTP_GetResponseHeaders (internal)
5259  *
5260  * Read server response
5261  *
5262  * RETURNS
5263  *
5264  *   TRUE  on success
5265  *   FALSE on error
5266  */
5267 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5268 {
5269     INT cbreaks = 0;
5270     WCHAR buffer[MAX_REPLY_LEN];
5271     DWORD buflen = MAX_REPLY_LEN;
5272     BOOL bSuccess = FALSE;
5273     INT  rc = 0;
5274     char bufferA[MAX_REPLY_LEN];
5275     LPWSTR status_code = NULL, status_text = NULL;
5276     DWORD cchMaxRawHeaders = 1024;
5277     LPWSTR lpszRawHeaders = NULL;
5278     LPWSTR temp;
5279     DWORD cchRawHeaders = 0;
5280     BOOL codeHundred = FALSE;
5281
5282     TRACE("-->\n");
5283
5284     if (!NETCON_connected(&request->netConnection))
5285         goto lend;
5286
5287     do {
5288         static const WCHAR szHundred[] = {'1','0','0',0};
5289         /*
5290          * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5291          */
5292         buflen = MAX_REPLY_LEN;
5293         if (!read_line(request, bufferA, &buflen))
5294             goto lend;
5295
5296         /* clear old response headers (eg. from a redirect response) */
5297         if (clear) {
5298             HTTP_clear_response_headers( request );
5299             clear = FALSE;
5300         }
5301
5302         rc += buflen;
5303         MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5304         /* check is this a status code line? */
5305         if (!strncmpW(buffer, g_szHttp1_0, 4))
5306         {
5307             /* split the version from the status code */
5308             status_code = strchrW( buffer, ' ' );
5309             if( !status_code )
5310                 goto lend;
5311             *status_code++=0;
5312
5313             /* split the status code from the status text */
5314             status_text = strchrW( status_code, ' ' );
5315             if( !status_text )
5316                 goto lend;
5317             *status_text++=0;
5318
5319             TRACE("version [%s] status code [%s] status text [%s]\n",
5320                debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5321
5322             codeHundred = (!strcmpW(status_code, szHundred));
5323         }
5324         else if (!codeHundred)
5325         {
5326             WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5327
5328             HeapFree(GetProcessHeap(), 0, request->version);
5329             HeapFree(GetProcessHeap(), 0, request->statusText);
5330
5331             request->version = heap_strdupW(g_szHttp1_0);
5332             request->statusText = heap_strdupW(szOK);
5333
5334             HeapFree(GetProcessHeap(), 0, request->rawHeaders);
5335             request->rawHeaders = heap_strdupW(szDefaultHeader);
5336
5337             bSuccess = TRUE;
5338             goto lend;
5339         }
5340     } while (codeHundred);
5341
5342     /* Add status code */
5343     HTTP_ProcessHeader(request, szStatus, status_code,
5344             HTTP_ADDHDR_FLAG_REPLACE);
5345
5346     HeapFree(GetProcessHeap(),0,request->version);
5347     HeapFree(GetProcessHeap(),0,request->statusText);
5348
5349     request->version = heap_strdupW(buffer);
5350     request->statusText = heap_strdupW(status_text);
5351
5352     /* Restore the spaces */
5353     *(status_code-1) = ' ';
5354     *(status_text-1) = ' ';
5355
5356     /* regenerate raw headers */
5357     lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5358     if (!lpszRawHeaders) goto lend;
5359
5360     while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5361         cchMaxRawHeaders *= 2;
5362     temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5363     if (temp == NULL) goto lend;
5364     lpszRawHeaders = temp;
5365     memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5366     cchRawHeaders += (buflen-1);
5367     memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5368     cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5369     lpszRawHeaders[cchRawHeaders] = '\0';
5370
5371     /* Parse each response line */
5372     do
5373     {
5374         buflen = MAX_REPLY_LEN;
5375         if (read_line(request, bufferA, &buflen))
5376         {
5377             LPWSTR * pFieldAndValue;
5378
5379             TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5380
5381             if (!bufferA[0]) break;
5382             MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5383
5384             pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5385             if (pFieldAndValue)
5386             {
5387                 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5388                     cchMaxRawHeaders *= 2;
5389                 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5390                 if (temp == NULL) goto lend;
5391                 lpszRawHeaders = temp;
5392                 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5393                 cchRawHeaders += (buflen-1);
5394                 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5395                 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5396                 lpszRawHeaders[cchRawHeaders] = '\0';
5397
5398                 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5399                                    HTTP_ADDREQ_FLAG_ADD );
5400
5401                 HTTP_FreeTokens(pFieldAndValue);
5402             }
5403         }
5404         else
5405         {
5406             cbreaks++;
5407             if (cbreaks >= 2)
5408                break;
5409         }
5410     }while(1);
5411
5412     /* make sure the response header is terminated with an empty line.  Some apps really
5413        truly care about that empty line being there for some reason.  Just add it to the
5414        header. */
5415     if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5416     {
5417         cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5418         temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5419         if (temp == NULL) goto lend;
5420         lpszRawHeaders = temp;
5421     }
5422
5423     memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5424
5425     HeapFree(GetProcessHeap(), 0, request->rawHeaders);
5426     request->rawHeaders = lpszRawHeaders;
5427     TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5428     bSuccess = TRUE;
5429
5430 lend:
5431
5432     TRACE("<--\n");
5433     if (bSuccess)
5434         return rc;
5435     else
5436     {
5437         HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
5438         return 0;
5439     }
5440 }
5441
5442 /***********************************************************************
5443  *           HTTP_InterpretHttpHeader (internal)
5444  *
5445  * Parse server response
5446  *
5447  * RETURNS
5448  *
5449  *   Pointer to array of field, value, NULL on success.
5450  *   NULL on error.
5451  */
5452 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5453 {
5454     LPWSTR * pTokenPair;
5455     LPWSTR pszColon;
5456     INT len;
5457
5458     pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5459
5460     pszColon = strchrW(buffer, ':');
5461     /* must have two tokens */
5462     if (!pszColon)
5463     {
5464         HTTP_FreeTokens(pTokenPair);
5465         if (buffer[0])
5466             TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5467         return NULL;
5468     }
5469
5470     pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5471     if (!pTokenPair[0])
5472     {
5473         HTTP_FreeTokens(pTokenPair);
5474         return NULL;
5475     }
5476     memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5477     pTokenPair[0][pszColon - buffer] = '\0';
5478
5479     /* skip colon */
5480     pszColon++;
5481     len = strlenW(pszColon);
5482     pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5483     if (!pTokenPair[1])
5484     {
5485         HTTP_FreeTokens(pTokenPair);
5486         return NULL;
5487     }
5488     memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5489
5490     strip_spaces(pTokenPair[0]);
5491     strip_spaces(pTokenPair[1]);
5492
5493     TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5494     return pTokenPair;
5495 }
5496
5497 /***********************************************************************
5498  *           HTTP_ProcessHeader (internal)
5499  *
5500  * Stuff header into header tables according to <dwModifier>
5501  *
5502  */
5503
5504 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5505
5506 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5507 {
5508     LPHTTPHEADERW lphttpHdr = NULL;
5509     INT index = -1;
5510     BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5511     DWORD res = ERROR_HTTP_INVALID_HEADER;
5512
5513     TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5514
5515     /* REPLACE wins out over ADD */
5516     if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5517         dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5518     
5519     if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5520         index = -1;
5521     else
5522         index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5523
5524     if (index >= 0)
5525     {
5526         if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5527             return ERROR_HTTP_INVALID_HEADER;
5528         lphttpHdr = &request->custHeaders[index];
5529     }
5530     else if (value)
5531     {
5532         HTTPHEADERW hdr;
5533
5534         hdr.lpszField = (LPWSTR)field;
5535         hdr.lpszValue = (LPWSTR)value;
5536         hdr.wFlags = hdr.wCount = 0;
5537
5538         if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5539             hdr.wFlags |= HDR_ISREQUEST;
5540
5541         return HTTP_InsertCustomHeader(request, &hdr);
5542     }
5543     /* no value to delete */
5544     else return ERROR_SUCCESS;
5545
5546     if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5547             lphttpHdr->wFlags |= HDR_ISREQUEST;
5548     else
5549         lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5550
5551     if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5552     {
5553         HTTP_DeleteCustomHeader( request, index );
5554
5555         if (value)
5556         {
5557             HTTPHEADERW hdr;
5558
5559             hdr.lpszField = (LPWSTR)field;
5560             hdr.lpszValue = (LPWSTR)value;
5561             hdr.wFlags = hdr.wCount = 0;
5562
5563             if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5564                 hdr.wFlags |= HDR_ISREQUEST;
5565
5566             return HTTP_InsertCustomHeader(request, &hdr);
5567         }
5568
5569         return ERROR_SUCCESS;
5570     }
5571     else if (dwModifier & COALESCEFLAGS)
5572     {
5573         LPWSTR lpsztmp;
5574         WCHAR ch = 0;
5575         INT len = 0;
5576         INT origlen = strlenW(lphttpHdr->lpszValue);
5577         INT valuelen = strlenW(value);
5578
5579         if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5580         {
5581             ch = ',';
5582             lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5583         }
5584         else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5585         {
5586             ch = ';';
5587             lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5588         }
5589
5590         len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5591
5592         lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5593         if (lpsztmp)
5594         {
5595             lphttpHdr->lpszValue = lpsztmp;
5596     /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5597             if (ch > 0)
5598             {
5599                 lphttpHdr->lpszValue[origlen] = ch;
5600                 origlen++;
5601                 lphttpHdr->lpszValue[origlen] = ' ';
5602                 origlen++;
5603             }
5604
5605             memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5606             lphttpHdr->lpszValue[len] = '\0';
5607             res = ERROR_SUCCESS;
5608         }
5609         else
5610         {
5611             WARN("heap_realloc (%d bytes) failed\n",len+1);
5612             res = ERROR_OUTOFMEMORY;
5613         }
5614     }
5615     TRACE("<-- %d\n", res);
5616     return res;
5617 }
5618
5619
5620 /***********************************************************************
5621  *           HTTP_FinishedReading (internal)
5622  *
5623  * Called when all content from server has been read by client.
5624  *
5625  */
5626 static BOOL HTTP_FinishedReading(http_request_t *request)
5627 {
5628     BOOL keepalive = HTTP_KeepAlive(request);
5629
5630     TRACE("\n");
5631
5632
5633     if (!keepalive)
5634     {
5635         HTTPREQ_CloseConnection(&request->hdr);
5636     }
5637
5638     /* FIXME: store data in the URL cache here */
5639
5640     return TRUE;
5641 }
5642
5643
5644 /***********************************************************************
5645  *           HTTP_GetCustomHeaderIndex (internal)
5646  *
5647  * Return index of custom header from header array
5648  *
5649  */
5650 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
5651                                      int requested_index, BOOL request_only)
5652 {
5653     DWORD index;
5654
5655     TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5656
5657     for (index = 0; index < request->nCustHeaders; index++)
5658     {
5659         if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
5660             continue;
5661
5662         if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
5663             continue;
5664
5665         if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
5666             continue;
5667
5668         if (requested_index == 0)
5669             break;
5670         requested_index --;
5671     }
5672
5673     if (index >= request->nCustHeaders)
5674         index = -1;
5675
5676     TRACE("Return: %d\n", index);
5677     return index;
5678 }
5679
5680
5681 /***********************************************************************
5682  *           HTTP_InsertCustomHeader (internal)
5683  *
5684  * Insert header into array
5685  *
5686  */
5687 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
5688 {
5689     INT count;
5690     LPHTTPHEADERW lph = NULL;
5691
5692     TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5693     count = request->nCustHeaders + 1;
5694     if (count > 1)
5695         lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
5696     else
5697         lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
5698
5699     if (!lph)
5700         return ERROR_OUTOFMEMORY;
5701
5702     request->custHeaders = lph;
5703     request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
5704     request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
5705     request->custHeaders[count-1].wFlags = lpHdr->wFlags;
5706     request->custHeaders[count-1].wCount= lpHdr->wCount;
5707     request->nCustHeaders++;
5708
5709     return ERROR_SUCCESS;
5710 }
5711
5712
5713 /***********************************************************************
5714  *           HTTP_DeleteCustomHeader (internal)
5715  *
5716  * Delete header from array
5717  *  If this function is called, the indexs may change.
5718  */
5719 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
5720 {
5721     if( request->nCustHeaders <= 0 )
5722         return FALSE;
5723     if( index >= request->nCustHeaders )
5724         return FALSE;
5725     request->nCustHeaders--;
5726
5727     HeapFree(GetProcessHeap(), 0, request->custHeaders[index].lpszField);
5728     HeapFree(GetProcessHeap(), 0, request->custHeaders[index].lpszValue);
5729
5730     memmove( &request->custHeaders[index], &request->custHeaders[index+1],
5731              (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
5732     memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
5733
5734     return TRUE;
5735 }
5736
5737
5738 /***********************************************************************
5739  *           HTTP_VerifyValidHeader (internal)
5740  *
5741  * Verify the given header is not invalid for the given http request
5742  *
5743  */
5744 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
5745 {
5746     /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5747     if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
5748         return ERROR_HTTP_INVALID_HEADER;
5749
5750     return ERROR_SUCCESS;
5751 }
5752
5753 /***********************************************************************
5754  *          IsHostInProxyBypassList (@)
5755  *
5756  * Undocumented
5757  *
5758  */
5759 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
5760 {
5761    FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
5762    return FALSE;
5763 }
5764
5765 /***********************************************************************
5766  *           InternetShowSecurityInfoByURLA (@)
5767  */
5768 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
5769 {
5770    FIXME("stub: %s %p\n", url, window);
5771    return FALSE;
5772 }
5773
5774 /***********************************************************************
5775  *           InternetShowSecurityInfoByURLW (@)
5776  */
5777 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
5778 {
5779    FIXME("stub: %s %p\n", debugstr_w(url), window);
5780    return FALSE;
5781 }