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