http.c 195 KB
Newer Older
1
/*
2
 * Wininet - HTTP Implementation
3 4
 *
 * Copyright 1999 Corel Corporation
5
 * Copyright 2002 CodeWeavers Inc.
6
 * Copyright 2002 TransGaming Technologies Inc.
7
 * Copyright 2004 Mike McCormack for CodeWeavers
8
 * Copyright 2005 Aric Stewart for CodeWeavers
9
 * Copyright 2006 Robert Shearman for CodeWeavers
10
 * Copyright 2011 Jacek Caban for CodeWeavers
11 12
 *
 * Ulrich Czekalla
13
 * David Hammerton
14
 *
15 16 17 18 19 20 21 22 23 24 25 26
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
27
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 29
 */

Patrik Stridvall's avatar
Patrik Stridvall committed
30
#include "config.h"
31
#include "wine/port.h"
Patrik Stridvall's avatar
Patrik Stridvall committed
32

33 34 35 36
#if defined(__MINGW32__) || defined (_MSC_VER)
#include <ws2tcpip.h>
#endif

37
#include <sys/types.h>
Patrik Stridvall's avatar
Patrik Stridvall committed
38 39 40
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
41 42 43
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
44
#include <stdarg.h>
45 46
#include <stdio.h>
#include <stdlib.h>
47 48 49
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
50
#include <time.h>
51
#include <assert.h>
52 53 54
#ifdef HAVE_ZLIB
#  include <zlib.h>
#endif
55

56 57 58 59
#include "windef.h"
#include "winbase.h"
#include "wininet.h"
#include "winerror.h"
60
#include "winternl.h"
61
#define NO_SHLWAPI_STREAM
62 63 64
#define NO_SHLWAPI_REG
#define NO_SHLWAPI_STRFCNS
#define NO_SHLWAPI_GDI
65
#include "shlwapi.h"
66
#include "sspi.h"
67
#include "wincrypt.h"
68 69
#include "winuser.h"
#include "cryptuiapi.h"
70

71
#include "internet.h"
72
#include "wine/debug.h"
73
#include "wine/exception.h"
74
#include "wine/unicode.h"
75

76
WINE_DEFAULT_DEBUG_CHANNEL(wininet);
77

78
static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
79
static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
80 81
static const WCHAR szOK[] = {'O','K',0};
static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
82
static const WCHAR hostW[] = { 'H','o','s','t',0 };
83
static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
84 85
static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
86
static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
Jacek Caban's avatar
Jacek Caban committed
87
static const WCHAR szGET[] = { 'G','E','T', 0 };
88
static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
89
static const WCHAR szCrLf[] = {'\r','\n', 0};
90

91 92 93 94 95 96 97 98 99 100
static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
static const WCHAR szAge[] = { 'A','g','e',0 };
static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
101
static const WCHAR szContent_Disposition[] = { 'C','o','n','t','e','n','t','-','D','i','s','p','o','s','i','t','i','o','n',0 };
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
static const WCHAR szContent_Transfer_Encoding[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
static const WCHAR szDate[] = { 'D','a','t','e',0 };
static const WCHAR szFrom[] = { 'F','r','o','m',0 };
static const WCHAR szETag[] = { 'E','T','a','g',0 };
static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
static const WCHAR szUnless_Modified_Since[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
static const WCHAR szURI[] = { 'U','R','I',0 };
static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
static const WCHAR szVary[] = { 'V','a','r','y',0 };
static const WCHAR szVia[] = { 'V','i','a',0 };
static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };

#define HTTP_REFERER    szReferer
#define HTTP_ACCEPT     szAccept
#define HTTP_USERAGENT  szUser_Agent
148 149 150 151 152 153 154 155 156

#define HTTP_ADDHDR_FLAG_ADD				0x20000000
#define HTTP_ADDHDR_FLAG_ADD_IF_NEW			0x10000000
#define HTTP_ADDHDR_FLAG_COALESCE			0x40000000
#define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA		0x40000000
#define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON	0x01000000
#define HTTP_ADDHDR_FLAG_REPLACE			0x80000000
#define HTTP_ADDHDR_FLAG_REQ				0x02000000

157 158
#define COLLECT_TIME 60000

159 160 161 162 163 164 165 166 167
#define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))

struct HttpAuthInfo
{
    LPWSTR scheme;
    CredHandle cred;
    CtxtHandle ctx;
    TimeStamp exp;
    ULONG attr;
168
    ULONG max_token;
169 170 171 172
    void *auth_data;
    unsigned int auth_data_len;
    BOOL finished; /* finished authenticating */
};
173

174

175
typedef struct _basicAuthorizationData
176 177 178
{
    struct list entry;

179 180 181 182
    LPWSTR host;
    LPWSTR realm;
    LPSTR  authorization;
    UINT   authorizationLen;
183 184 185 186 187 188 189 190 191 192 193 194 195 196
} basicAuthorizationData;

typedef struct _authorizationData
{
    struct list entry;

    LPWSTR host;
    LPWSTR scheme;
    LPWSTR domain;
    UINT   domain_len;
    LPWSTR user;
    UINT   user_len;
    LPWSTR password;
    UINT   password_len;
197 198 199
} authorizationData;

static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
200
static struct list authorizationCache = LIST_INIT(authorizationCache);
201 202 203 204 205 206 207 208 209 210

static CRITICAL_SECTION authcache_cs;
static CRITICAL_SECTION_DEBUG critsect_debug =
{
    0, 0, &authcache_cs,
    { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
      0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
};
static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };

211
static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
212
static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
Jacek Caban's avatar
Jacek Caban committed
213
static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
214
static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
215 216
static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
217
static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
218
static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
219
static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
220
static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
221
static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
222
static BOOL drain_content(http_request_t*,BOOL);
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245

static CRITICAL_SECTION connection_pool_cs;
static CRITICAL_SECTION_DEBUG connection_pool_debug =
{
    0, 0, &connection_pool_cs,
    { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
      0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
};
static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };

static struct list connection_pool = LIST_INIT(connection_pool);
static BOOL collector_running;

void server_addref(server_t *server)
{
    InterlockedIncrement(&server->ref);
}

void server_release(server_t *server)
{
    if(InterlockedDecrement(&server->ref))
        return;

246 247
    list_remove(&server->entry);

248 249
    if(server->cert_chain)
        CertFreeCertificateChain(server->cert_chain);
250
    heap_free(server->name);
251
    heap_free(server->scheme_host_port);
252
    heap_free(server);
253 254
}

255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
static BOOL process_host_port(server_t *server)
{
    BOOL default_port;
    size_t name_len;
    WCHAR *buf;

    static const WCHAR httpW[] = {'h','t','t','p',0};
    static const WCHAR httpsW[] = {'h','t','t','p','s',0};
    static const WCHAR formatW[] = {'%','s',':','/','/','%','s',':','%','u',0};

    name_len = strlenW(server->name);
    buf = heap_alloc((name_len + 10 /* strlen("://:<port>") */)*sizeof(WCHAR) + sizeof(httpsW));
    if(!buf)
        return FALSE;

    sprintfW(buf, formatW, server->is_https ? httpsW : httpW, server->name, server->port);
    server->scheme_host_port = buf;

    server->host_port = server->scheme_host_port + 7 /* strlen("http://") */;
    if(server->is_https)
        server->host_port++;

    default_port = server->port == (server->is_https ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT);
    server->canon_host_port = default_port ? server->name : server->host_port;
    return TRUE;
}

282
server_t *get_server(const WCHAR *name, INTERNET_PORT port, BOOL is_https, BOOL do_create)
283 284 285
{
    server_t *iter, *server = NULL;

286 287 288
    if(port == INTERNET_INVALID_PORT_NUMBER)
        port = is_https ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT;

289 290 291
    EnterCriticalSection(&connection_pool_cs);

    LIST_FOR_EACH_ENTRY(iter, &connection_pool, server_t, entry) {
292
        if(iter->port == port && !strcmpW(iter->name, name) && iter->is_https == is_https) {
293 294 295 296 297 298
            server = iter;
            server_addref(server);
            break;
        }
    }

299 300
    if(!server && do_create) {
        server = heap_alloc_zero(sizeof(*server));
301
        if(server) {
302
            server->ref = 2; /* list reference and return */
303
            server->port = port;
304
            server->is_https = is_https;
305 306
            list_init(&server->conn_pool);
            server->name = heap_strdupW(name);
307
            if(server->name && process_host_port(server)) {
308 309 310 311 312 313 314 315 316 317 318 319 320
                list_add_head(&connection_pool, &server->entry);
            }else {
                heap_free(server);
                server = NULL;
            }
        }
    }

    LeaveCriticalSection(&connection_pool_cs);

    return server;
}

321
BOOL collect_connections(collect_type_t collect_type)
322 323 324 325 326 327 328 329 330 331
{
    netconn_t *netconn, *netconn_safe;
    server_t *server, *server_safe;
    BOOL remaining = FALSE;
    DWORD64 now;

    now = GetTickCount64();

    LIST_FOR_EACH_ENTRY_SAFE(server, server_safe, &connection_pool, server_t, entry) {
        LIST_FOR_EACH_ENTRY_SAFE(netconn, netconn_safe, &server->conn_pool, netconn_t, pool_entry) {
332
            if(collect_type > COLLECT_TIMEOUT || netconn->keep_until < now) {
333 334 335 336 337 338 339 340
                TRACE("freeing %p\n", netconn);
                list_remove(&netconn->pool_entry);
                free_netconn(netconn);
            }else {
                remaining = TRUE;
            }
        }

341
        if(collect_type == COLLECT_CLEANUP) {
342 343 344
            list_remove(&server->entry);
            list_init(&server->entry);
            server_release(server);
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
        }
    }

    return remaining;
}

static DWORD WINAPI collect_connections_proc(void *arg)
{
    BOOL remaining_conns;

    do {
        /* FIXME: Use more sophisticated method */
        Sleep(5000);

        EnterCriticalSection(&connection_pool_cs);

361
        remaining_conns = collect_connections(COLLECT_TIMEOUT);
362 363 364 365 366 367 368 369
        if(!remaining_conns)
            collector_running = FALSE;

        LeaveCriticalSection(&connection_pool_cs);
    }while(remaining_conns);

    FreeLibraryAndExitThread(WININET_hModule, 0);
}
370

371
static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
372 373 374 375 376 377
{
    int HeaderIndex = 0;
    HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
    if (HeaderIndex == -1)
        return NULL;
    else
378
        return &req->custHeaders[HeaderIndex];
379
}
380

381 382 383 384 385 386 387 388 389 390
typedef enum {
    READMODE_SYNC,
    READMODE_ASYNC,
    READMODE_NOBLOCK
} read_mode_t;

struct data_stream_vtbl_t {
    DWORD (*get_avail_data)(data_stream_t*,http_request_t*);
    BOOL (*end_of_data)(data_stream_t*,http_request_t*);
    DWORD (*read)(data_stream_t*,http_request_t*,BYTE*,DWORD,DWORD*,read_mode_t);
391
    BOOL (*drain_content)(data_stream_t*,http_request_t*);
392 393 394 395 396 397 398 399 400 401 402 403
    void (*destroy)(data_stream_t*);
};

typedef struct {
    data_stream_t data_stream;

    BYTE buf[READ_BUFFER_SIZE];
    DWORD buf_size;
    DWORD buf_pos;
    DWORD chunk_size;
} chunked_stream_t;

404
static inline void destroy_data_stream(data_stream_t *stream)
405 406 407 408 409 410 411 412 413 414 415 416
{
    stream->vtbl->destroy(stream);
}

static void reset_data_stream(http_request_t *req)
{
    destroy_data_stream(req->data_stream);
    req->data_stream = &req->netconn_stream.data_stream;
    req->read_pos = req->read_size = req->netconn_stream.content_read = 0;
    req->read_chunked = req->read_gzip = FALSE;
}

417 418
#ifdef HAVE_ZLIB

419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
typedef struct {
    data_stream_t stream;
    data_stream_t *parent_stream;
    z_stream zstream;
    BYTE buf[READ_BUFFER_SIZE];
    DWORD buf_size;
    DWORD buf_pos;
    BOOL end_of_data;
} gzip_stream_t;

static DWORD gzip_get_avail_data(data_stream_t *stream, http_request_t *req)
{
    /* Allow reading only from read buffer */
    return 0;
}

static BOOL gzip_end_of_data(data_stream_t *stream, http_request_t *req)
{
    gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
    return gzip_stream->end_of_data;
}

static DWORD gzip_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
        DWORD *read, read_mode_t read_mode)
{
    gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
    z_stream *zstream = &gzip_stream->zstream;
    DWORD current_read, ret_read = 0;
    BOOL end;
    int zres;
    DWORD res = ERROR_SUCCESS;

    while(size && !gzip_stream->end_of_data) {
        end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);

        if(gzip_stream->buf_size <= 64 && !end) {
            if(gzip_stream->buf_pos) {
                if(gzip_stream->buf_size)
                    memmove(gzip_stream->buf, gzip_stream->buf+gzip_stream->buf_pos, gzip_stream->buf_size);
                gzip_stream->buf_pos = 0;
            }
            res = gzip_stream->parent_stream->vtbl->read(gzip_stream->parent_stream, req, gzip_stream->buf+gzip_stream->buf_size,
                    sizeof(gzip_stream->buf)-gzip_stream->buf_size, &current_read, read_mode);
            gzip_stream->buf_size += current_read;
            if(res != ERROR_SUCCESS)
                break;
            end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
            if(!current_read && !end) {
                if(read_mode != READMODE_NOBLOCK) {
                    WARN("unexpected end of data\n");
                    gzip_stream->end_of_data = TRUE;
                }
                break;
            }
            if(gzip_stream->buf_size <= 64 && !end)
                continue;
        }

        zstream->next_in = gzip_stream->buf+gzip_stream->buf_pos;
        zstream->avail_in = gzip_stream->buf_size-(end ? 0 : 64);
        zstream->next_out = buf+ret_read;
        zstream->avail_out = size;
        zres = inflate(&gzip_stream->zstream, 0);
        current_read = size - zstream->avail_out;
        size -= current_read;
        ret_read += current_read;
        gzip_stream->buf_size -= zstream->next_in - (gzip_stream->buf+gzip_stream->buf_pos);
        gzip_stream->buf_pos = zstream->next_in-gzip_stream->buf;
        if(zres == Z_STREAM_END) {
            TRACE("end of data\n");
            gzip_stream->end_of_data = TRUE;
            inflateEnd(zstream);
        }else if(zres != Z_OK) {
            WARN("inflate failed %d: %s\n", zres, debugstr_a(zstream->msg));
            if(!ret_read)
                res = ERROR_INTERNET_DECODING_FAILED;
            break;
        }

        if(ret_read && read_mode == READMODE_ASYNC)
            read_mode = READMODE_NOBLOCK;
    }

    TRACE("read %u bytes\n", ret_read);
    *read = ret_read;
    return res;
}

507 508 509 510 511 512
static BOOL gzip_drain_content(data_stream_t *stream, http_request_t *req)
{
    gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
    return gzip_stream->parent_stream->vtbl->drain_content(gzip_stream->parent_stream, req);
}

513 514 515 516 517 518 519 520 521 522 523 524 525 526 527
static void gzip_destroy(data_stream_t *stream)
{
    gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;

    destroy_data_stream(gzip_stream->parent_stream);

    if(!gzip_stream->end_of_data)
        inflateEnd(&gzip_stream->zstream);
    heap_free(gzip_stream);
}

static const data_stream_vtbl_t gzip_stream_vtbl = {
    gzip_get_avail_data,
    gzip_end_of_data,
    gzip_read,
528
    gzip_drain_content,
529 530 531
    gzip_destroy
};

532 533
static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
{
534
    return heap_alloc(items*size);
535 536 537 538
}

static void wininet_zfree(voidpf opaque, voidpf address)
{
539
    heap_free(address);
540 541
}

542
static DWORD init_gzip_stream(http_request_t *req)
543 544
{
    gzip_stream_t *gzip_stream;
545
    int index, zres;
546

547
    gzip_stream = heap_alloc_zero(sizeof(gzip_stream_t));
548 549 550 551
    if(!gzip_stream)
        return ERROR_OUTOFMEMORY;

    gzip_stream->stream.vtbl = &gzip_stream_vtbl;
552 553 554 555 556 557
    gzip_stream->zstream.zalloc = wininet_zalloc;
    gzip_stream->zstream.zfree = wininet_zfree;

    zres = inflateInit2(&gzip_stream->zstream, 0x1f);
    if(zres != Z_OK) {
        ERR("inflateInit failed: %d\n", zres);
558
        heap_free(gzip_stream);
559
        return ERROR_OUTOFMEMORY;
560 561
    }

562 563 564
    index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
    if(index != -1)
        HTTP_DeleteCustomHeader(req, index);
565

566 567 568 569 570 571 572 573 574 575
    if(req->read_size) {
        memcpy(gzip_stream->buf, req->read_buf+req->read_pos, req->read_size);
        gzip_stream->buf_size = req->read_size;
        req->read_pos = req->read_size = 0;
    }

    req->read_gzip = TRUE;
    gzip_stream->parent_stream = req->data_stream;
    req->data_stream = &gzip_stream->stream;
    return ERROR_SUCCESS;
576 577
}

578 579
#else

580
static DWORD init_gzip_stream(http_request_t *req)
581 582
{
    ERR("gzip stream not supported, missing zlib.\n");
583
    return ERROR_SUCCESS;
584 585
}

586 587
#endif

588 589 590 591 592 593 594 595 596 597 598 599
/***********************************************************************
 *           HTTP_Tokenize (internal)
 *
 *  Tokenize a string, allocating memory for the tokens.
 */
static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
{
    LPWSTR * token_array;
    int tokens = 0;
    int i;
    LPCWSTR next_token;

600 601 602 603
    if (string)
    {
        /* empty string has no tokens */
        if (*string)
604
            tokens++;
605 606 607 608 609 610 611 612 613 614 615 616 617
        /* count tokens */
        for (i = 0; string[i]; i++)
        {
            if (!strncmpW(string+i, token_string, strlenW(token_string)))
            {
                DWORD j;
                tokens++;
                /* we want to skip over separators, but not the null terminator */
                for (j = 0; j < strlenW(token_string) - 1; j++)
                    if (!string[i+j])
                        break;
                i += j;
            }
618
        }
619
    }
620 621

    /* add 1 for terminating NULL */
622
    token_array = heap_alloc((tokens+1) * sizeof(*token_array));
623 624 625 626 627 628 629 630 631
    token_array[tokens] = NULL;
    if (!tokens)
        return token_array;
    for (i = 0; i < tokens; i++)
    {
        int len;
        next_token = strstrW(string, token_string);
        if (!next_token) next_token = string+strlenW(string);
        len = next_token - string;
632
        token_array[i] = heap_alloc((len+1)*sizeof(WCHAR));
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647
        memcpy(token_array[i], string, len*sizeof(WCHAR));
        token_array[i][len] = '\0';
        string = next_token+strlenW(token_string);
    }
    return token_array;
}

/***********************************************************************
 *           HTTP_FreeTokens (internal)
 *
 *  Frees memory returned from HTTP_Tokenize.
 */
static void HTTP_FreeTokens(LPWSTR * token_array)
{
    int i;
648 649
    for (i = 0; token_array[i]; i++) heap_free(token_array[i]);
    heap_free(token_array);
650 651
}

652
static void HTTP_FixURL(http_request_t *request)
653 654 655 656 657
{
    static const WCHAR szSlash[] = { '/',0 };
    static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };

    /* If we don't have a path we set it to root */
658 659
    if (NULL == request->path)
        request->path = heap_strdupW(szSlash);
660 661
    else /* remove \r and \n*/
    {
662 663
        int nLen = strlenW(request->path);
        while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n')))
664 665
        {
            nLen--;
666
            request->path[nLen]='\0';
667 668 669 670
        }
        /* Replace '\' with '/' */
        while (nLen>0) {
            nLen--;
671
            if (request->path[nLen] == '\\') request->path[nLen]='/';
672 673 674
        }
    }

675
    if(CSTR_EQUAL != CompareStringW( LOCALE_INVARIANT, NORM_IGNORECASE,
676 677
                       request->path, strlenW(request->path), szHttp, strlenW(szHttp) )
       && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */
678
    {
679
        WCHAR *fixurl = heap_alloc((strlenW(request->path) + 2)*sizeof(WCHAR));
680
        *fixurl = '/';
681
        strcpyW(fixurl + 1, request->path);
682
        heap_free( request->path );
683
        request->path = fixurl;
684 685 686
    }
}

687
static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *request, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
688 689 690 691
{
    LPWSTR requestString;
    DWORD len, n;
    LPCWSTR *req;
692
    UINT i;
693 694 695 696 697 698 699
    LPWSTR p;

    static const WCHAR szSpace[] = { ' ',0 };
    static const WCHAR szColon[] = { ':',' ',0 };
    static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};

    /* allocate space for an array of all the string pointers to be added */
700
    len = (request->nCustHeaders)*4 + 10;
701
    req = heap_alloc(len*sizeof(LPCWSTR));
702

703
    /* add the verb, path and HTTP version string */
704
    n = 0;
705
    req[n++] = verb;
706
    req[n++] = szSpace;
707
    req[n++] = path;
708 709
    req[n++] = szSpace;
    req[n++] = version;
710

Austin English's avatar
Austin English committed
711
    /* Append custom request headers */
712
    for (i = 0; i < request->nCustHeaders; i++)
713
    {
714
        if (request->custHeaders[i].wFlags & HDR_ISREQUEST)
715
        {
716
            req[n++] = szCrLf;
717
            req[n++] = request->custHeaders[i].lpszField;
718
            req[n++] = szColon;
719
            req[n++] = request->custHeaders[i].lpszValue;
720 721

            TRACE("Adding custom header %s (%s)\n",
722 723
                   debugstr_w(request->custHeaders[i].lpszField),
                   debugstr_w(request->custHeaders[i].lpszValue));
724 725 726 727 728 729 730 731
        }
    }

    if( n >= len )
        ERR("oops. buffer overrun\n");

    req[n] = NULL;
    requestString = HTTP_build_req( req, 4 );
732
    heap_free( req );
733 734 735 736 737 738 739 740 741 742 743 744 745

    /*
     * Set (header) termination string for request
     * Make sure there's exactly two new lines at the end of the request
     */
    p = &requestString[strlenW(requestString)-1];
    while ( (*p == '\n') || (*p == '\r') )
       p--;
    strcpyW( p+1, sztwocrlf );
    
    return requestString;
}

746
static void HTTP_ProcessCookies( http_request_t *request )
747
{
748
    int HeaderIndex;
749
    int numCookies = 0;
750 751
    LPHTTPHEADERW setCookieHeader;

752 753 754 755
    if(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES)
        return;

    while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, szSet_Cookie, numCookies++, FALSE)) != -1)
756
    {
757 758 759 760
        HTTPHEADERW *host;
        const WCHAR *data;
        WCHAR *name;

761
        setCookieHeader = &request->custHeaders[HeaderIndex];
762

763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
        if (!setCookieHeader->lpszValue)
            continue;

        host = HTTP_GetHeader(request, hostW);
        if(!host)
            continue;

        data = strchrW(setCookieHeader->lpszValue, '=');
        if(!data)
            continue;

        name = heap_strndupW(setCookieHeader->lpszValue, data-setCookieHeader->lpszValue);
        if(!name)
            continue;

        data++;
        set_cookie(host->lpszValue, request->path, name, data);
        heap_free(name);
781 782 783
    }
}

784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803
static void strip_spaces(LPWSTR start)
{
    LPWSTR str = start;
    LPWSTR end;

    while (*str == ' ' && *str != '\0')
        str++;

    if (str != start)
        memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));

    end = start + strlenW(start) - 1;
    while (end >= start && *end == ' ')
    {
        *end = '\0';
        end--;
    }
}

static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
804 805
{
    static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
806 807 808
    static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
    BOOL is_basic;
    is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
809
        ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836
    if (is_basic && pszRealm)
    {
        LPCWSTR token;
        LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
        LPCWSTR realm;
        ptr++;
        *pszRealm=NULL;
        token = strchrW(ptr,'=');
        if (!token)
            return TRUE;
        realm = ptr;
        while (*realm == ' ' && *realm != '\0')
            realm++;
        if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
            (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
        {
            token++;
            while (*token == ' ' && *token != '\0')
                token++;
            if (*token == '\0')
                return TRUE;
            *pszRealm = heap_strdupW(token);
            strip_spaces(*pszRealm);
        }
    }

    return is_basic;
837 838
}

839 840 841 842 843 844 845 846 847
static void destroy_authinfo( struct HttpAuthInfo *authinfo )
{
    if (!authinfo) return;

    if (SecIsValidHandle(&authinfo->ctx))
        DeleteSecurityContext(&authinfo->ctx);
    if (SecIsValidHandle(&authinfo->cred))
        FreeCredentialsHandle(&authinfo->cred);

848 849 850
    heap_free(authinfo->auth_data);
    heap_free(authinfo->scheme);
    heap_free(authinfo);
851 852
}

853 854
static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
{
855
    basicAuthorizationData *ad;
856 857 858 859 860
    UINT rc = 0;

    TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));

    EnterCriticalSection(&authcache_cs);
861
    LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
862
    {
863
        if (!strcmpiW(host,ad->host) && !strcmpW(realm,ad->realm))
864 865
        {
            TRACE("Authorization found in cache\n");
866
            *auth_data = heap_alloc(ad->authorizationLen);
867 868
            memcpy(*auth_data,ad->authorization,ad->authorizationLen);
            rc = ad->authorizationLen;
869 870 871 872 873 874 875 876 877 878
            break;
        }
    }
    LeaveCriticalSection(&authcache_cs);
    return rc;
}

static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
{
    struct list *cursor;
879
    basicAuthorizationData* ad = NULL;
880 881 882 883 884 885

    TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));

    EnterCriticalSection(&authcache_cs);
    LIST_FOR_EACH(cursor, &basicAuthorizationCache)
    {
886
        basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
887
        if (!strcmpiW(host,check->host) && !strcmpW(realm,check->realm))
888 889 890 891 892 893 894 895 896
        {
            ad = check;
            break;
        }
    }

    if (ad)
    {
        TRACE("Found match in cache, replacing\n");
897
        heap_free(ad->authorization);
898
        ad->authorization = heap_alloc(auth_data_len);
899 900
        memcpy(ad->authorization, auth_data, auth_data_len);
        ad->authorizationLen = auth_data_len;
901 902 903
    }
    else
    {
904
        ad = heap_alloc(sizeof(basicAuthorizationData));
905
        ad->host = heap_strdupW(host);
906
        ad->realm = heap_strdupW(realm);
907
        ad->authorization = heap_alloc(auth_data_len);
908 909
        memcpy(ad->authorization, auth_data, auth_data_len);
        ad->authorizationLen = auth_data_len;
910 911 912 913 914 915
        list_add_head(&basicAuthorizationCache,&ad->entry);
        TRACE("authorization cached\n");
    }
    LeaveCriticalSection(&authcache_cs);
}

916 917 918 919 920 921 922 923 924 925 926 927 928 929
static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
        SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
{
    authorizationData *ad;

    TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));

    EnterCriticalSection(&authcache_cs);
    LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
        if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
            TRACE("Authorization found in cache\n");

            nt_auth_identity->User = heap_strdupW(ad->user);
            nt_auth_identity->Password = heap_strdupW(ad->password);
930
            nt_auth_identity->Domain = heap_alloc(sizeof(WCHAR)*ad->domain_len);
931 932
            if(!nt_auth_identity->User || !nt_auth_identity->Password ||
                    (!nt_auth_identity->Domain && ad->domain_len)) {
933 934 935
                heap_free(nt_auth_identity->User);
                heap_free(nt_auth_identity->Password);
                heap_free(nt_auth_identity->Domain);
936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968
                break;
            }

            nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
            nt_auth_identity->UserLength = ad->user_len;
            nt_auth_identity->PasswordLength = ad->password_len;
            memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
            nt_auth_identity->DomainLength = ad->domain_len;
            LeaveCriticalSection(&authcache_cs);
            return TRUE;
        }
    }
    LeaveCriticalSection(&authcache_cs);

    return FALSE;
}

static void cache_authorization(LPWSTR host, LPWSTR scheme,
        SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
{
    authorizationData *ad;
    BOOL found = FALSE;

    TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));

    EnterCriticalSection(&authcache_cs);
    LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
        if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
            found = TRUE;
            break;
        }

    if(found) {
969 970 971
        heap_free(ad->user);
        heap_free(ad->password);
        heap_free(ad->domain);
972
    } else {
973
        ad = heap_alloc(sizeof(authorizationData));
974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992
        if(!ad) {
            LeaveCriticalSection(&authcache_cs);
            return;
        }

        ad->host = heap_strdupW(host);
        ad->scheme = heap_strdupW(scheme);
        list_add_head(&authorizationCache, &ad->entry);
    }

    ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
    ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
    ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
    ad->user_len = nt_auth_identity->UserLength;
    ad->password_len = nt_auth_identity->PasswordLength;
    ad->domain_len = nt_auth_identity->DomainLength;

    if(!ad->host || !ad->scheme || !ad->user || !ad->password
            || (nt_auth_identity->Domain && !ad->domain)) {
993 994 995 996 997
        heap_free(ad->host);
        heap_free(ad->scheme);
        heap_free(ad->user);
        heap_free(ad->password);
        heap_free(ad->domain);
998
        list_remove(&ad->entry);
999
        heap_free(ad);
1000 1001 1002 1003 1004
    }

    LeaveCriticalSection(&authcache_cs);
}

1005
static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue,
1006
                                  struct HttpAuthInfo **ppAuthInfo,
1007 1008
                                  LPWSTR domain_and_username, LPWSTR password,
                                  LPWSTR host )
1009 1010
{
    SECURITY_STATUS sec_status;
1011
    struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
1012
    BOOL first = FALSE;
1013
    LPWSTR szRealm = NULL;
1014 1015 1016 1017 1018 1019 1020

    TRACE("%s\n", debugstr_w(pszAuthValue));

    if (!pAuthInfo)
    {
        TimeStamp exp;

1021
        first = TRUE;
1022
        pAuthInfo = heap_alloc(sizeof(*pAuthInfo));
1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033
        if (!pAuthInfo)
            return FALSE;

        SecInvalidateHandle(&pAuthInfo->cred);
        SecInvalidateHandle(&pAuthInfo->ctx);
        memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
        pAuthInfo->attr = 0;
        pAuthInfo->auth_data = NULL;
        pAuthInfo->auth_data_len = 0;
        pAuthInfo->finished = FALSE;

1034
        if (is_basic_auth_value(pszAuthValue,NULL))
1035 1036
        {
            static const WCHAR szBasic[] = {'B','a','s','i','c',0};
1037
            pAuthInfo->scheme = heap_strdupW(szBasic);
1038 1039
            if (!pAuthInfo->scheme)
            {
1040
                heap_free(pAuthInfo);
1041 1042 1043 1044
                return FALSE;
            }
        }
        else
1045
        {
1046
            PVOID pAuthData;
1047 1048
            SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;

1049
            pAuthInfo->scheme = heap_strdupW(pszAuthValue);
1050 1051
            if (!pAuthInfo->scheme)
            {
1052
                heap_free(pAuthInfo);
1053 1054 1055
                return FALSE;
            }

1056
            if (domain_and_username)
1057
            {
1058 1059
                WCHAR *user = strchrW(domain_and_username, '\\');
                WCHAR *domain = domain_and_username;
1060

1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078
                /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */

                pAuthData = &nt_auth_identity;

                if (user) user++;
                else
                {
                    user = domain_and_username;
                    domain = NULL;
                }

                nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
                nt_auth_identity.User = user;
                nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
                nt_auth_identity.Domain = domain;
                nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
                nt_auth_identity.Password = password;
                nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
1079 1080

                cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
1081
            }
1082 1083
            else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
                pAuthData = &nt_auth_identity;
1084 1085 1086
            else
                /* use default credentials */
                pAuthData = NULL;
1087 1088 1089

            sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
                                                   SECPKG_CRED_OUTBOUND, NULL,
1090
                                                   pAuthData, NULL,
1091 1092
                                                   NULL, &pAuthInfo->cred,
                                                   &exp);
1093 1094

            if(pAuthData && !domain_and_username) {
1095 1096 1097
                heap_free(nt_auth_identity.User);
                heap_free(nt_auth_identity.Domain);
                heap_free(nt_auth_identity.Password);
1098 1099
            }

1100 1101 1102 1103 1104 1105 1106 1107 1108 1109
            if (sec_status == SEC_E_OK)
            {
                PSecPkgInfoW sec_pkg_info;
                sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
                if (sec_status == SEC_E_OK)
                {
                    pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
                    FreeContextBuffer(sec_pkg_info);
                }
            }
1110 1111 1112 1113
            if (sec_status != SEC_E_OK)
            {
                WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
                     debugstr_w(pAuthInfo->scheme), sec_status);
1114 1115
                heap_free(pAuthInfo->scheme);
                heap_free(pAuthInfo);
1116 1117 1118
                return FALSE;
            }
        }
1119
        *ppAuthInfo = pAuthInfo;
1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131
    }
    else if (pAuthInfo->finished)
        return FALSE;

    if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
        strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
    {
        ERR("authentication scheme changed from %s to %s\n",
            debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
        return FALSE;
    }

1132
    if (is_basic_auth_value(pszAuthValue,&szRealm))
1133
    {
1134 1135
        int userlen;
        int passlen;
1136 1137
        char *auth_data = NULL;
        UINT auth_data_len = 0;
1138

1139
        TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
1140

1141 1142 1143 1144 1145 1146
        if (!domain_and_username)
        {
            if (host && szRealm)
                auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
            if (auth_data_len == 0)
            {
1147
                heap_free(szRealm);
1148 1149 1150 1151 1152 1153 1154
                return FALSE;
            }
        }
        else
        {
            userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
            passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
1155

1156
            /* length includes a nul terminator, which will be re-used for the ':' */
1157
            auth_data = heap_alloc(userlen + 1 + passlen);
1158 1159
            if (!auth_data)
            {
1160
                heap_free(szRealm);
1161 1162
                return FALSE;
            }
1163

1164 1165 1166 1167 1168 1169 1170
            WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
            auth_data[userlen] = ':';
            WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
            auth_data_len = userlen + 1 + passlen;
            if (host && szRealm)
                cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
        }
1171 1172

        pAuthInfo->auth_data = auth_data;
1173
        pAuthInfo->auth_data_len = auth_data_len;
1174
        pAuthInfo->finished = TRUE;
1175
        heap_free(szRealm);
1176
        return TRUE;
1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199
    }
    else
    {
        LPCWSTR pszAuthData;
        SecBufferDesc out_desc, in_desc;
        SecBuffer out, in;
        unsigned char *buffer;
        ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
            ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;

        in.BufferType = SECBUFFER_TOKEN;
        in.cbBuffer = 0;
        in.pvBuffer = NULL;

        in_desc.ulVersion = 0;
        in_desc.cBuffers = 1;
        in_desc.pBuffers = &in;

        pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
        if (*pszAuthData == ' ')
        {
            pszAuthData++;
            in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
1200
            in.pvBuffer = heap_alloc(in.cbBuffer);
1201 1202 1203
            HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
        }

1204
        buffer = heap_alloc(pAuthInfo->max_token);
1205 1206

        out.BufferType = SECBUFFER_TOKEN;
1207
        out.cbBuffer = pAuthInfo->max_token;
1208 1209 1210 1211 1212 1213
        out.pvBuffer = buffer;

        out_desc.ulVersion = 0;
        out_desc.cBuffers = 1;
        out_desc.pBuffers = &out;

1214
        sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
1215
                                                first ? NULL : &pAuthInfo->ctx,
1216
                                                first ? request->server->name : NULL,
1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236
                                                context_req, 0, SECURITY_NETWORK_DREP,
                                                in.pvBuffer ? &in_desc : NULL,
                                                0, &pAuthInfo->ctx, &out_desc,
                                                &pAuthInfo->attr, &pAuthInfo->exp);
        if (sec_status == SEC_E_OK)
        {
            pAuthInfo->finished = TRUE;
            pAuthInfo->auth_data = out.pvBuffer;
            pAuthInfo->auth_data_len = out.cbBuffer;
            TRACE("sending last auth packet\n");
        }
        else if (sec_status == SEC_I_CONTINUE_NEEDED)
        {
            pAuthInfo->auth_data = out.pvBuffer;
            pAuthInfo->auth_data_len = out.cbBuffer;
            TRACE("sending next auth packet\n");
        }
        else
        {
            ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
1237
            heap_free(out.pvBuffer);
1238 1239
            destroy_authinfo(pAuthInfo);
            *ppAuthInfo = NULL;
1240 1241 1242 1243 1244 1245 1246
            return FALSE;
        }
    }

    return TRUE;
}

1247
/***********************************************************************
1248
 *           HTTP_HttpAddRequestHeadersW (internal)
1249
 */
1250
static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *request,
1251
	LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1252
{
1253 1254 1255
    LPWSTR lpszStart;
    LPWSTR lpszEnd;
    LPWSTR buffer;
1256
    DWORD len, res = ERROR_HTTP_INVALID_HEADER;
1257

1258
    TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
1259

1260
    if( dwHeaderLength == ~0U )
1261 1262 1263
        len = strlenW(lpszHeader);
    else
        len = dwHeaderLength;
1264
    buffer = heap_alloc(sizeof(WCHAR)*(len+1));
1265
    lstrcpynW( buffer, lpszHeader, len + 1);
1266

1267 1268 1269 1270
    lpszStart = buffer;

    do
    {
1271 1272
        LPWSTR * pFieldAndValue;

1273 1274 1275 1276
        lpszEnd = lpszStart;

        while (*lpszEnd != '\0')
        {
1277
            if (*lpszEnd == '\r' || *lpszEnd == '\n')
1278 1279 1280 1281
                 break;
            lpszEnd++;
        }

1282
        if (*lpszStart == '\0')
1283 1284
	    break;

1285
        if (*lpszEnd == '\r' || *lpszEnd == '\n')
1286 1287
        {
            *lpszEnd = '\0';
1288
            lpszEnd++; /* Jump over newline */
1289
        }
1290
        TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1291 1292 1293 1294
        if (*lpszStart == '\0')
        {
            /* Skip 0-length headers */
            lpszStart = lpszEnd;
1295
            res = ERROR_SUCCESS;
1296 1297
            continue;
        }
1298 1299 1300
        pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
        if (pFieldAndValue)
        {
1301
            res = HTTP_VerifyValidHeader(request, pFieldAndValue[0]);
1302
            if (res == ERROR_SUCCESS)
1303
                res = HTTP_ProcessHeader(request, pFieldAndValue[0],
1304
                    pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1305 1306
            HTTP_FreeTokens(pFieldAndValue);
        }
1307

1308
        lpszStart = lpszEnd;
1309
    } while (res == ERROR_SUCCESS);
1310

1311
    heap_free(buffer);
1312
    return res;
1313 1314 1315 1316 1317 1318 1319
}

/***********************************************************************
 *           HttpAddRequestHeadersW (WININET.@)
 *
 * Adds one or more HTTP header to the request handler
 *
1320 1321 1322
 * NOTE
 * On Windows if dwHeaderLength includes the trailing '\0', then
 * HttpAddRequestHeadersW() adds it too. However this results in an
1323
 * invalid HTTP header which is rejected by some servers so we probably
1324 1325
 * don't need to match Windows on that point.
 *
1326 1327 1328 1329 1330 1331 1332 1333
 * RETURNS
 *    TRUE  on success
 *    FALSE on failure
 *
 */
BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
	LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
{
1334
    http_request_t *request;
1335
    DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1336

1337
    TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1338 1339 1340 1341

    if (!lpszHeader) 
      return TRUE;

1342 1343 1344 1345 1346
    request = (http_request_t*) get_handle_object( hHttpRequest );
    if (request && request->hdr.htype == WH_HHTTPREQ)
        res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier );
    if( request )
        WININET_Release( &request->hdr );
1347

1348 1349 1350
    if(res != ERROR_SUCCESS)
        SetLastError(res);
    return res == ERROR_SUCCESS;
1351 1352
}

1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369
/***********************************************************************
 *           HttpAddRequestHeadersA (WININET.@)
 *
 * Adds one or more HTTP header to the request handler
 *
 * RETURNS
 *    TRUE  on success
 *    FALSE on failure
 *
 */
BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
	LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
{
    DWORD len;
    LPWSTR hdr;
    BOOL r;

1370
    TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1371 1372

    len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1373
    hdr = heap_alloc(len*sizeof(WCHAR));
1374
    MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1375
    if( dwHeaderLength != ~0U )
1376 1377 1378 1379
        dwHeaderLength = len;

    r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );

1380
    heap_free( hdr );
1381 1382 1383
    return r;
}

1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437
static void free_accept_types( WCHAR **accept_types )
{
    WCHAR *ptr, **types = accept_types;

    if (!types) return;
    while ((ptr = *types))
    {
        heap_free( ptr );
        types++;
    }
    heap_free( accept_types );
}

static WCHAR **convert_accept_types( const char **accept_types )
{
    unsigned int count;
    const char **types = accept_types;
    WCHAR **typesW;
    BOOL invalid_pointer = FALSE;

    if (!types) return NULL;
    count = 0;
    while (*types)
    {
        __TRY
        {
            /* find out how many there are */
            if (*types && **types)
            {
                TRACE("accept type: %s\n", debugstr_a(*types));
                count++;
            }
        }
        __EXCEPT_PAGE_FAULT
        {
            WARN("invalid accept type pointer\n");
            invalid_pointer = TRUE;
        }
        __ENDTRY;
        types++;
    }
    if (invalid_pointer) return NULL;
    if (!(typesW = heap_alloc( sizeof(WCHAR *) * (count + 1) ))) return NULL;
    count = 0;
    types = accept_types;
    while (*types)
    {
        if (*types && **types) typesW[count++] = heap_strdupAtoW( *types );
        types++;
    }
    typesW[count] = NULL;
    return typesW;
}

1438
/***********************************************************************
1439
 *           HttpOpenRequestA (WININET.@)
1440 1441 1442 1443 1444 1445 1446 1447
 *
 * Open a HTTP request handle
 *
 * RETURNS
 *    HINTERNET  a HTTP request handle on success
 *    NULL 	 on failure
 *
 */
1448 1449 1450
HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
	LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
	LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1451
	DWORD dwFlags, DWORD_PTR dwContext)
1452
{
1453 1454
    LPWSTR szVerb = NULL, szObjectName = NULL;
    LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1455
    HINTERNET rc = FALSE;
1456

1457
    TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1458 1459
          debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
          debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1460 1461 1462 1463
          dwFlags, dwContext);

    if (lpszVerb)
    {
1464
        szVerb = heap_strdupAtoW(lpszVerb);
1465
        if ( !szVerb )
1466 1467
            goto end;
    }
1468

1469 1470
    if (lpszObjectName)
    {
1471
        szObjectName = heap_strdupAtoW(lpszObjectName);
1472
        if ( !szObjectName )
1473 1474 1475 1476 1477
            goto end;
    }

    if (lpszVersion)
    {
1478
        szVersion = heap_strdupAtoW(lpszVersion);
1479
        if ( !szVersion )
1480 1481 1482 1483 1484
            goto end;
    }

    if (lpszReferrer)
    {
1485
        szReferrer = heap_strdupAtoW(lpszReferrer);
1486
        if ( !szReferrer )
1487 1488 1489
            goto end;
    }

1490 1491 1492
    szAcceptTypes = convert_accept_types( lpszAcceptTypes );
    rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName, szVersion, szReferrer,
                          (const WCHAR **)szAcceptTypes, dwFlags, dwContext);
1493 1494

end:
1495
    free_accept_types(szAcceptTypes);
1496 1497 1498 1499
    heap_free(szReferrer);
    heap_free(szVersion);
    heap_free(szObjectName);
    heap_free(szVerb);
1500
    return rc;
1501
}
1502

1503
/***********************************************************************
1504
 *  HTTP_EncodeBase64
1505
 */
1506
static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1507 1508
{
    UINT n = 0, x;
1509
    static const CHAR HTTP_Base64Enc[] =
1510 1511
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

1512
    while( len > 0 )
1513 1514 1515 1516 1517 1518
    {
        /* first 6 bits, all from bin[0] */
        base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
        x = (bin[0] & 3) << 4;

        /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1519
        if( len == 1 )
1520 1521 1522 1523 1524 1525 1526 1527 1528 1529
        {
            base64[n++] = HTTP_Base64Enc[x];
            base64[n++] = '=';
            base64[n++] = '=';
            break;
        }
        base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
        x = ( bin[1] & 0x0f ) << 2;

        /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1530
        if( len == 2 )
1531 1532 1533 1534 1535 1536 1537 1538 1539 1540
        {
            base64[n++] = HTTP_Base64Enc[x];
            base64[n++] = '=';
            break;
        }
        base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];

        /* last 6 bits, all from bin [2] */
        base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
        bin += 3;
1541
        len -= 3;
1542 1543 1544 1545 1546
    }
    base64[n] = 0;
    return n;
}

1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592
#define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
               ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
               ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
               ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
static const signed char HTTP_Base64Dec[256] =
{
    CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
    CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
    CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
    CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
    CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
    CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
    CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
    CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
    CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
    CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
    CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
    CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
    CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
    CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
    CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
    CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
    CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
    CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
    CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
    CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
    CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
    CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
    CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
    CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
    CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
    CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
};
#undef CH

/***********************************************************************
 *  HTTP_DecodeBase64
 */
static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
{
    unsigned int n = 0;

    while(*base64)
    {
        signed char in[4];

1593
        if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1594
            ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1595
            base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634
            ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
        {
            WARN("invalid base64: %s\n", debugstr_w(base64));
            return 0;
        }
        if (bin)
            bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
        n++;

        if ((base64[2] == '=') && (base64[3] == '='))
            break;
        if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
            ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
        {
            WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
            return 0;
        }
        if (bin)
            bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
        n++;

        if (base64[3] == '=')
            break;
        if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
            ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
        {
            WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
            return 0;
        }
        if (bin)
            bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
        n++;

        base64 += 4;
    }

    return n;
}

1635
/***********************************************************************
1636
 *  HTTP_InsertAuthorization
1637 1638 1639
 *
 *   Insert or delete the authorization field in the request header.
 */
1640
static BOOL HTTP_InsertAuthorization( http_request_t *request, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1641
{
1642
    if (pAuthInfo)
1643 1644
    {
        static const WCHAR wszSpace[] = {' ',0};
1645
        static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1646
        unsigned int len;
1647
        WCHAR *authorization = NULL;
1648

1649 1650 1651 1652
        if (pAuthInfo->auth_data_len)
        {
            /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
            len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1653
            authorization = heap_alloc((len+1)*sizeof(WCHAR));
1654 1655
            if (!authorization)
                return FALSE;
1656

1657 1658 1659 1660 1661
            strcpyW(authorization, pAuthInfo->scheme);
            strcatW(authorization, wszSpace);
            HTTP_EncodeBase64(pAuthInfo->auth_data,
                              pAuthInfo->auth_data_len,
                              authorization+strlenW(authorization));
1662

1663 1664 1665 1666 1667
            /* clear the data as it isn't valid now that it has been sent to the
             * server, unless it's Basic authentication which doesn't do
             * connection tracking */
            if (strcmpiW(pAuthInfo->scheme, wszBasic))
            {
1668
                heap_free(pAuthInfo->auth_data);
1669 1670 1671
                pAuthInfo->auth_data = NULL;
                pAuthInfo->auth_data_len = 0;
            }
1672
        }
1673

1674
        TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1675

1676
        HTTP_ProcessHeader(request, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1677
        heap_free(authorization);
1678
    }
1679 1680 1681
    return TRUE;
}

1682
static WCHAR *build_proxy_path_url(http_request_t *req)
1683
{
1684 1685 1686 1687 1688 1689 1690 1691 1692 1693
    DWORD size, len;
    WCHAR *url;

    len = strlenW(req->server->scheme_host_port);
    size = len + strlenW(req->path) + 1;
    if(*req->path != '/')
        size++;
    url = heap_alloc(size * sizeof(WCHAR));
    if(!url)
        return NULL;
1694

1695 1696 1697
    memcpy(url, req->server->scheme_host_port, len*sizeof(WCHAR));
    if(*req->path != '/')
        url[len++] = '/';
1698

1699
    strcpyW(url+len, req->path);
1700

1701 1702 1703 1704
    TRACE("url=%s\n", debugstr_w(url));
    return url;
}

1705 1706 1707
/***********************************************************************
 *           HTTP_DealWithProxy
 */
1708
static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_request_t *request)
1709
{
1710 1711 1712 1713
    WCHAR buf[INTERNET_MAX_HOST_NAME_LENGTH];
    WCHAR protoProxy[INTERNET_MAX_URL_LENGTH];
    DWORD protoProxyLen = INTERNET_MAX_URL_LENGTH;
    WCHAR proxy[INTERNET_MAX_URL_LENGTH];
1714
    static WCHAR szNul[] = { 0 };
1715
    URL_COMPONENTSW UrlComponents;
1716
    server_t *new_server;
1717
    static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1718 1719
    static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
    static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1720 1721 1722 1723

    memset( &UrlComponents, 0, sizeof UrlComponents );
    UrlComponents.dwStructSize = sizeof UrlComponents;
    UrlComponents.lpszHostName = buf;
1724
    UrlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
1725

1726
    if (!INTERNET_FindProxyForProtocol(hIC->proxy, protoHttp, protoProxy, &protoProxyLen))
1727
        return FALSE;
1728
    if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1729 1730
                                 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
        sprintfW(proxy, szFormat, protoProxy);
1731
    else
1732
	strcpyW(proxy, protoProxy);
1733
    if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1734 1735 1736 1737
        return FALSE;
    if( UrlComponents.dwHostNameLength == 0 )
        return FALSE;

1738 1739
    if( !request->path )
        request->path = szNul;
1740

1741
    new_server = get_server(UrlComponents.lpszHostName, UrlComponents.nPort, UrlComponents.nScheme == INTERNET_SCHEME_HTTPS, TRUE);
1742 1743 1744
    if(!new_server)
        return FALSE;

1745
    request->proxy = new_server;
1746

1747
    TRACE("proxy server=%s port=%d\n", debugstr_w(new_server->name), new_server->port);
1748 1749 1750
    return TRUE;
}

1751
static DWORD HTTP_ResolveName(http_request_t *request)
1752
{
1753
    server_t *server = request->proxy ? request->proxy : request->server;
1754
    socklen_t addr_len;
1755
    void *addr;
1756

1757 1758 1759
    if(server->addr_len)
        return ERROR_SUCCESS;

1760
    INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1761
                          INTERNET_STATUS_RESOLVING_NAME,
1762 1763
                          server->name,
                          (strlenW(server->name)+1) * sizeof(WCHAR));
1764

1765 1766
    addr_len = sizeof(server->addr);
    if (!GetAddress(server->name, server->port, (struct sockaddr *)&server->addr, &addr_len))
1767
        return ERROR_INTERNET_NAME_NOT_RESOLVED;
1768

1769
    switch(server->addr.ss_family) {
1770
    case AF_INET:
1771
        addr = &((struct sockaddr_in *)&server->addr)->sin_addr;
1772
        break;
1773
    case AF_INET6:
1774
        addr = &((struct sockaddr_in6 *)&server->addr)->sin6_addr;
1775
        break;
1776
    default:
1777
        WARN("unsupported family %d\n", server->addr.ss_family);
1778
        return ERROR_INTERNET_NAME_NOT_RESOLVED;
1779
    }
1780 1781 1782

    server->addr_len = addr_len;
    inet_ntop(server->addr.ss_family, addr, server->addr_str, sizeof(server->addr_str));
1783
    INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1784
                          INTERNET_STATUS_NAME_RESOLVED,
1785
                          server->addr_str, strlen(server->addr_str)+1);
1786

1787
    TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str);
1788
    return ERROR_SUCCESS;
1789 1790
}

1791 1792
static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
{
1793 1794 1795
    static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
    static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
    static const WCHAR slash[] = { '/',0 };
1796
    LPHTTPHEADERW host_header;
1797
    LPCWSTR scheme;
1798 1799 1800 1801 1802

    host_header = HTTP_GetHeader(req, hostW);
    if(!host_header)
        return FALSE;

1803 1804 1805 1806 1807 1808 1809 1810 1811
    if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
        scheme = https;
    else
        scheme = http;
    strcpyW(buf, scheme);
    strcatW(buf, host_header->lpszValue);
    if (req->path[0] != '/')
        strcatW(buf, slash);
    strcatW(buf, req->path);
1812 1813 1814
    return TRUE;
}

1815 1816 1817 1818 1819 1820 1821

/***********************************************************************
 *           HTTPREQ_Destroy (internal)
 *
 * Deallocate request handle
 *
 */
1822
static void HTTPREQ_Destroy(object_header_t *hdr)
1823
{
1824
    http_request_t *request = (http_request_t*) hdr;
1825 1826 1827 1828
    DWORD i;

    TRACE("\n");

1829 1830
    if(request->hCacheFile) {
        CloseHandle(request->hCacheFile);
1831
        DeleteFileW(request->cacheFile);
1832
    }
1833
    heap_free(request->cacheFile);
1834

1835
    request->read_section.DebugInfo->Spare[0] = 0;
1836
    DeleteCriticalSection( &request->read_section );
1837
    WININET_Release(&request->session->hdr);
1838

1839 1840
    destroy_authinfo(request->authInfo);
    destroy_authinfo(request->proxyAuthInfo);
1841

1842 1843
    if(request->server)
        server_release(request->server);
1844 1845
    if(request->proxy)
        server_release(request->proxy);
1846

1847 1848 1849 1850 1851
    heap_free(request->path);
    heap_free(request->verb);
    heap_free(request->rawHeaders);
    heap_free(request->version);
    heap_free(request->statusText);
1852

1853
    for (i = 0; i < request->nCustHeaders; i++)
1854
    {
1855 1856
        heap_free(request->custHeaders[i].lpszField);
        heap_free(request->custHeaders[i].lpszValue);
1857
    }
1858
    destroy_data_stream(request->data_stream);
1859
    heap_free(request->custHeaders);
1860 1861
}

1862
static void http_release_netconn(http_request_t *req, BOOL reuse)
1863
{
1864 1865 1866 1867 1868 1869 1870 1871 1872
    TRACE("%p %p\n",req, req->netconn);

    if(!req->netconn)
        return;

    if(reuse && req->netconn->keep_alive) {
        BOOL run_collector;

        EnterCriticalSection(&connection_pool_cs);
1873

1874 1875 1876
        list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry);
        req->netconn->keep_until = GetTickCount64() + COLLECT_TIME;
        req->netconn = NULL;
1877

1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897
        run_collector = !collector_running;
        collector_running = TRUE;

        LeaveCriticalSection(&connection_pool_cs);

        if(run_collector) {
            HANDLE thread = NULL;
            HMODULE module;

            GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
            if(module)
                thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL);
            if(!thread) {
                EnterCriticalSection(&connection_pool_cs);
                collector_running = FALSE;
                LeaveCriticalSection(&connection_pool_cs);

                if(module)
                    FreeLibrary(module);
            }
1898 1899
            else
                CloseHandle(thread);
1900
        }
1901
        return;
1902
    }
1903

1904
    INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1905 1906
                          INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);

1907 1908
    free_netconn(req->netconn);
    req->netconn = NULL;
1909

1910
    INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
1911 1912 1913
                          INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
}

1914
static BOOL HTTP_KeepAlive(http_request_t *request)
1915 1916 1917 1918 1919 1920 1921 1922
{
    WCHAR szVersion[10];
    WCHAR szConnectionResponse[20];
    DWORD dwBufferSize = sizeof(szVersion);
    BOOL keepalive = FALSE;

    /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
     * the connection is keep-alive by default */
1923
    if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1924
        && !strcmpiW(szVersion, g_szHttp1_1))
1925 1926 1927 1928 1929
    {
        keepalive = TRUE;
    }

    dwBufferSize = sizeof(szConnectionResponse);
1930 1931
    if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
        || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1932 1933 1934 1935 1936 1937 1938
    {
        keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
    }

    return keepalive;
}

1939 1940 1941 1942
static void HTTPREQ_CloseConnection(object_header_t *hdr)
{
    http_request_t *req = (http_request_t*)hdr;

1943
    http_release_netconn(req, drain_content(req, FALSE));
1944 1945
}

1946
static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1947
{
1948
    http_request_t *req = (http_request_t*)hdr;
1949

1950
    switch(option) {
1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965
    case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
    {
        INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;

        FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");

        if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
            return ERROR_INSUFFICIENT_BUFFER;
        *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
        /* FIXME: can't get a SOCKET from our connection since we don't use
         * winsock
         */
        info->Socket = 0;
        /* FIXME: get source port from req->netConnection */
        info->SourcePort = 0;
1966
        info->DestPort = req->server->port;
1967 1968 1969
        info->Flags = 0;
        if (HTTP_KeepAlive(req))
            info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1970
        if (req->proxy)
1971
            info->Flags |= IDSI_FLAG_PROXY;
1972
        if (req->netconn->useSSL)
1973 1974 1975 1976 1977
            info->Flags |= IDSI_FLAG_SECURE;

        return ERROR_SUCCESS;
    }

1978 1979 1980
    case 98:
        TRACE("Queried undocumented option 98, forwarding to INTERNET_OPTION_SECURITY_FLAGS\n");
        /* fall through */
1981 1982
    case INTERNET_OPTION_SECURITY_FLAGS:
    {
1983
        DWORD flags;
1984 1985 1986 1987 1988

        if (*size < sizeof(ULONG))
            return ERROR_INSUFFICIENT_BUFFER;

        *size = sizeof(DWORD);
1989
        flags = req->netconn ? req->netconn->security_flags : req->security_flags | req->server->security_flags;
1990
        *(DWORD *)buffer = flags;
1991 1992

        TRACE("INTERNET_OPTION_SECURITY_FLAGS %x\n", flags);
1993 1994 1995
        return ERROR_SUCCESS;
    }

1996 1997 1998 1999 2000 2001 2002 2003 2004
    case INTERNET_OPTION_HANDLE_TYPE:
        TRACE("INTERNET_OPTION_HANDLE_TYPE\n");

        if (*size < sizeof(ULONG))
            return ERROR_INSUFFICIENT_BUFFER;

        *size = sizeof(DWORD);
        *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
        return ERROR_SUCCESS;
2005 2006 2007 2008 2009

    case INTERNET_OPTION_URL: {
        WCHAR url[INTERNET_MAX_URL_LENGTH];
        HTTPHEADERW *host;
        DWORD len;
2010
        WCHAR *pch;
2011

2012
        static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
2013 2014 2015 2016

        TRACE("INTERNET_OPTION_URL\n");

        host = HTTP_GetHeader(req, hostW);
2017 2018 2019 2020
        strcpyW(url, httpW);
        strcatW(url, host->lpszValue);
        if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
            *pch = 0;
2021
        strcatW(url, req->path);
2022

2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041
        TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));

        if(unicode) {
            len = (strlenW(url)+1) * sizeof(WCHAR);
            if(*size < len)
                return ERROR_INSUFFICIENT_BUFFER;

            *size = len;
            strcpyW(buffer, url);
            return ERROR_SUCCESS;
        }else {
            len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
            if(len > *size)
                return ERROR_INSUFFICIENT_BUFFER;

            *size = len;
            return ERROR_SUCCESS;
        }
    }
2042

2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062
    case INTERNET_OPTION_CACHE_TIMESTAMPS: {
        INTERNET_CACHE_ENTRY_INFOW *info;
        INTERNET_CACHE_TIMESTAMPS *ts = buffer;
        WCHAR url[INTERNET_MAX_URL_LENGTH];
        DWORD nbytes, error;
        BOOL ret;

        TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");

        if (*size < sizeof(*ts))
        {
            *size = sizeof(*ts);
            return ERROR_INSUFFICIENT_BUFFER;
        }
        nbytes = 0;
        HTTP_GetRequestURL(req, url);
        ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
        error = GetLastError();
        if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
        {
2063
            if (!(info = heap_alloc(nbytes)))
2064 2065 2066 2067 2068 2069 2070
                return ERROR_OUTOFMEMORY;

            GetUrlCacheEntryInfoW(url, info, &nbytes);

            ts->ftExpires = info->ExpireTime;
            ts->ftLastModified = info->LastModifiedTime;

2071
            heap_free(info);
2072 2073 2074 2075 2076 2077
            *size = sizeof(*ts);
            return ERROR_SUCCESS;
        }
        return error;
    }

2078 2079 2080 2081 2082
    case INTERNET_OPTION_DATAFILE_NAME: {
        DWORD req_size;

        TRACE("INTERNET_OPTION_DATAFILE_NAME\n");

2083
        if(!req->cacheFile) {
2084 2085 2086 2087 2088
            *size = 0;
            return ERROR_INTERNET_ITEM_NOT_FOUND;
        }

        if(unicode) {
2089
            req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
2090 2091 2092 2093
            if(*size < req_size)
                return ERROR_INSUFFICIENT_BUFFER;

            *size = req_size;
2094
            memcpy(buffer, req->cacheFile, *size);
2095 2096
            return ERROR_SUCCESS;
        }else {
2097
            req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
2098 2099 2100
            if (req_size > *size)
                return ERROR_INSUFFICIENT_BUFFER;

2101
            *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
2102 2103 2104 2105
                    -1, buffer, *size, NULL, NULL);
            return ERROR_SUCCESS;
        }
    }
2106 2107 2108 2109

    case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
        PCCERT_CONTEXT context;

2110 2111
        if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
            *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2112 2113 2114
            return ERROR_INSUFFICIENT_BUFFER;
        }

2115
        context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
2116
        if(context) {
2117
            INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
2118 2119
            DWORD len;

2120
            memset(info, 0, sizeof(*info));
2121 2122
            info->ftExpiry = context->pCertInfo->NotAfter;
            info->ftStart = context->pCertInfo->NotBefore;
2123
            len = CertNameToStrA(context->dwCertEncodingType,
2124
                     &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2125 2126 2127
            info->lpszSubjectInfo = LocalAlloc(0, len);
            if(info->lpszSubjectInfo)
                CertNameToStrA(context->dwCertEncodingType,
2128
                         &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2129 2130
                         info->lpszSubjectInfo, len);
            len = CertNameToStrA(context->dwCertEncodingType,
2131
                     &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2132 2133 2134
            info->lpszIssuerInfo = LocalAlloc(0, len);
            if(info->lpszIssuerInfo)
                CertNameToStrA(context->dwCertEncodingType,
2135
                         &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG,
2136
                         info->lpszIssuerInfo, len);
2137
            info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2138 2139 2140
            CertFreeCertificateContext(context);
            return ERROR_SUCCESS;
        }
2141
        return ERROR_NOT_SUPPORTED;
2142
    }
2143 2144 2145 2146 2147 2148
    case INTERNET_OPTION_CONNECT_TIMEOUT:
        if (*size < sizeof(DWORD))
            return ERROR_INSUFFICIENT_BUFFER;

        *size = sizeof(DWORD);
        *(DWORD *)buffer = req->connect_timeout;
2149
        return ERROR_SUCCESS;
2150 2151
    case INTERNET_OPTION_REQUEST_FLAGS: {
        DWORD flags = 0;
2152 2153 2154 2155

        if (*size < sizeof(DWORD))
            return ERROR_INSUFFICIENT_BUFFER;

2156 2157 2158 2159 2160
        /* FIXME: Add support for:
         * INTERNET_REQFLAG_FROM_CACHE
         * INTERNET_REQFLAG_CACHE_WRITE_DISABLED
         */

2161
        if(req->proxy)
2162 2163 2164
            flags |= INTERNET_REQFLAG_VIA_PROXY;
        if(!req->rawHeaders)
            flags |= INTERNET_REQFLAG_NO_HEADERS;
2165

2166 2167 2168 2169
        TRACE("INTERNET_OPTION_REQUEST_FLAGS returning %x\n", flags);

        *size = sizeof(DWORD);
        *(DWORD*)buffer = flags;
2170
        return ERROR_SUCCESS;
2171
    }
2172
    }
2173

2174
    return INET_QueryOption(hdr, option, buffer, size, unicode);
2175 2176
}

2177
static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
2178
{
2179
    http_request_t *req = (http_request_t*)hdr;
2180 2181

    switch(option) {
2182 2183 2184 2185 2186 2187 2188 2189 2190
    case 99: /* Undocumented, seems to be INTERNET_OPTION_SECURITY_FLAGS with argument validation */
        TRACE("Undocumented option 99\n");

        if (!buffer || size != sizeof(DWORD))
            return ERROR_INVALID_PARAMETER;
        if(*(DWORD*)buffer & ~SECURITY_SET_MASK)
            return ERROR_INTERNET_OPTION_NOT_SETTABLE;

        /* fall through */
2191 2192 2193 2194 2195 2196 2197
    case INTERNET_OPTION_SECURITY_FLAGS:
    {
        DWORD flags;

        if (!buffer || size != sizeof(DWORD))
            return ERROR_INVALID_PARAMETER;
        flags = *(DWORD *)buffer;
2198 2199 2200
        TRACE("INTERNET_OPTION_SECURITY_FLAGS %08x\n", flags);
        flags &= SECURITY_SET_MASK;
        req->security_flags |= flags;
2201
        if(req->netconn)
2202
            req->netconn->security_flags |= flags;
2203 2204
        return ERROR_SUCCESS;
    }
2205
    case INTERNET_OPTION_CONNECT_TIMEOUT:
2206
        if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2207 2208 2209
        req->connect_timeout = *(DWORD *)buffer;
        return ERROR_SUCCESS;

2210
    case INTERNET_OPTION_SEND_TIMEOUT:
2211 2212 2213
        if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
        req->send_timeout = *(DWORD *)buffer;
        return ERROR_SUCCESS;
2214

2215 2216 2217 2218
    case INTERNET_OPTION_RECEIVE_TIMEOUT:
        if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
        req->receive_timeout = *(DWORD *)buffer;
        return ERROR_SUCCESS;
2219 2220

    case INTERNET_OPTION_USERNAME:
2221
        heap_free(req->session->userName);
2222
        if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2223 2224 2225
        return ERROR_SUCCESS;

    case INTERNET_OPTION_PASSWORD:
2226
        heap_free(req->session->password);
2227
        if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2228
        return ERROR_SUCCESS;
2229 2230 2231 2232 2233
    case INTERNET_OPTION_HTTP_DECODING:
        if(size != sizeof(BOOL))
            return ERROR_INVALID_PARAMETER;
        req->decoding = *(BOOL*)buffer;
        return ERROR_SUCCESS;
2234 2235
    }

2236
    return INET_SetOption(hdr, option, buffer, size);
2237 2238
}

2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257
static void commit_cache_entry(http_request_t *req)
{
    WCHAR url[INTERNET_MAX_URL_LENGTH];

    TRACE("%p\n", req);

    CloseHandle(req->hCacheFile);
    req->hCacheFile = NULL;

    if(HTTP_GetRequestURL(req, url)) {
        DWORD headersLen;

        headersLen = req->rawHeaders ? strlenW(req->rawHeaders) : 0;
        CommitUrlCacheEntryW(url, req->cacheFile, req->expires,
                req->last_modified, NORMAL_CACHE_ENTRY,
                req->rawHeaders, headersLen, NULL, 0);
    }
}

2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295
static void create_cache_entry(http_request_t *req)
{
    WCHAR url[INTERNET_MAX_URL_LENGTH];
    WCHAR file_name[MAX_PATH+1];
    BOOL b;

    /* FIXME: We should free previous cache file earlier */
    heap_free(req->cacheFile);
    CloseHandle(req->hCacheFile);
    req->hCacheFile = NULL;

    b = HTTP_GetRequestURL(req, url);
    if(!b) {
        WARN("Could not get URL\n");
        return;
    }

    b = CreateUrlCacheEntryW(url, req->contentLength, NULL, file_name, 0);
    if(!b) {
        WARN("Could not create cache entry: %08x\n", GetLastError());
        return;
    }

    req->cacheFile = heap_strdupW(file_name);
    req->hCacheFile = CreateFileW(req->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
              NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if(req->hCacheFile == INVALID_HANDLE_VALUE) {
        WARN("Could not create file: %u\n", GetLastError());
        req->hCacheFile = NULL;
        return;
    }

    if(req->read_size) {
        DWORD written;

        b = WriteFile(req->hCacheFile, req->read_buf+req->read_pos, req->read_size, &written, NULL);
        if(!b)
            FIXME("WriteFile failed: %u\n", GetLastError());
2296 2297 2298

        if(req->data_stream->vtbl->end_of_data(req->data_stream, req))
            commit_cache_entry(req);
2299 2300 2301
    }
}

2302
/* read some more data into the read buffer (the read section must be held) */
2303
static DWORD read_more_data( http_request_t *req, int maxlen )
2304
{
2305
    DWORD res;
2306
    int len;
2307

2308
    if (req->read_pos)
2309 2310
    {
        /* move existing data to the start of the buffer */
2311 2312
        if(req->read_size)
            memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2313
        req->read_pos = 0;
2314 2315
    }

2316
    if (maxlen == -1) maxlen = sizeof(req->read_buf);
2317

2318
    res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2319
                       maxlen - req->read_size, 0, &len );
2320 2321
    if(res == ERROR_SUCCESS)
        req->read_size += len;
2322

2323
    return res;
2324 2325
}

2326
/* remove some amount of data from the read buffer (the read section must be held) */
2327
static void remove_data( http_request_t *req, int count )
2328
{
2329 2330 2331
    if (!(req->read_size -= count)) req->read_pos = 0;
    else req->read_pos += count;
}
2332

2333
static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2334 2335
{
    int count, bytes_read, pos = 0;
2336
    DWORD res;
2337

2338
    EnterCriticalSection( &req->read_section );
2339 2340
    for (;;)
    {
2341
        BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2342

2343 2344 2345 2346 2347 2348
        if (eol)
        {
            count = eol - (req->read_buf + req->read_pos);
            bytes_read = count + 1;
        }
        else count = bytes_read = req->read_size;
2349

2350 2351 2352 2353 2354
        count = min( count, *len - pos );
        memcpy( buffer + pos, req->read_buf + req->read_pos, count );
        pos += count;
        remove_data( req, bytes_read );
        if (eol) break;
2355

2356
        if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2357 2358
        {
            *len = 0;
2359
            TRACE( "returning empty string %u\n", res);
2360
            LeaveCriticalSection( &req->read_section );
2361
            INTERNET_SetLastError(res);
2362 2363
            return FALSE;
        }
2364
    }
2365
    LeaveCriticalSection( &req->read_section );
2366

2367 2368 2369 2370 2371 2372 2373 2374
    if (pos < *len)
    {
        if (pos && buffer[pos - 1] == '\r') pos--;
        *len = pos + 1;
    }
    buffer[*len - 1] = 0;
    TRACE( "returning %s\n", debugstr_a(buffer));
    return TRUE;
2375 2376
}

2377 2378 2379 2380 2381 2382
/* check if we have reached the end of the data to read (the read section must be held) */
static BOOL end_of_read_data( http_request_t *req )
{
    return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
}

2383 2384 2385 2386 2387 2388 2389
static DWORD read_http_stream(http_request_t *req, BYTE *buf, DWORD size, DWORD *read, read_mode_t read_mode)
{
    DWORD res;

    res = req->data_stream->vtbl->read(req->data_stream, req, buf, size, read, read_mode);
    assert(*read <= size);

2390 2391 2392 2393
    if(req->hCacheFile) {
        if(*read) {
            BOOL bres;
            DWORD written;
2394

2395 2396 2397 2398 2399 2400 2401
            bres = WriteFile(req->hCacheFile, buf, *read, &written, NULL);
            if(!bres)
                FIXME("WriteFile failed: %u\n", GetLastError());
        }

        if(req->data_stream->vtbl->end_of_data(req->data_stream, req))
            commit_cache_entry(req);
2402 2403 2404 2405 2406
    }

    return res;
}

2407
/* fetch some more data into the read buffer (the read section must be held) */
2408
static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
2409
{
2410
    DWORD res, read=0;
2411 2412 2413 2414 2415 2416 2417 2418 2419 2420

    if(req->read_size == sizeof(req->read_buf))
        return ERROR_SUCCESS;

    if(req->read_pos) {
        if(req->read_size)
            memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
        req->read_pos = 0;
    }

2421 2422
    res = read_http_stream(req, req->read_buf+req->read_size, sizeof(req->read_buf) - req->read_size,
            &read, read_mode);
2423 2424 2425
    req->read_size += read;

    TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2426 2427
    if(read_bytes)
        *read_bytes = read;
2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438
    return res;
}

/* return the size of data available to be read immediately (the read section must be held) */
static DWORD get_avail_data( http_request_t *req )
{
    return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
}

static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
{
2439
    netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2440 2441
    DWORD avail = 0;

2442 2443 2444 2445 2446
    if(req->netconn)
        NETCON_query_data_available(req->netconn, &avail);
    return netconn_stream->content_length == ~0u
        ? avail
        : min(avail, netconn_stream->content_length-netconn_stream->content_read);
2447 2448 2449 2450 2451
}

static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
{
    netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2452
    return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
2453 2454 2455 2456 2457 2458
}

static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
        DWORD *read, read_mode_t read_mode)
{
    netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2459
    int len = 0;
2460 2461 2462

    size = min(size, netconn_stream->content_length-netconn_stream->content_read);

2463 2464 2465 2466 2467
    if(read_mode == READMODE_NOBLOCK) {
        DWORD avail = netconn_get_avail_data(stream, req);
        if (size > avail)
            size = avail;
    }
2468

2469 2470
    if(size && req->netconn) {
        if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
2471
            len = 0;
2472 2473
        if(!len)
            netconn_stream->content_length = netconn_stream->content_read;
2474 2475
    }

2476 2477
    netconn_stream->content_read += *read = len;
    TRACE("read %u bytes\n", len);
2478 2479 2480
    return ERROR_SUCCESS;
}

2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504
static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
{
    netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
    BYTE buf[1024];
    DWORD avail;
    int len;

    if(netconn_end_of_data(stream, req))
        return TRUE;

    do {
        avail = netconn_get_avail_data(stream, req);
        if(!avail)
            return FALSE;

        if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
            return FALSE;

        netconn_stream->content_read += len;
    }while(netconn_stream->content_read < netconn_stream->content_length);

    return TRUE;
}

2505 2506 2507 2508 2509 2510 2511 2512
static void netconn_destroy(data_stream_t *stream)
{
}

static const data_stream_vtbl_t netconn_stream_vtbl = {
    netconn_get_avail_data,
    netconn_end_of_data,
    netconn_read,
2513
    netconn_drain_content,
2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532
    netconn_destroy
};

/* read some more data into the read buffer (the read section must be held) */
static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
{
    DWORD res;
    int len;

    if (stream->buf_pos)
    {
        /* move existing data to the start of the buffer */
        if(stream->buf_size)
            memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
        stream->buf_pos = 0;
    }

    if (maxlen == -1) maxlen = sizeof(stream->buf);

2533
    res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547
                       maxlen - stream->buf_size, 0, &len );
    if(res == ERROR_SUCCESS)
        stream->buf_size += len;

    return res;
}

/* remove some amount of data from the read buffer (the read section must be held) */
static void remove_chunked_data(chunked_stream_t *stream, int count)
{
    if (!(stream->buf_size -= count)) stream->buf_pos = 0;
    else stream->buf_pos += count;
}

2548
/* discard data contents until we reach end of line (the read section must be held) */
2549
static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2550
{
2551 2552
    DWORD res;

2553
    do
2554
    {
2555
        BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2556 2557
        if (eol)
        {
2558
            remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2559 2560
            break;
        }
2561 2562 2563
        stream->buf_pos = stream->buf_size = 0;  /* discard everything */
        if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
    } while (stream->buf_size);
2564
    return ERROR_SUCCESS;
2565 2566
}

2567
/* read the size of the next chunk (the read section must be held) */
2568
static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2569
{
2570
    /* TODOO */
2571
    DWORD chunk_size = 0, res;
2572

2573 2574 2575
    if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
        return res;

2576 2577
    for (;;)
    {
2578
        while (stream->buf_size)
2579
        {
2580
            char ch = stream->buf[stream->buf_pos];
2581 2582 2583 2584
            if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
            else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
            else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
            else if (ch == ';' || ch == '\r' || ch == '\n')
2585
            {
2586
                TRACE( "reading %u byte chunk\n", chunk_size );
2587 2588 2589
                stream->chunk_size = chunk_size;
                req->contentLength += chunk_size;
                return discard_chunked_eol(stream, req);
2590
            }
2591
            remove_chunked_data(stream, 1);
2592
        }
2593 2594
        if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
        if (!stream->buf_size)
2595
        {
2596
            stream->chunk_size = 0;
2597
            return ERROR_SUCCESS;
2598
        }
2599 2600
    }
}
2601

2602
static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2603
{
2604 2605
    /* Allow reading only from read buffer */
    return 0;
2606
}
2607

2608
static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2609
{
2610 2611 2612
    chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
    return !chunked_stream->chunk_size;
}
2613

2614 2615 2616 2617 2618 2619 2620 2621 2622 2623
static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
        DWORD *read, read_mode_t read_mode)
{
    chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
    DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;

    if(chunked_stream->chunk_size == ~0u) {
        res = start_next_chunk(chunked_stream, req);
        if(res != ERROR_SUCCESS)
            return res;
2624
    }
2625

2626 2627 2628
    while(size && chunked_stream->chunk_size) {
        if(chunked_stream->buf_size) {
            read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2629

2630 2631 2632
            /* this could block */
            if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
                break;
2633

2634 2635 2636 2637
            memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
            remove_chunked_data(chunked_stream, read_bytes);
        }else {
            read_bytes = min(size, chunked_stream->chunk_size);
2638

2639 2640
            if(read_mode == READMODE_NOBLOCK) {
                DWORD avail;
2641

2642
                if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
2643 2644 2645
                    break;
                if(read_bytes > avail)
                    read_bytes = avail;
2646

2647 2648 2649 2650
                /* this could block */
                if(read_bytes == chunked_stream->chunk_size)
                    break;
            }
2651

2652
            res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2653 2654 2655
            if(res != ERROR_SUCCESS)
                break;
        }
2656

2657 2658 2659 2660 2661 2662 2663 2664
        chunked_stream->chunk_size -= read_bytes;
        size -= read_bytes;
        ret_read += read_bytes;
        if(!chunked_stream->chunk_size) {
            assert(read_mode != READMODE_NOBLOCK);
            res = start_next_chunk(chunked_stream, req);
            if(res != ERROR_SUCCESS)
                break;
2665
        }
2666 2667 2668

        if(read_mode == READMODE_ASYNC)
            read_mode = READMODE_NOBLOCK;
2669 2670
    }

2671 2672 2673
    TRACE("read %u bytes\n", ret_read);
    *read = ret_read;
    return res;
2674 2675
}

2676 2677 2678 2679 2680 2681 2682 2683
static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
{
    chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;

    /* FIXME: we can do better */
    return !chunked_stream->chunk_size;
}

2684
static void chunked_destroy(data_stream_t *stream)
2685
{
2686 2687 2688
    chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
    heap_free(chunked_stream);
}
2689

2690 2691 2692 2693
static const data_stream_vtbl_t chunked_stream_vtbl = {
    chunked_get_avail_data,
    chunked_end_of_data,
    chunked_read,
2694
    chunked_drain_content,
2695 2696 2697 2698
    chunked_destroy
};

/* set the request content length based on the headers */
2699
static DWORD set_content_length(http_request_t *request)
2700 2701 2702 2703
{
    static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
    WCHAR encoding[20];
    DWORD size;
2704

2705
    if(request->status_code == HTTP_STATUS_NO_CONTENT) {
2706 2707 2708 2709
        request->contentLength = request->netconn_stream.content_length = 0;
        return ERROR_SUCCESS;
    }

2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739
    size = sizeof(request->contentLength);
    if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
                            &request->contentLength, &size, NULL) != ERROR_SUCCESS)
        request->contentLength = ~0u;
    request->netconn_stream.content_length = request->contentLength;
    request->netconn_stream.content_read = request->read_size;

    size = sizeof(encoding);
    if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
        !strcmpiW(encoding, szChunked))
    {
        chunked_stream_t *chunked_stream;

        chunked_stream = heap_alloc(sizeof(*chunked_stream));
        if(!chunked_stream)
            return ERROR_OUTOFMEMORY;

        chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
        chunked_stream->buf_size = chunked_stream->buf_pos = 0;
        chunked_stream->chunk_size = ~0u;

        if(request->read_size) {
            memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
            chunked_stream->buf_size = request->read_size;
            request->read_size = request->read_pos = 0;
        }

        request->data_stream = &chunked_stream->data_stream;
        request->contentLength = ~0u;
        request->read_chunked = TRUE;
2740 2741
    }

2742 2743
    if(request->decoding) {
        int encoding_idx;
2744

2745
        static const WCHAR gzipW[] = {'g','z','i','p',0};
2746

2747 2748 2749 2750
        encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
        if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
            return init_gzip_stream(request);
    }
2751

2752
    return ERROR_SUCCESS;
2753 2754
}

2755
static void send_request_complete(http_request_t *req, DWORD_PTR result, DWORD error)
2756 2757
{
    INTERNET_ASYNC_RESULT iar;
2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768

    iar.dwResult = result;
    iar.dwError = error;

    INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
            sizeof(INTERNET_ASYNC_RESULT));
}

static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
{
    DWORD res, read = 0, avail = 0;
2769
    read_mode_t mode;
2770 2771 2772

    TRACE("%p\n", req);

2773
    EnterCriticalSection( &req->read_section );
2774 2775 2776

    mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
    res = refill_read_buffer(req, mode, &read);
2777 2778
    if(res == ERROR_SUCCESS && !first_notif)
        avail = get_avail_data(req);
2779

2780
    LeaveCriticalSection( &req->read_section );
2781

2782 2783 2784 2785 2786
    if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
        WARN("res %u read %u, closing connection\n", res, read);
        http_release_netconn(req, FALSE);
    }

2787
    if(res == ERROR_SUCCESS)
2788
        send_request_complete(req, req->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)req->hdr.hInternet : 1, avail);
2789 2790
    else
        send_request_complete(req, 0, res);
2791 2792
}

2793
/* read data from the http connection (the read section must be held) */
2794
static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2795
{
2796 2797 2798 2799 2800
    DWORD current_read = 0, ret_read = 0;
    read_mode_t read_mode;
    DWORD res = ERROR_SUCCESS;

    read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2801

2802
    EnterCriticalSection( &req->read_section );
2803

2804 2805 2806 2807 2808 2809 2810
    if(req->read_size) {
        ret_read = min(size, req->read_size);
        memcpy(buffer, req->read_buf+req->read_pos, ret_read);
        req->read_size -= ret_read;
        req->read_pos += ret_read;
        if(read_mode == READMODE_ASYNC)
            read_mode = READMODE_NOBLOCK;
2811 2812
    }

2813
    if(ret_read < size) {
2814
        res = read_http_stream(req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, read_mode);
2815
        ret_read += current_read;
2816 2817
    }

2818 2819
    LeaveCriticalSection( &req->read_section );

2820 2821 2822
    *read = ret_read;
    TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );

2823 2824
    if(size && !ret_read)
        http_release_netconn(req, res == ERROR_SUCCESS);
2825

2826
    return res;
2827 2828
}

2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861
static BOOL drain_content(http_request_t *req, BOOL blocking)
{
    BOOL ret;

    if(!req->netconn || req->contentLength == -1)
        return FALSE;

    if(!strcmpW(req->verb, szHEAD))
        return TRUE;

    if(!blocking)
        return req->data_stream->vtbl->drain_content(req->data_stream, req);

    EnterCriticalSection( &req->read_section );

    while(1) {
        DWORD bytes_read, res;
        BYTE buf[4096];

        res = HTTPREQ_Read(req, buf, sizeof(buf), &bytes_read, TRUE);
        if(res != ERROR_SUCCESS) {
            ret = FALSE;
            break;
        }
        if(!bytes_read) {
            ret = TRUE;
            break;
        }
    }

    LeaveCriticalSection( &req->read_section );
    return ret;
}
2862

2863
static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2864
{
2865
    http_request_t *req = (http_request_t*)hdr;
2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877
    DWORD res;

    EnterCriticalSection( &req->read_section );
    if(hdr->dwError == INTERNET_HANDLE_IN_USE)
        hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;

    res = HTTPREQ_Read(req, buffer, size, read, TRUE);
    if(res == ERROR_SUCCESS)
        res = hdr->dwError;
    LeaveCriticalSection( &req->read_section );

    return res;
2878 2879
}

2880 2881
static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
{
2882
    struct WORKREQ_HTTPREADFILEEX const *data = &workRequest->u.HttpReadFileEx;
2883
    http_request_t *req = (http_request_t*)workRequest->hdr;
2884 2885 2886 2887
    DWORD res;

    TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);

2888
    res = HTTPREQ_Read(req, data->buf, data->size, data->ret_read, TRUE);
2889

2890
    send_request_complete(req, res == ERROR_SUCCESS, res);
2891 2892
}

2893
static DWORD HTTPREQ_ReadFileEx(object_header_t *hdr, void *buf, DWORD size, DWORD *ret_read,
2894 2895 2896
        DWORD flags, DWORD_PTR context)
{

2897
    http_request_t *req = (http_request_t*)hdr;
2898
    DWORD res, read, cread, error = ERROR_SUCCESS;
2899 2900 2901 2902 2903 2904

    if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
        FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));

    INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);

2905
    if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2906 2907
    {
        WORKREQUEST workRequest;
2908

2909 2910 2911 2912
        if (TryEnterCriticalSection( &req->read_section ))
        {
            if (get_avail_data(req))
            {
2913
                res = HTTPREQ_Read(req, buf, size, &read, FALSE);
2914 2915 2916 2917 2918 2919
                LeaveCriticalSection( &req->read_section );
                goto done;
            }
            LeaveCriticalSection( &req->read_section );
        }

2920 2921
        workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
        workRequest.hdr = WININET_AddRef(&req->hdr);
2922 2923 2924
        workRequest.u.HttpReadFileEx.buf = buf;
        workRequest.u.HttpReadFileEx.size = size;
        workRequest.u.HttpReadFileEx.ret_read = ret_read;
2925

2926
        INTERNET_AsyncCall(&workRequest);
2927

2928
        return ERROR_IO_PENDING;
2929 2930
    }

2931 2932 2933 2934 2935 2936 2937 2938 2939
    read = 0;

    EnterCriticalSection( &req->read_section );
    if(hdr->dwError == ERROR_SUCCESS)
        hdr->dwError = INTERNET_HANDLE_IN_USE;
    else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
        hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;

    while(1) {
2940
        res = HTTPREQ_Read(req, (char*)buf+read, size-read, &cread, !(flags & IRF_NO_WAIT));
2941
        if(res != ERROR_SUCCESS)
2942 2943
            break;

2944
        read += cread;
2945
        if(read == size || end_of_read_data(req))
2946
            break;
2947

2948 2949 2950
        LeaveCriticalSection( &req->read_section );

        INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2951
                &cread, sizeof(cread));
2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963
        INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
                INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);

        EnterCriticalSection( &req->read_section );
    }

    if(hdr->dwError == INTERNET_HANDLE_IN_USE)
        hdr->dwError = ERROR_SUCCESS;
    else
        error = hdr->dwError;

    LeaveCriticalSection( &req->read_section );
2964

2965
done:
2966
    *ret_read = read;
2967 2968
    if (res == ERROR_SUCCESS) {
        INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2969
                &read, sizeof(read));
2970 2971
    }

2972
    return res==ERROR_SUCCESS ? error : res;
2973 2974
}

2975
static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2976
{
2977
    DWORD res;
2978
    http_request_t *request = (http_request_t*)hdr;
2979

2980
    INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2981

2982
    *written = 0;
2983
    res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
2984
    if (res == ERROR_SUCCESS)
2985
        request->bytesWritten += *written;
2986

2987
    INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2988
    return res;
2989 2990
}

2991 2992
static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
{
2993
    http_request_t *req = (http_request_t*)workRequest->hdr;
2994

2995
    HTTP_ReceiveRequestData(req, FALSE);
2996 2997
}

2998
static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2999
{
3000
    http_request_t *req = (http_request_t*)hdr;
3001 3002 3003

    TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);

3004
    if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3005
    {
3006
        WORKREQUEST workRequest;
3007

3008 3009
        /* never wait, if we can't enter the section we queue an async request right away */
        if (TryEnterCriticalSection( &req->read_section ))
3010
        {
3011
            refill_read_buffer(req, READMODE_NOBLOCK, NULL);
3012 3013 3014 3015
            if ((*available = get_avail_data( req ))) goto done;
            if (end_of_read_data( req )) goto done;
            LeaveCriticalSection( &req->read_section );
        }
3016

3017 3018
        workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
        workRequest.hdr = WININET_AddRef( &req->hdr );
3019

3020
        INTERNET_AsyncCall(&workRequest);
3021

3022
        return ERROR_IO_PENDING;
3023
    }
3024

3025 3026 3027 3028
    EnterCriticalSection( &req->read_section );

    if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
    {
3029
        refill_read_buffer( req, READMODE_ASYNC, NULL );
3030 3031 3032 3033 3034
        *available = get_avail_data( req );
    }

done:
    LeaveCriticalSection( &req->read_section );
3035

3036
    TRACE( "returning %u\n", *available );
3037 3038 3039
    return ERROR_SUCCESS;
}

3040
static const object_vtbl_t HTTPREQVtbl = {
3041
    HTTPREQ_Destroy,
3042
    HTTPREQ_CloseConnection,
3043
    HTTPREQ_QueryOption,
3044
    HTTPREQ_SetOption,
3045
    HTTPREQ_ReadFile,
3046
    HTTPREQ_ReadFileEx,
3047
    HTTPREQ_WriteFile,
3048
    HTTPREQ_QueryDataAvailable,
3049
    NULL
3050 3051
};

3052
/***********************************************************************
3053
 *           HTTP_HttpOpenRequestW (internal)
3054 3055 3056 3057 3058 3059 3060 3061
 *
 * Open a HTTP request handle
 *
 * RETURNS
 *    HINTERNET  a HTTP request handle on success
 *    NULL 	 on failure
 *
 */
3062
static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
3063 3064 3065
        LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
        LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
        DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3066
{
3067
    appinfo_t *hIC = session->appInfo;
3068
    http_request_t *request;
3069
    DWORD len, res = ERROR_SUCCESS;
3070

3071
    TRACE("-->\n");
3072

3073 3074
    request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
    if(!request)
3075 3076
        return ERROR_OUTOFMEMORY;

3077 3078 3079
    request->hdr.htype = WH_HHTTPREQ;
    request->hdr.dwFlags = dwFlags;
    request->hdr.dwContext = dwContext;
3080
    request->contentLength = ~0u;
3081

3082 3083
    request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
    request->data_stream = &request->netconn_stream.data_stream;
3084
    request->connect_timeout = session->connect_timeout;
3085 3086
    request->send_timeout = session->send_timeout;
    request->receive_timeout = session->receive_timeout;
3087

3088
    InitializeCriticalSection( &request->read_section );
3089
    request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3090

3091
    WININET_AddRef( &session->hdr );
3092
    request->session = session;
3093
    list_add_head( &session->hdr.children, &request->hdr.entry );
3094

3095
    request->server = get_server(session->hostName, session->hostPort, (dwFlags & INTERNET_FLAG_SECURE) != 0, TRUE);
3096 3097 3098 3099 3100
    if(!request->server) {
        WININET_Release(&request->hdr);
        return ERROR_OUTOFMEMORY;
    }

3101
    if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
3102
        request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
3103
    if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
3104
        request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
3105

Jacek Caban's avatar
Jacek Caban committed
3106
    if (lpszObjectName && *lpszObjectName) {
3107
        HRESULT rc;
3108 3109 3110

        len = 0;
        rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
3111
        if (rc != E_POINTER)
3112
            len = strlenW(lpszObjectName)+1;
3113
        request->path = heap_alloc(len*sizeof(WCHAR));
3114
        rc = UrlEscapeW(lpszObjectName, request->path, &len,
3115
                   URL_ESCAPE_SPACES_ONLY);
3116
        if (rc != S_OK)
3117
        {
3118
            ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3119
            strcpyW(request->path,lpszObjectName);
3120
        }
3121 3122 3123
    }else {
        static const WCHAR slashW[] = {'/',0};

3124
        request->path = heap_strdupW(slashW);
3125
    }
3126

Jacek Caban's avatar
Jacek Caban committed
3127
    if (lpszReferrer && *lpszReferrer)
3128
        HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3129

3130
    if (lpszAcceptTypes)
3131 3132
    {
        int i;
3133 3134 3135
        for (i = 0; lpszAcceptTypes[i]; i++)
        {
            if (!*lpszAcceptTypes[i]) continue;
3136
            HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3137 3138 3139 3140
                               HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
                               HTTP_ADDHDR_FLAG_REQ |
                               (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
        }
3141
    }
3142

3143
    request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3144
    request->version = heap_strdupW(lpszVersion && *lpszVersion ? lpszVersion : g_szHttp1_1);
3145

3146
    HTTP_ProcessHeader(request, hostW, request->server->canon_host_port, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
3147

3148 3149
    if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
        session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
3150 3151
                        INTERNET_DEFAULT_HTTPS_PORT :
                        INTERNET_DEFAULT_HTTP_PORT);
3152

3153
    if (hIC->proxy && hIC->proxy[0])
3154
        HTTP_DealWithProxy( hIC, session, request );
3155

3156 3157
    INTERNET_SendCallback(&session->hdr, dwContext,
                          INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
3158
                          sizeof(HINTERNET));
3159

3160
    TRACE("<-- %u (%p)\n", res, request);
3161 3162

    if(res != ERROR_SUCCESS) {
3163
        WININET_Release( &request->hdr );
3164 3165 3166
        *ret = NULL;
        return res;
    }
3167

3168
    *ret = request->hdr.hInternet;
3169
    return ERROR_SUCCESS;
3170 3171
}

3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186
/***********************************************************************
 *           HttpOpenRequestW (WININET.@)
 *
 * Open a HTTP request handle
 *
 * RETURNS
 *    HINTERNET  a HTTP request handle on success
 *    NULL 	 on failure
 *
 */
HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
	LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
	LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
	DWORD dwFlags, DWORD_PTR dwContext)
{
3187
    http_session_t *session;
3188
    HINTERNET handle = NULL;
3189
    DWORD res;
3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201

    TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
          debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
          debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
          dwFlags, dwContext);
    if(lpszAcceptTypes!=NULL)
    {
        int i;
        for(i=0;lpszAcceptTypes[i]!=NULL;i++)
            TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
    }

3202 3203
    session = (http_session_t*) get_handle_object( hHttpSession );
    if (NULL == session ||  session->hdr.htype != WH_HHTTPSESSION)
3204
    {
3205 3206
        res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
        goto lend;
3207 3208 3209 3210 3211 3212 3213 3214 3215
    }

    /*
     * My tests seem to show that the windows version does not
     * become asynchronous until after this point. And anyhow
     * if this call was asynchronous then how would you get the
     * necessary HINTERNET pointer returned by this function.
     *
     */
3216
    res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3217 3218
                                lpszVersion, lpszReferrer, lpszAcceptTypes,
                                dwFlags, dwContext, &handle);
3219
lend:
3220 3221
    if( session )
        WININET_Release( &session->hdr );
3222
    TRACE("returning %p\n", handle);
3223 3224
    if(res != ERROR_SUCCESS)
        SetLastError(res);
3225 3226 3227
    return handle;
}

3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275
static const LPCWSTR header_lookup[] = {
    szMime_Version,		/* HTTP_QUERY_MIME_VERSION = 0 */
    szContent_Type,		/* HTTP_QUERY_CONTENT_TYPE = 1 */
    szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
    szContent_ID,		/* HTTP_QUERY_CONTENT_ID = 3 */
    NULL,			/* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
    szContent_Length,		/* HTTP_QUERY_CONTENT_LENGTH =  5 */
    szContent_Language,		/* HTTP_QUERY_CONTENT_LANGUAGE =  6 */
    szAllow,			/* HTTP_QUERY_ALLOW = 7 */
    szPublic,			/* HTTP_QUERY_PUBLIC = 8 */
    szDate,			/* HTTP_QUERY_DATE = 9 */
    szExpires,			/* HTTP_QUERY_EXPIRES = 10 */
    szLast_Modified,		/* HTTP_QUERY_LAST_MODIFIED = 11 */
    NULL,			/* HTTP_QUERY_MESSAGE_ID = 12 */
    szURI,			/* HTTP_QUERY_URI = 13 */
    szFrom,			/* HTTP_QUERY_DERIVED_FROM = 14 */
    NULL,			/* HTTP_QUERY_COST = 15 */
    NULL,			/* HTTP_QUERY_LINK = 16 */
    szPragma,			/* HTTP_QUERY_PRAGMA = 17 */
    NULL,			/* HTTP_QUERY_VERSION = 18 */
    szStatus,			/* HTTP_QUERY_STATUS_CODE = 19 */
    NULL,			/* HTTP_QUERY_STATUS_TEXT = 20 */
    NULL,			/* HTTP_QUERY_RAW_HEADERS = 21 */
    NULL,			/* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
    szConnection,		/* HTTP_QUERY_CONNECTION = 23 */
    szAccept,			/* HTTP_QUERY_ACCEPT = 24 */
    szAccept_Charset,		/* HTTP_QUERY_ACCEPT_CHARSET = 25 */
    szAccept_Encoding,		/* HTTP_QUERY_ACCEPT_ENCODING = 26 */
    szAccept_Language,		/* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
    szAuthorization,		/* HTTP_QUERY_AUTHORIZATION = 28 */
    szContent_Encoding,		/* HTTP_QUERY_CONTENT_ENCODING = 29 */
    NULL,			/* HTTP_QUERY_FORWARDED = 30 */
    NULL,			/* HTTP_QUERY_FROM = 31 */
    szIf_Modified_Since,	/* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
    szLocation,			/* HTTP_QUERY_LOCATION = 33 */
    NULL,			/* HTTP_QUERY_ORIG_URI = 34 */
    szReferer,			/* HTTP_QUERY_REFERER = 35 */
    szRetry_After,		/* HTTP_QUERY_RETRY_AFTER = 36 */
    szServer,			/* HTTP_QUERY_SERVER = 37 */
    NULL,			/* HTTP_TITLE = 38 */
    szUser_Agent,		/* HTTP_QUERY_USER_AGENT = 39 */
    szWWW_Authenticate,		/* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
    szProxy_Authenticate,	/* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
    szAccept_Ranges,		/* HTTP_QUERY_ACCEPT_RANGES = 42 */
    szSet_Cookie,		/* HTTP_QUERY_SET_COOKIE = 43 */
    szCookie,			/* HTTP_QUERY_COOKIE = 44 */
    NULL,			/* HTTP_QUERY_REQUEST_METHOD = 45 */
    NULL,			/* HTTP_QUERY_REFRESH = 46 */
3276
    szContent_Disposition,	/* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3277 3278 3279 3280 3281 3282 3283
    szAge,			/* HTTP_QUERY_AGE = 48 */
    szCache_Control,		/* HTTP_QUERY_CACHE_CONTROL = 49 */
    szContent_Base,		/* HTTP_QUERY_CONTENT_BASE = 50 */
    szContent_Location,		/* HTTP_QUERY_CONTENT_LOCATION = 51 */
    szContent_MD5,		/* HTTP_QUERY_CONTENT_MD5 = 52 */
    szContent_Range,		/* HTTP_QUERY_CONTENT_RANGE = 53 */
    szETag,			/* HTTP_QUERY_ETAG = 54 */
3284
    hostW,			/* HTTP_QUERY_HOST = 55 */
3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299
    szIf_Match,			/* HTTP_QUERY_IF_MATCH = 56 */
    szIf_None_Match,		/* HTTP_QUERY_IF_NONE_MATCH = 57 */
    szIf_Range,			/* HTTP_QUERY_IF_RANGE = 58 */
    szIf_Unmodified_Since,	/* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
    szMax_Forwards,		/* HTTP_QUERY_MAX_FORWARDS = 60 */
    szProxy_Authorization,	/* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
    szRange,			/* HTTP_QUERY_RANGE = 62 */
    szTransfer_Encoding,	/* HTTP_QUERY_TRANSFER_ENCODING = 63 */
    szUpgrade,			/* HTTP_QUERY_UPGRADE = 64 */
    szVary,			/* HTTP_QUERY_VARY = 65 */
    szVia,			/* HTTP_QUERY_VIA = 66 */
    szWarning,			/* HTTP_QUERY_WARNING = 67 */
    szExpect,			/* HTTP_QUERY_EXPECT = 68 */
    szProxy_Connection,		/* HTTP_QUERY_PROXY_CONNECTION = 69 */
    szUnless_Modified_Since,	/* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3300 3301
};

3302 3303
#define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))

3304
/***********************************************************************
3305
 *           HTTP_HttpQueryInfoW (internal)
3306
 */
3307
static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3308
        LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3309
{
3310
    LPHTTPHEADERW lphttpHdr = NULL;
3311
    BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3312
    INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3313
    DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3314
    INT index = -1;
3315

3316
    /* Find requested header structure */
3317
    switch (level)
3318
    {
3319
    case HTTP_QUERY_CUSTOM:
3320
        if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3321
        index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3322 3323
        break;
    case HTTP_QUERY_RAW_HEADERS_CRLF:
3324
        {
3325
            LPWSTR headers;
3326
            DWORD len = 0;
3327
            DWORD res = ERROR_INVALID_PARAMETER;
3328 3329

            if (request_only)
3330
                headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3331
            else
3332
                headers = request->rawHeaders;
3333

3334 3335 3336
            if (headers)
                len = strlenW(headers) * sizeof(WCHAR);

3337
            if (len + sizeof(WCHAR) > *lpdwBufferLength)
3338
            {
3339
                len += sizeof(WCHAR);
3340
                res = ERROR_INSUFFICIENT_BUFFER;
3341 3342
            }
            else if (lpBuffer)
3343
            {
3344 3345 3346 3347
                if (headers)
                    memcpy(lpBuffer, headers, len + sizeof(WCHAR));
                else
                {
3348 3349
                    len = strlenW(szCrLf) * sizeof(WCHAR);
                    memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3350
                }
3351
                TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3352
                res = ERROR_SUCCESS;
3353
            }
3354
            *lpdwBufferLength = len;
3355

3356
            if (request_only) heap_free(headers);
3357
            return res;
3358
        }
3359
    case HTTP_QUERY_RAW_HEADERS:
3360
        {
3361
            LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3362
            DWORD i, size = 0;
3363
            LPWSTR pszString = lpBuffer;
3364 3365 3366 3367 3368 3369 3370 3371

            for (i = 0; ppszRawHeaderLines[i]; i++)
                size += strlenW(ppszRawHeaderLines[i]) + 1;

            if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
            {
                HTTP_FreeTokens(ppszRawHeaderLines);
                *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3372
                return ERROR_INSUFFICIENT_BUFFER;
3373
            }
3374
            if (pszString)
3375
            {
3376 3377 3378 3379 3380 3381 3382 3383
                for (i = 0; ppszRawHeaderLines[i]; i++)
                {
                    DWORD len = strlenW(ppszRawHeaderLines[i]);
                    memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
                    pszString += len+1;
                }
                *pszString = '\0';
                TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3384
            }
3385
            *lpdwBufferLength = size * sizeof(WCHAR);
3386 3387
            HTTP_FreeTokens(ppszRawHeaderLines);

3388
            return ERROR_SUCCESS;
3389
        }
3390
    case HTTP_QUERY_STATUS_TEXT:
3391
        if (request->statusText)
3392
        {
3393
            DWORD len = strlenW(request->statusText);
3394 3395 3396
            if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
            {
                *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3397
                return ERROR_INSUFFICIENT_BUFFER;
3398
            }
3399 3400
            if (lpBuffer)
            {
3401
                memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3402 3403
                TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
            }
3404
            *lpdwBufferLength = len * sizeof(WCHAR);
3405
            return ERROR_SUCCESS;
3406
        }
3407
        break;
3408
    case HTTP_QUERY_VERSION:
3409
        if (request->version)
3410
        {
3411
            DWORD len = strlenW(request->version);
3412 3413 3414
            if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
            {
                *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3415
                return ERROR_INSUFFICIENT_BUFFER;
3416
            }
3417 3418
            if (lpBuffer)
            {
3419
                memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3420 3421
                TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
            }
3422
            *lpdwBufferLength = len * sizeof(WCHAR);
3423
            return ERROR_SUCCESS;
3424
        }
3425
        break;
3426
    case HTTP_QUERY_CONTENT_ENCODING:
3427
        index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3428 3429
                requested_index,request_only);
        break;
3430 3431 3432
    case HTTP_QUERY_STATUS_CODE: {
        DWORD res = ERROR_SUCCESS;

3433 3434 3435 3436
        if(request_only)
            return ERROR_HTTP_INVALID_QUERY_REQUEST;

        if(requested_index)
3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449
            break;

        if(dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) {
            if(*lpdwBufferLength >= sizeof(DWORD))
                *(DWORD*)lpBuffer = request->status_code;
            else
                res = ERROR_INSUFFICIENT_BUFFER;
            *lpdwBufferLength = sizeof(DWORD);
        }else {
            WCHAR buf[12];
            DWORD size;
            static const WCHAR formatW[] = {'%','u',0};

3450
            size = sprintfW(buf, formatW, request->status_code) * sizeof(WCHAR);
3451

3452 3453 3454 3455
            if(size <= *lpdwBufferLength) {
                memcpy(lpBuffer, buf, size+sizeof(WCHAR));
            }else {
                size += sizeof(WCHAR);
3456
                res = ERROR_INSUFFICIENT_BUFFER;
3457
            }
3458 3459 3460 3461 3462

            *lpdwBufferLength = size;
        }
        return res;
    }
3463
    default:
3464 3465
        assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));

3466
        if (level < LAST_TABLE_HEADER && header_lookup[level])
3467
            index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3468
                                              requested_index,request_only);
3469 3470
    }

3471
    if (index >= 0)
3472
        lphttpHdr = &request->custHeaders[index];
3473

Austin English's avatar
Austin English committed
3474
    /* Ensure header satisfies requested attributes */
3475 3476 3477
    if (!lphttpHdr ||
        ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
         (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3478
    {
3479
        return ERROR_HTTP_HEADER_NOT_FOUND;
3480
    }
3481

3482
    if (lpdwIndex) (*lpdwIndex)++;
3483

Austin English's avatar
Austin English committed
3484
    /* coalesce value to requested type */
3485
    if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3486
    {
3487 3488
        *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
        TRACE(" returning number: %d\n", *(int *)lpBuffer);
3489
     }
3490
    else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3491 3492 3493 3494
    {
        time_t tmpTime;
        struct tm tmpTM;
        SYSTEMTIME *STHook;
3495

3496 3497 3498
        tmpTime = ConvertTimeString(lphttpHdr->lpszValue);

        tmpTM = *gmtime(&tmpTime);
3499 3500 3501 3502 3503 3504 3505 3506 3507
        STHook = (SYSTEMTIME *)lpBuffer;
        STHook->wDay = tmpTM.tm_mday;
        STHook->wHour = tmpTM.tm_hour;
        STHook->wMilliseconds = 0;
        STHook->wMinute = tmpTM.tm_min;
        STHook->wDayOfWeek = tmpTM.tm_wday;
        STHook->wMonth = tmpTM.tm_mon + 1;
        STHook->wSecond = tmpTM.tm_sec;
        STHook->wYear = tmpTM.tm_year;
3508

3509 3510 3511
        TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
              STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
              STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3512
    }
3513
    else if (lphttpHdr->lpszValue)
3514
    {
3515
        DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3516

3517
        if (len > *lpdwBufferLength)
3518
        {
3519
            *lpdwBufferLength = len;
3520
            return ERROR_INSUFFICIENT_BUFFER;
3521
        }
3522 3523 3524
        if (lpBuffer)
        {
            memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3525
            TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3526
        }
3527
        *lpdwBufferLength = len - sizeof(WCHAR);
3528
    }
3529
    return ERROR_SUCCESS;
3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542
}

/***********************************************************************
 *           HttpQueryInfoW (WININET.@)
 *
 * Queries for information about an HTTP request
 *
 * RETURNS
 *    TRUE  on success
 *    FALSE on failure
 *
 */
BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3543
        LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3544
{
3545
    http_request_t *request;
3546
    DWORD res;
3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593

    if (TRACE_ON(wininet)) {
#define FE(x) { x, #x }
	static const wininet_flag_info query_flags[] = {
	    FE(HTTP_QUERY_MIME_VERSION),
	    FE(HTTP_QUERY_CONTENT_TYPE),
	    FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
	    FE(HTTP_QUERY_CONTENT_ID),
	    FE(HTTP_QUERY_CONTENT_DESCRIPTION),
	    FE(HTTP_QUERY_CONTENT_LENGTH),
	    FE(HTTP_QUERY_CONTENT_LANGUAGE),
	    FE(HTTP_QUERY_ALLOW),
	    FE(HTTP_QUERY_PUBLIC),
	    FE(HTTP_QUERY_DATE),
	    FE(HTTP_QUERY_EXPIRES),
	    FE(HTTP_QUERY_LAST_MODIFIED),
	    FE(HTTP_QUERY_MESSAGE_ID),
	    FE(HTTP_QUERY_URI),
	    FE(HTTP_QUERY_DERIVED_FROM),
	    FE(HTTP_QUERY_COST),
	    FE(HTTP_QUERY_LINK),
	    FE(HTTP_QUERY_PRAGMA),
	    FE(HTTP_QUERY_VERSION),
	    FE(HTTP_QUERY_STATUS_CODE),
	    FE(HTTP_QUERY_STATUS_TEXT),
	    FE(HTTP_QUERY_RAW_HEADERS),
	    FE(HTTP_QUERY_RAW_HEADERS_CRLF),
	    FE(HTTP_QUERY_CONNECTION),
	    FE(HTTP_QUERY_ACCEPT),
	    FE(HTTP_QUERY_ACCEPT_CHARSET),
	    FE(HTTP_QUERY_ACCEPT_ENCODING),
	    FE(HTTP_QUERY_ACCEPT_LANGUAGE),
	    FE(HTTP_QUERY_AUTHORIZATION),
	    FE(HTTP_QUERY_CONTENT_ENCODING),
	    FE(HTTP_QUERY_FORWARDED),
	    FE(HTTP_QUERY_FROM),
	    FE(HTTP_QUERY_IF_MODIFIED_SINCE),
	    FE(HTTP_QUERY_LOCATION),
	    FE(HTTP_QUERY_ORIG_URI),
	    FE(HTTP_QUERY_REFERER),
	    FE(HTTP_QUERY_RETRY_AFTER),
	    FE(HTTP_QUERY_SERVER),
	    FE(HTTP_QUERY_TITLE),
	    FE(HTTP_QUERY_USER_AGENT),
	    FE(HTTP_QUERY_WWW_AUTHENTICATE),
	    FE(HTTP_QUERY_PROXY_AUTHENTICATE),
	    FE(HTTP_QUERY_ACCEPT_RANGES),
3594 3595
        FE(HTTP_QUERY_SET_COOKIE),
        FE(HTTP_QUERY_COOKIE),
3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629
	    FE(HTTP_QUERY_REQUEST_METHOD),
	    FE(HTTP_QUERY_REFRESH),
	    FE(HTTP_QUERY_CONTENT_DISPOSITION),
	    FE(HTTP_QUERY_AGE),
	    FE(HTTP_QUERY_CACHE_CONTROL),
	    FE(HTTP_QUERY_CONTENT_BASE),
	    FE(HTTP_QUERY_CONTENT_LOCATION),
	    FE(HTTP_QUERY_CONTENT_MD5),
	    FE(HTTP_QUERY_CONTENT_RANGE),
	    FE(HTTP_QUERY_ETAG),
	    FE(HTTP_QUERY_HOST),
	    FE(HTTP_QUERY_IF_MATCH),
	    FE(HTTP_QUERY_IF_NONE_MATCH),
	    FE(HTTP_QUERY_IF_RANGE),
	    FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
	    FE(HTTP_QUERY_MAX_FORWARDS),
	    FE(HTTP_QUERY_PROXY_AUTHORIZATION),
	    FE(HTTP_QUERY_RANGE),
	    FE(HTTP_QUERY_TRANSFER_ENCODING),
	    FE(HTTP_QUERY_UPGRADE),
	    FE(HTTP_QUERY_VARY),
	    FE(HTTP_QUERY_VIA),
	    FE(HTTP_QUERY_WARNING),
	    FE(HTTP_QUERY_CUSTOM)
	};
	static const wininet_flag_info modifier_flags[] = {
	    FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
	    FE(HTTP_QUERY_FLAG_SYSTEMTIME),
	    FE(HTTP_QUERY_FLAG_NUMBER),
	    FE(HTTP_QUERY_FLAG_COALESCE)
	};
#undef FE
	DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
	DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3630
	DWORD i;
3631

3632
	TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3633 3634 3635
	TRACE("  Attribute:");
	for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
	    if (query_flags[i].val == info) {
Diego Pettenò's avatar
Diego Pettenò committed
3636
		TRACE(" %s", query_flags[i].name);
3637 3638 3639 3640
		break;
	    }
	}
	if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3641
	    TRACE(" Unknown (%08x)", info);
3642 3643
	}

Diego Pettenò's avatar
Diego Pettenò committed
3644
	TRACE(" Modifier:");
3645 3646
	for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
	    if (modifier_flags[i].val & info_mod) {
Diego Pettenò's avatar
Diego Pettenò committed
3647
		TRACE(" %s", modifier_flags[i].name);
3648 3649 3650 3651 3652
		info_mod &= ~ modifier_flags[i].val;
	    }
	}
	
	if (info_mod) {
3653
	    TRACE(" Unknown (%08x)", info_mod);
3654
	}
Diego Pettenò's avatar
Diego Pettenò committed
3655
	TRACE("\n");
3656 3657
    }
    
3658 3659
    request = (http_request_t*) get_handle_object( hHttpRequest );
    if (NULL == request ||  request->hdr.htype != WH_HHTTPREQ)
3660
    {
3661 3662
        res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
        goto lend;
3663 3664
    }

3665 3666
    if (lpBuffer == NULL)
        *lpdwBufferLength = 0;
3667
    res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3668
                               lpBuffer, lpdwBufferLength, lpdwIndex);
3669

3670
lend:
3671 3672
    if( request )
         WININET_Release( &request->hdr );
3673

3674 3675 3676 3677
    TRACE("%u <--\n", res);
    if(res != ERROR_SUCCESS)
        SetLastError(res);
    return res == ERROR_SUCCESS;
3678 3679
}

3680
/***********************************************************************
3681
 *           HttpQueryInfoA (WININET.@)
3682 3683 3684 3685 3686 3687 3688 3689
 *
 * Queries for information about an HTTP request
 *
 * RETURNS
 *    TRUE  on success
 *    FALSE on failure
 *
 */
3690
BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3691 3692 3693
	LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
{
    BOOL result;
3694 3695 3696
    DWORD len;
    WCHAR* bufferW;

3697 3698 3699
    if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
       (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
    {
3700 3701
        return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
                               lpdwBufferLength, lpdwIndex );
3702
    }
3703

3704 3705
    if (lpBuffer)
    {
3706
        DWORD alloclen;
3707
        len = (*lpdwBufferLength)*sizeof(WCHAR);
3708 3709 3710 3711 3712 3713 3714 3715
        if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
        {
            alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
            if (alloclen < len)
                alloclen = len;
        }
        else
            alloclen = len;
3716
        bufferW = heap_alloc(alloclen);
3717 3718
        /* buffer is in/out because of HTTP_QUERY_CUSTOM */
        if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3719
            MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3720 3721 3722 3723 3724 3725
    } else
    {
        bufferW = NULL;
        len = 0;
    }

3726 3727 3728
    result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
                           &len, lpdwIndex );
    if( result )
3729
    {
3730
        len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3731
                                     lpBuffer, *lpdwBufferLength, NULL, NULL );
3732 3733 3734
        *lpdwBufferLength = len - 1;

        TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3735
    }
3736 3737 3738 3739 3740
    else
        /* since the strings being returned from HttpQueryInfoW should be
         * only ASCII characters, it is reasonable to assume that all of
         * the Unicode characters can be reduced to a single byte */
        *lpdwBufferLength = len / sizeof(WCHAR);
3741

3742
    heap_free( bufferW );
3743 3744
    return result;
}
3745 3746

/***********************************************************************
3747
 *           HTTP_GetRedirectURL (internal)
3748
 */
3749
static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3750
{
3751 3752
    static WCHAR szHttp[] = {'h','t','t','p',0};
    static WCHAR szHttps[] = {'h','t','t','p','s',0};
3753
    http_session_t *session = request->session;
3754 3755 3756 3757
    URL_COMPONENTSW urlComponents;
    DWORD url_length = 0;
    LPWSTR orig_url;
    LPWSTR combined_url;
3758

3759
    urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3760
    urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3761
    urlComponents.dwSchemeLength = 0;
3762
    urlComponents.lpszHostName = session->hostName;
3763
    urlComponents.dwHostNameLength = 0;
3764 3765
    urlComponents.nPort = session->hostPort;
    urlComponents.lpszUserName = session->userName;
3766 3767 3768
    urlComponents.dwUserNameLength = 0;
    urlComponents.lpszPassword = NULL;
    urlComponents.dwPasswordLength = 0;
3769
    urlComponents.lpszUrlPath = request->path;
3770 3771 3772
    urlComponents.dwUrlPathLength = 0;
    urlComponents.lpszExtraInfo = NULL;
    urlComponents.dwExtraInfoLength = 0;
3773

3774 3775 3776
    if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
        (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
        return NULL;
3777

3778
    orig_url = heap_alloc(url_length);
3779

3780 3781 3782 3783
    /* convert from bytes to characters */
    url_length = url_length / sizeof(WCHAR) - 1;
    if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
    {
3784
        heap_free(orig_url);
3785 3786
        return NULL;
    }
3787

3788 3789 3790 3791
    url_length = 0;
    if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
        (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
    {
3792
        heap_free(orig_url);
3793 3794
        return NULL;
    }
3795
    combined_url = heap_alloc(url_length * sizeof(WCHAR));
3796

3797 3798
    if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
    {
3799 3800
        heap_free(orig_url);
        heap_free(combined_url);
3801 3802
        return NULL;
    }
3803
    heap_free(orig_url);
3804 3805
    return combined_url;
}
3806 3807


3808 3809 3810
/***********************************************************************
 *           HTTP_HandleRedirect (internal)
 */
3811
static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3812
{
3813
    http_session_t *session = request->session;
3814
    WCHAR path[INTERNET_MAX_PATH_LENGTH];
3815
    int index;
3816

3817
    if(lpszUrl[0]=='/')
3818
    {
3819 3820
        /* if it's an absolute path, keep the same session info */
        lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3821
    }
3822 3823 3824
    else
    {
        URL_COMPONENTSW urlComponents;
3825 3826 3827
        WCHAR protocol[INTERNET_MAX_SCHEME_LENGTH];
        WCHAR hostName[INTERNET_MAX_HOST_NAME_LENGTH];
        WCHAR userName[INTERNET_MAX_USER_NAME_LENGTH];
3828 3829 3830 3831
        BOOL custom_port = FALSE;

        static WCHAR httpW[] = {'h','t','t','p',0};
        static WCHAR httpsW[] = {'h','t','t','p','s',0};
3832

3833 3834 3835
        userName[0] = 0;
        hostName[0] = 0;
        protocol[0] = 0;
3836

3837 3838
        urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
        urlComponents.lpszScheme = protocol;
3839
        urlComponents.dwSchemeLength = INTERNET_MAX_SCHEME_LENGTH;
3840
        urlComponents.lpszHostName = hostName;
3841
        urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
3842
        urlComponents.lpszUserName = userName;
3843
        urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
3844 3845 3846
        urlComponents.lpszPassword = NULL;
        urlComponents.dwPasswordLength = 0;
        urlComponents.lpszUrlPath = path;
3847
        urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
3848 3849 3850
        urlComponents.lpszExtraInfo = NULL;
        urlComponents.dwExtraInfoLength = 0;
        if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3851
            return INTERNET_GetLastError();
3852

3853 3854 3855 3856 3857 3858
        if(!strcmpiW(protocol, httpW)) {
            if(request->hdr.dwFlags & INTERNET_FLAG_SECURE) {
                TRACE("redirect from secure page to non-secure page\n");
                /* FIXME: warn about from secure redirect to non-secure page */
                request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
            }
3859

3860
            if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3861
                urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3862 3863 3864 3865 3866 3867 3868 3869
            else if(urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT)
                custom_port = TRUE;
        }else if(!strcmpiW(protocol, httpsW)) {
            if(!(request->hdr.dwFlags & INTERNET_FLAG_SECURE)) {
                TRACE("redirect from non-secure page to secure page\n");
                /* FIXME: notify about redirect to secure page */
                request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
            }
3870

3871 3872 3873 3874 3875
            if(urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
                urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
            else if(urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
                custom_port = TRUE;
        }
3876

3877
        heap_free(session->hostName);
3878 3879

        if(custom_port) {
3880
            int len;
3881
            static const WCHAR fmt[] = {'%','s',':','%','u',0};
3882 3883
            len = lstrlenW(hostName);
            len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3884
            session->hostName = heap_alloc(len*sizeof(WCHAR));
3885
            sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3886
        }
3887
        else
3888
            session->hostName = heap_strdupW(hostName);
3889

3890
        HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3891

3892
        heap_free(session->userName);
3893
        session->userName = NULL;
3894
        if (userName[0])
3895
            session->userName = heap_strdupW(userName);
3896

3897
        reset_data_stream(request);
3898

3899
        if(strcmpiW(request->server->name, hostName) || request->server->port != urlComponents.nPort) {
3900 3901
            server_t *new_server;

3902
            new_server = get_server(hostName, urlComponents.nPort, urlComponents.nScheme == INTERNET_SCHEME_HTTPS, TRUE);
3903 3904
            server_release(request->server);
            request->server = new_server;
3905
        }
3906
    }
3907
    heap_free(request->path);
3908
    request->path=NULL;
3909
    if (*path)
3910
    {
3911 3912 3913 3914 3915 3916
        DWORD needed = 0;
        HRESULT rc;

        rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
        if (rc != E_POINTER)
            needed = strlenW(path)+1;
3917
        request->path = heap_alloc(needed*sizeof(WCHAR));
3918
        rc = UrlEscapeW(path, request->path, &needed,
3919 3920 3921 3922
                        URL_ESCAPE_SPACES_ONLY);
        if (rc != S_OK)
        {
            ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3923
            strcpyW(request->path,path);
3924
        }
3925
    }
3926

3927
    /* Remove custom content-type/length headers on redirects.  */
3928
    index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3929
    if (0 <= index)
3930 3931
        HTTP_DeleteCustomHeader(request, index);
    index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3932
    if (0 <= index)
3933
        HTTP_DeleteCustomHeader(request, index);
3934

3935
    return ERROR_SUCCESS;
3936 3937
}

3938
/***********************************************************************
3939
 *           HTTP_build_req (internal)
3940
 *
3941
 *  concatenate all the strings in the request together
3942
 */
3943
static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3944
{
3945 3946
    LPCWSTR *t;
    LPWSTR str;
3947

3948 3949 3950
    for( t = list; *t ; t++  )
        len += strlenW( *t );
    len++;
3951

3952
    str = heap_alloc(len*sizeof(WCHAR));
3953
    *str = 0;
3954

3955 3956
    for( t = list; *t ; t++ )
        strcatW( str, *t );
3957

3958 3959
    return str;
}
3960

3961
static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3962
{
3963
    server_t *server = request->server;
3964 3965 3966 3967 3968 3969
    LPWSTR requestString;
    INT len;
    INT cnt;
    INT responseLen;
    char *ascii_req;
    DWORD res;
3970 3971

    static const WCHAR connectW[] = {'C','O','N','N','E','C','T',0};
3972

3973
    TRACE("\n");
3974

3975
    requestString = HTTP_BuildHeaderRequestString( request, connectW, server->host_port, g_szHttp1_1 );
3976

3977 3978 3979
    len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
                                NULL, 0, NULL, NULL );
    len--; /* the nul terminator isn't needed */
3980
    ascii_req = heap_alloc(len);
3981 3982
    WideCharToMultiByte( CP_ACP, 0, requestString, -1, ascii_req, len, NULL, NULL );
    heap_free( requestString );
3983

3984 3985
    TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );

3986
    NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
3987
    res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
3988
    heap_free( ascii_req );
3989 3990 3991
    if (res != ERROR_SUCCESS)
        return res;

3992
    responseLen = HTTP_GetResponseHeaders( request, TRUE );
3993 3994 3995 3996 3997 3998
    if (!responseLen)
        return ERROR_HTTP_INVALID_HEADER;

    return ERROR_SUCCESS;
}

3999
static void HTTP_InsertCookies(http_request_t *request)
4000
{
4001 4002 4003
    DWORD cookie_size, size, cnt = 0;
    HTTPHEADERW *host;
    WCHAR *cookies;
4004

4005
    static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
4006

4007 4008 4009
    host = HTTP_GetHeader(request, hostW);
    if(!host)
        return;
4010

4011
    if(get_cookie(host->lpszValue, request->path, NULL, &cookie_size) != ERROR_SUCCESS)
4012
        return;
4013

4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024
    size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
    if(!(cookies = heap_alloc(size)))
        return;

    cnt += sprintfW(cookies, cookieW);
    get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
    strcatW(cookies, szCrLf);

    HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);

    heap_free(cookies);
4025 4026
}

4027
static WORD HTTP_ParseWkday(LPCWSTR day)
4028
{
4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040
    static const WCHAR days[7][4] = {{ 's','u','n',0 },
                                     { 'm','o','n',0 },
                                     { 't','u','e',0 },
                                     { 'w','e','d',0 },
                                     { 't','h','u',0 },
                                     { 'f','r','i',0 },
                                     { 's','a','t',0 }};
    int i;
    for (i = 0; i < sizeof(days)/sizeof(*days); i++)
        if (!strcmpiW(day, days[i]))
            return i;

4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075
    /* Invalid */
    return 7;
}

static WORD HTTP_ParseMonth(LPCWSTR month)
{
    static const WCHAR jan[] = { 'j','a','n',0 };
    static const WCHAR feb[] = { 'f','e','b',0 };
    static const WCHAR mar[] = { 'm','a','r',0 };
    static const WCHAR apr[] = { 'a','p','r',0 };
    static const WCHAR may[] = { 'm','a','y',0 };
    static const WCHAR jun[] = { 'j','u','n',0 };
    static const WCHAR jul[] = { 'j','u','l',0 };
    static const WCHAR aug[] = { 'a','u','g',0 };
    static const WCHAR sep[] = { 's','e','p',0 };
    static const WCHAR oct[] = { 'o','c','t',0 };
    static const WCHAR nov[] = { 'n','o','v',0 };
    static const WCHAR dec[] = { 'd','e','c',0 };

    if (!strcmpiW(month, jan)) return 1;
    if (!strcmpiW(month, feb)) return 2;
    if (!strcmpiW(month, mar)) return 3;
    if (!strcmpiW(month, apr)) return 4;
    if (!strcmpiW(month, may)) return 5;
    if (!strcmpiW(month, jun)) return 6;
    if (!strcmpiW(month, jul)) return 7;
    if (!strcmpiW(month, aug)) return 8;
    if (!strcmpiW(month, sep)) return 9;
    if (!strcmpiW(month, oct)) return 10;
    if (!strcmpiW(month, nov)) return 11;
    if (!strcmpiW(month, dec)) return 12;
    /* Invalid */
    return 0;
}

4076 4077 4078 4079
/* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
 * optionally preceded by whitespace.
 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
 * st, and sets *str to the first character after the time format.
4080
 */
4081
static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
4082
{
4083 4084
    LPCWSTR ptr = *str;
    WCHAR *nextPtr;
4085 4086
    unsigned long num;

4087 4088 4089 4090 4091
    while (isspaceW(*ptr))
        ptr++;

    num = strtoulW(ptr, &nextPtr, 10);
    if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4092
    {
4093
        ERR("unexpected time format %s\n", debugstr_w(ptr));
4094 4095
        return FALSE;
    }
4096
    if (num > 23)
4097
    {
4098
        ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
4099 4100
        return FALSE;
    }
4101 4102 4103 4104
    ptr = nextPtr + 1;
    st->wHour = (WORD)num;
    num = strtoulW(ptr, &nextPtr, 10);
    if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
4105
    {
4106
        ERR("unexpected time format %s\n", debugstr_w(ptr));
4107 4108
        return FALSE;
    }
4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131
    if (num > 59)
    {
        ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
        return FALSE;
    }
    ptr = nextPtr + 1;
    st->wMinute = (WORD)num;
    num = strtoulW(ptr, &nextPtr, 10);
    if (!nextPtr || nextPtr <= ptr)
    {
        ERR("unexpected time format %s\n", debugstr_w(ptr));
        return FALSE;
    }
    if (num > 59)
    {
        ERR("unexpected second in time format %s\n", debugstr_w(ptr));
        return FALSE;
    }
    ptr = nextPtr + 1;
    *str = ptr;
    st->wSecond = (WORD)num;
    return TRUE;
}
4132

4133 4134 4135 4136 4137 4138 4139
static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
{
    static const WCHAR gmt[]= { 'G','M','T',0 };
    WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
    LPCWSTR ptr;
    SYSTEMTIME st = { 0 };
    unsigned long num;
4140

4141 4142 4143 4144
    for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
         dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
        *dayPtr = *ptr;
    *dayPtr = 0;
4145
    st.wDayOfWeek = HTTP_ParseWkday(day);
4146
    if (st.wDayOfWeek >= 7)
4147
    {
4148
        ERR("unexpected weekday %s\n", debugstr_w(day));
4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170
        return FALSE;
    }

    while (isspaceW(*ptr))
        ptr++;

    for (monthPtr = month; !isspace(*ptr) &&
         monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
         monthPtr++, ptr++)
        *monthPtr = *ptr;
    *monthPtr = 0;
    st.wMonth = HTTP_ParseMonth(month);
    if (!st.wMonth || st.wMonth > 12)
    {
        ERR("unexpected month %s\n", debugstr_w(month));
        return FALSE;
    }

    while (isspaceW(*ptr))
        ptr++;

    num = strtoulW(ptr, &nextPtr, 10);
4171
    if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4172
    {
4173
        ERR("unexpected day %s\n", debugstr_w(ptr));
4174 4175 4176
        return FALSE;
    }
    ptr = nextPtr;
4177 4178 4179 4180 4181 4182 4183
    st.wDay = (WORD)num;

    while (isspaceW(*ptr))
        ptr++;

    if (!HTTP_ParseTime(&st, &ptr))
        return FALSE;
4184 4185 4186 4187 4188

    while (isspaceW(*ptr))
        ptr++;

    num = strtoulW(ptr, &nextPtr, 10);
4189
    if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4190
    {
4191
        ERR("unexpected year %s\n", debugstr_w(ptr));
4192 4193
        return FALSE;
    }
4194 4195 4196 4197 4198 4199 4200 4201 4202 4203
    ptr = nextPtr;
    st.wYear = (WORD)num;

    while (isspaceW(*ptr))
        ptr++;

    /* asctime() doesn't report a timezone, but some web servers do, so accept
     * with or without GMT.
     */
    if (*ptr && strcmpW(ptr, gmt))
4204
    {
4205
        ERR("unexpected timezone %s\n", debugstr_w(ptr));
4206 4207
        return FALSE;
    }
4208 4209 4210 4211 4212 4213 4214 4215 4216
    return SystemTimeToFileTime(&st, ft);
}

static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
{
    static const WCHAR gmt[]= { 'G','M','T',0 };
    WCHAR *nextPtr, day[4], month[4], *monthPtr;
    LPCWSTR ptr;
    unsigned long num;
4217
    SYSTEMTIME st = { 0 };
4218 4219 4220 4221 4222

    ptr = strchrW(value, ',');
    if (!ptr)
        return FALSE;
    if (ptr - value != 3)
4223
    {
4224
        WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4225 4226
        return FALSE;
    }
4227 4228
    memcpy(day, value, (ptr - value) * sizeof(WCHAR));
    day[3] = 0;
4229
    st.wDayOfWeek = HTTP_ParseWkday(day);
4230
    if (st.wDayOfWeek > 6)
4231
    {
4232
        WARN("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4233 4234
        return FALSE;
    }
4235 4236 4237 4238 4239
    ptr++;

    while (isspaceW(*ptr))
        ptr++;

4240
    num = strtoulW(ptr, &nextPtr, 10);
4241
    if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4242
    {
4243
        WARN("unexpected day %s\n", debugstr_w(value));
4244 4245
        return FALSE;
    }
4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258
    ptr = nextPtr;
    st.wDay = (WORD)num;

    while (isspaceW(*ptr))
        ptr++;

    for (monthPtr = month; !isspace(*ptr) &&
         monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
         monthPtr++, ptr++)
        *monthPtr = *ptr;
    *monthPtr = 0;
    st.wMonth = HTTP_ParseMonth(month);
    if (!st.wMonth || st.wMonth > 12)
4259
    {
4260
        WARN("unexpected month %s\n", debugstr_w(month));
4261 4262
        return FALSE;
    }
4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277

    while (isspaceW(*ptr))
        ptr++;

    num = strtoulW(ptr, &nextPtr, 10);
    if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
    {
        ERR("unexpected year %s\n", debugstr_w(value));
        return FALSE;
    }
    ptr = nextPtr;
    st.wYear = (WORD)num;

    if (!HTTP_ParseTime(&st, &ptr))
        return FALSE;
4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289

    while (isspaceW(*ptr))
        ptr++;

    if (strcmpW(ptr, gmt))
    {
        ERR("unexpected time zone %s\n", debugstr_w(ptr));
        return FALSE;
    }
    return SystemTimeToFileTime(&st, ft);
}

4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329
static WORD HTTP_ParseWeekday(LPCWSTR day)
{
    static const WCHAR days[7][10] = {{ 's','u','n','d','a','y',0 },
                                     { 'm','o','n','d','a','y',0 },
                                     { 't','u','e','s','d','a','y',0 },
                                     { 'w','e','d','n','e','s','d','a','y',0 },
                                     { 't','h','u','r','s','d','a','y',0 },
                                     { 'f','r','i','d','a','y',0 },
                                     { 's','a','t','u','r','d','a','y',0 }};
    int i;
    for (i = 0; i < sizeof(days)/sizeof(*days); i++)
        if (!strcmpiW(day, days[i]))
            return i;

    /* Invalid */
    return 7;
}

static BOOL HTTP_ParseRfc850Date(LPCWSTR value, FILETIME *ft)
{
    static const WCHAR gmt[]= { 'G','M','T',0 };
    WCHAR *nextPtr, day[10], month[4], *monthPtr;
    LPCWSTR ptr;
    unsigned long num;
    SYSTEMTIME st = { 0 };

    ptr = strchrW(value, ',');
    if (!ptr)
        return FALSE;
    if (ptr - value == 3)
    {
        memcpy(day, value, (ptr - value) * sizeof(WCHAR));
        day[3] = 0;
        st.wDayOfWeek = HTTP_ParseWkday(day);
        if (st.wDayOfWeek > 6)
        {
            ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
            return FALSE;
        }
    }
4330
    else if (ptr - value < sizeof(day) / sizeof(day[0]))
4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408
    {
        memcpy(day, value, (ptr - value) * sizeof(WCHAR));
        day[ptr - value + 1] = 0;
        st.wDayOfWeek = HTTP_ParseWeekday(day);
        if (st.wDayOfWeek > 6)
        {
            ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
            return FALSE;
        }
    }
    else
    {
        ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
        return FALSE;
    }
    ptr++;

    while (isspaceW(*ptr))
        ptr++;

    num = strtoulW(ptr, &nextPtr, 10);
    if (!nextPtr || nextPtr <= ptr || !num || num > 31)
    {
        ERR("unexpected day %s\n", debugstr_w(value));
        return FALSE;
    }
    ptr = nextPtr;
    st.wDay = (WORD)num;

    if (*ptr != '-')
    {
        ERR("unexpected month format %s\n", debugstr_w(ptr));
        return FALSE;
    }
    ptr++;

    for (monthPtr = month; *ptr != '-' &&
         monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
         monthPtr++, ptr++)
        *monthPtr = *ptr;
    *monthPtr = 0;
    st.wMonth = HTTP_ParseMonth(month);
    if (!st.wMonth || st.wMonth > 12)
    {
        ERR("unexpected month %s\n", debugstr_w(month));
        return FALSE;
    }

    if (*ptr != '-')
    {
        ERR("unexpected year format %s\n", debugstr_w(ptr));
        return FALSE;
    }
    ptr++;

    num = strtoulW(ptr, &nextPtr, 10);
    if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
    {
        ERR("unexpected year %s\n", debugstr_w(value));
        return FALSE;
    }
    ptr = nextPtr;
    st.wYear = (WORD)num;

    if (!HTTP_ParseTime(&st, &ptr))
        return FALSE;

    while (isspaceW(*ptr))
        ptr++;

    if (strcmpW(ptr, gmt))
    {
        ERR("unexpected time zone %s\n", debugstr_w(ptr));
        return FALSE;
    }
    return SystemTimeToFileTime(&st, ft);
}

4409 4410
static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
{
4411
    static const WCHAR zero[] = { '0',0 };
4412 4413
    BOOL ret;

4414 4415 4416 4417 4418 4419
    if (!strcmpW(value, zero))
    {
        ft->dwLowDateTime = ft->dwHighDateTime = 0;
        ret = TRUE;
    }
    else if (strchrW(value, ','))
4420
    {
4421
        ret = HTTP_ParseRfc1123Date(value, ft);
4422 4423 4424 4425 4426 4427 4428
        if (!ret)
        {
            ret = HTTP_ParseRfc850Date(value, ft);
            if (!ret)
                ERR("unexpected date format %s\n", debugstr_w(value));
        }
    }
4429 4430 4431 4432 4433 4434 4435 4436 4437
    else
    {
        ret = HTTP_ParseDateAsAsctime(value, ft);
        if (!ret)
            ERR("unexpected date format %s\n", debugstr_w(value));
    }
    return ret;
}

4438 4439
static void HTTP_ProcessExpires(http_request_t *request)
{
4440
    BOOL expirationFound = FALSE;
4441 4442
    int headerIndex;

4443 4444 4445 4446
    /* Look for a Cache-Control header with a max-age directive, as it takes
     * precedence over the Expires header.
     */
    headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4447 4448
    if (headerIndex != -1)
    {
4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465
        LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
        LPWSTR ptr;

        for (ptr = ccHeader->lpszValue; ptr && *ptr; )
        {
            LPWSTR comma = strchrW(ptr, ','), end, equal;

            if (comma)
                end = comma;
            else
                end = ptr + strlenW(ptr);
            for (equal = end - 1; equal > ptr && *equal != '='; equal--)
                ;
            if (*equal == '=')
            {
                static const WCHAR max_age[] = {
                    'm','a','x','-','a','g','e',0 };
4466

4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501
                if (!strncmpiW(ptr, max_age, equal - ptr - 1))
                {
                    LPWSTR nextPtr;
                    unsigned long age;

                    age = strtoulW(equal + 1, &nextPtr, 10);
                    if (nextPtr > equal + 1)
                    {
                        LARGE_INTEGER ft;

                        NtQuerySystemTime( &ft );
                        /* Age is in seconds, FILETIME resolution is in
                         * 100 nanosecond intervals.
                         */
                        ft.QuadPart += age * (ULONGLONG)1000000;
                        request->expires.dwLowDateTime = ft.u.LowPart;
                        request->expires.dwHighDateTime = ft.u.HighPart;
                        expirationFound = TRUE;
                    }
                }
            }
            if (comma)
            {
                ptr = comma + 1;
                while (isspaceW(*ptr))
                    ptr++;
            }
            else
                ptr = NULL;
        }
    }
    if (!expirationFound)
    {
        headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
        if (headerIndex != -1)
4502
        {
4503 4504 4505 4506 4507 4508 4509 4510
            LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
            FILETIME ft;

            if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
            {
                expirationFound = TRUE;
                request->expires = ft;
            }
4511 4512 4513 4514
        }
    }
    if (!expirationFound)
    {
4515
        LARGE_INTEGER t;
4516 4517

        /* With no known age, default to 10 minutes until expiration. */
4518 4519 4520 4521
        NtQuerySystemTime( &t );
        t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
        request->expires.dwLowDateTime = t.u.LowPart;
        request->expires.dwHighDateTime = t.u.HighPart;
4522 4523 4524
    }
}

4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539
static void HTTP_ProcessLastModified(http_request_t *request)
{
    int headerIndex;

    headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
    if (headerIndex != -1)
    {
        LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
        FILETIME ft;

        if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
            request->last_modified = ft;
    }
}

4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559
static void http_process_keep_alive(http_request_t *req)
{
    int index;

    index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
    if(index != -1)
        req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
    else
        req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
}

static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
{
    const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
    netconn_t *netconn = NULL;
    DWORD res;

    assert(!request->netconn);
    reset_data_stream(request);

4560 4561
    res = HTTP_ResolveName(request);
    if(res != ERROR_SUCCESS)
4562 4563 4564 4565
        return res;

    EnterCriticalSection(&connection_pool_cs);

4566 4567
    while(!list_empty(&request->server->conn_pool)) {
        netconn = LIST_ENTRY(list_head(&request->server->conn_pool), netconn_t, pool_entry);
4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586
        list_remove(&netconn->pool_entry);

        if(NETCON_is_alive(netconn))
            break;

        TRACE("connection %p closed during idle\n", netconn);
        free_netconn(netconn);
        netconn = NULL;
    }

    LeaveCriticalSection(&connection_pool_cs);

    if(netconn) {
        TRACE("<-- reusing %p netconn\n", netconn);
        request->netconn = netconn;
        *reusing = TRUE;
        return ERROR_SUCCESS;
    }

4587 4588 4589
    TRACE("connecting to %s, proxy %s\n", debugstr_w(request->server->name),
          request->proxy ? debugstr_w(request->proxy->name) : "(null)");

4590 4591
    INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
                          INTERNET_STATUS_CONNECTING_TO_SERVER,
4592 4593
                          request->server->addr_str,
                          strlen(request->server->addr_str)+1);
4594

4595
    res = create_netconn(is_https, request->proxy ? request->proxy : request->server, request->security_flags,
4596 4597
                         (request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0,
                         request->connect_timeout, &netconn);
4598 4599 4600 4601 4602 4603 4604 4605 4606
    if(res != ERROR_SUCCESS) {
        ERR("create_netconn failed: %u\n", res);
        return res;
    }

    request->netconn = netconn;

    INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
            INTERNET_STATUS_CONNECTED_TO_SERVER,
4607
            request->server->addr_str, strlen(request->server->addr_str)+1);
4608 4609 4610 4611 4612 4613 4614 4615

    if(is_https) {
        /* Note: we differ from Microsoft's WinINet here. they seem to have
         * a bug that causes no status callbacks to be sent when starting
         * a tunnel to a proxy server using the CONNECT verb. i believe our
         * behaviour to be more correct and to not cause any incompatibilities
         * because using a secure connection through a proxy server is a rare
         * case that would be hard for anyone to depend on */
4616
        if(request->proxy)
4617 4618
            res = HTTP_SecureProxyConnect(request);
        if(res == ERROR_SUCCESS)
4619
            res = NETCON_secure_connect(request->netconn, request->server);
4620 4621 4622 4623 4624 4625 4626 4627
    }

    if(res != ERROR_SUCCESS) {
        http_release_netconn(request, FALSE);
        return res;
    }

    *reusing = FALSE;
4628
    TRACE("Created connection to %s: %p\n", debugstr_w(request->server->name), netconn);
4629 4630 4631
    return ERROR_SUCCESS;
}

4632
/***********************************************************************
4633
 *           HTTP_HttpSendRequestW (internal)
4634 4635 4636 4637
 *
 * Sends the specified request to the HTTP server
 *
 * RETURNS
Juan Lang's avatar
Juan Lang committed
4638 4639
 *    ERROR_SUCCESS on success
 *    win32 error code on failure
4640 4641
 *
 */
4642
static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4643 4644
	DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
	DWORD dwContentLength, BOOL bEndRequest)
4645
{
4646
    INT cnt;
4647
    BOOL redirected = FALSE;
4648 4649 4650 4651 4652 4653 4654 4655
    LPWSTR requestString = NULL;
    INT responseLen;
    BOOL loop_next;
    static const WCHAR szPost[] = { 'P','O','S','T',0 };
    static const WCHAR szContentLength[] =
        { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
    WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
    DWORD res;
4656

4657
    TRACE("--> %p\n", request);
4658

4659
    assert(request->hdr.htype == WH_HHTTPREQ);
4660

4661
    /* if the verb is NULL default to GET */
4662 4663
    if (!request->verb)
        request->verb = heap_strdupW(szGET);
4664

4665
    if (dwContentLength || strcmpW(request->verb, szGET))
4666
    {
4667
        sprintfW(contentLengthStr, szContentLength, dwContentLength);
4668
        HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4669
        request->bytesToWrite = dwContentLength;
4670
    }
4671
    if (request->session->appInfo->agent)
4672
    {
4673 4674 4675
        WCHAR *agent_header;
        static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
        int len;
4676

4677
        len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4678
        agent_header = heap_alloc(len * sizeof(WCHAR));
4679
        sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4680

4681
        HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4682
        heap_free(agent_header);
4683
    }
4684
    if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4685
    {
4686
        static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4687
        HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4688
    }
4689
    if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4690
    {
4691 4692
        static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
                                              ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4693
        HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4694
    }
4695

4696 4697 4698 4699
    /* add the headers the caller supplied */
    if( lpszHeaders && dwHeaderLength )
        HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);

4700
    do
4701
    {
4702
        DWORD len;
4703
        BOOL reusing_connection;
4704
        char *ascii_req;
4705

4706
        loop_next = FALSE;
4707
        reusing_connection = request->netconn != NULL;
4708

4709
        if(redirected) {
4710 4711
            request->contentLength = ~0u;
            request->bytesToWrite = 0;
4712
        }
4713

4714
        if (TRACE_ON(wininet))
4715
        {
4716
            LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4717
            TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4718 4719
        }

4720 4721
        HTTP_FixURL(request);
        if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4722
        {
4723
            HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4724
        }
4725 4726
        HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
        HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4727

4728 4729
        if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
            HTTP_InsertCookies(request);
4730

4731
        if (request->proxy)
4732
        {
4733
            WCHAR *url = build_proxy_path_url(request);
4734
            requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4735
            heap_free(url);
4736 4737
        }
        else
4738
            requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4739

4740 4741
 
        TRACE("Request header -> %s\n", debugstr_w(requestString) );
4742

4743
        if (!reusing_connection && (res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
4744
            break;
4745

4746 4747 4748 4749 4750
        /* send the request as ASCII, tack on the optional data */
        if (!lpOptional || redirected)
            dwOptionalLength = 0;
        len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
                                   NULL, 0, NULL, NULL );
4751
        ascii_req = heap_alloc(len + dwOptionalLength);
4752 4753 4754 4755 4756 4757 4758
        WideCharToMultiByte( CP_ACP, 0, requestString, -1,
                             ascii_req, len, NULL, NULL );
        if( lpOptional )
            memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
        len = (len + dwOptionalLength - 1);
        ascii_req[len] = 0;
        TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4759

4760
        INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4761
                              INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4762

4763
        NETCON_set_timeout( request->netconn, TRUE, request->send_timeout );
4764
        res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
4765
        heap_free( ascii_req );
4766 4767 4768 4769 4770 4771 4772 4773
        if(res != ERROR_SUCCESS) {
            TRACE("send failed: %u\n", res);
            if(!reusing_connection)
                break;
            http_release_netconn(request, FALSE);
            loop_next = TRUE;
            continue;
        }
4774

4775
        request->bytesWritten = dwOptionalLength;
4776

4777
        INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4778 4779
                              INTERNET_STATUS_REQUEST_SENT,
                              &len, sizeof(DWORD));
4780

4781 4782 4783
        if (bEndRequest)
        {
            DWORD dwBufferSize;
4784

4785
            INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4786 4787
                                INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
    
4788
            responseLen = HTTP_GetResponseHeaders(request, TRUE);
4789 4790 4791 4792
            /* FIXME: We should know that connection is closed before sending
             * headers. Otherwise wrong callbacks are executed */
            if(!responseLen && reusing_connection) {
                TRACE("Connection closed by server, reconnecting\n");
4793
                http_release_netconn(request, FALSE);
4794 4795 4796
                loop_next = TRUE;
                continue;
            }
4797

4798
            INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4799 4800
                                INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
                                sizeof(DWORD));
4801

4802
            http_process_keep_alive(request);
4803
            HTTP_ProcessCookies(request);
4804
            HTTP_ProcessExpires(request);
4805
            HTTP_ProcessLastModified(request);
4806

4807
            res = set_content_length(request);
4808 4809 4810 4811 4812
            if(res != ERROR_SUCCESS)
                goto lend;
            if(!request->contentLength)
                http_release_netconn(request, TRUE);

4813
            if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4814
            {
4815
                WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4816
                dwBufferSize=sizeof(szNewLocation);
4817 4818 4819 4820 4821 4822 4823 4824
                switch(request->status_code) {
                case HTTP_STATUS_REDIRECT:
                case HTTP_STATUS_MOVED:
                case HTTP_STATUS_REDIRECT_KEEP_VERB:
                case HTTP_STATUS_REDIRECT_METHOD:
                    if(HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) != ERROR_SUCCESS)
                        break;

4825
                    if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
4826
                        request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
4827
                    {
4828
                        heap_free(request->verb);
4829
                        request->verb = heap_strdupW(szGET);
4830
                    }
4831
                    http_release_netconn(request, drain_content(request, FALSE));
4832
                    if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4833
                    {
4834
                        INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4835
                                              new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4836
                        res = HTTP_HandleRedirect(request, new_url);
4837
                        if (res == ERROR_SUCCESS)
4838
                        {
4839
                            heap_free(requestString);
4840 4841
                            loop_next = TRUE;
                        }
4842
                        heap_free( new_url );
4843
                    }
4844
                    redirected = TRUE;
4845 4846
                }
            }
4847
            if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4848 4849 4850
            {
                WCHAR szAuthValue[2048];
                dwBufferSize=2048;
4851
                if (request->status_code == HTTP_STATUS_DENIED)
4852
                {
4853
                    LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4854
                    DWORD dwIndex = 0;
4855
                    while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4856
                    {
4857
                        if (HTTP_DoAuthorization(request, szAuthValue,
4858 4859 4860
                                                 &request->authInfo,
                                                 request->session->userName,
                                                 request->session->password,
4861
                                                 Host->lpszValue))
4862
                        {
4863
                            heap_free(requestString);
4864 4865 4866 4867
                            if(!drain_content(request, TRUE)) {
                                FIXME("Could not drain content\n");
                                http_release_netconn(request, FALSE);
                            }
4868 4869 4870 4871
                            loop_next = TRUE;
                            break;
                        }
                    }
4872 4873 4874

                    if(!loop_next) {
                        TRACE("Cleaning wrong authorization data\n");
4875 4876
                        destroy_authinfo(request->authInfo);
                        request->authInfo = NULL;
4877
                    }
4878
                }
4879
                if (request->status_code == HTTP_STATUS_PROXY_AUTH_REQ)
4880 4881
                {
                    DWORD dwIndex = 0;
4882
                    while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4883
                    {
4884
                        if (HTTP_DoAuthorization(request, szAuthValue,
4885 4886 4887
                                                 &request->proxyAuthInfo,
                                                 request->session->appInfo->proxyUsername,
                                                 request->session->appInfo->proxyPassword,
4888
                                                 NULL))
4889
                        {
4890 4891 4892 4893
                            if(!drain_content(request, TRUE)) {
                                FIXME("Could not drain content\n");
                                http_release_netconn(request, FALSE);
                            }
4894 4895 4896 4897
                            loop_next = TRUE;
                            break;
                        }
                    }
4898 4899 4900

                    if(!loop_next) {
                        TRACE("Cleaning wrong proxy authorization data\n");
4901 4902
                        destroy_authinfo(request->proxyAuthInfo);
                        request->proxyAuthInfo = NULL;
4903
                    }
4904 4905
                }
            }
4906 4907
        }
        else
4908
            res = ERROR_SUCCESS;
4909 4910
    }
    while (loop_next);
4911

4912
lend:
4913
    heap_free(requestString);
4914

4915
    /* TODO: send notification for P3P header */
4916

4917 4918 4919
    if(res == ERROR_SUCCESS)
        create_cache_entry(request);

4920
    if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4921
    {
4922
        if (res == ERROR_SUCCESS) {
4923
            if(bEndRequest && request->contentLength && request->bytesWritten == request->bytesToWrite)
4924 4925 4926 4927 4928 4929 4930
                HTTP_ReceiveRequestData(request, TRUE);
            else
                send_request_complete(request,
                        request->session->hdr.dwInternalFlags & INET_OPENURL ? (DWORD_PTR)request->hdr.hInternet : 1, 0);
        }else {
                send_request_complete(request, 0, res);
        }
4931
    }
4932 4933

    TRACE("<--\n");
4934
    return res;
4935 4936
}

4937 4938 4939 4940 4941 4942 4943 4944
/***********************************************************************
 *
 * Helper functions for the HttpSendRequest(Ex) functions
 *
 */
static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
{
    struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4945
    http_request_t *request = (http_request_t*) workRequest->hdr;
4946

4947
    TRACE("%p\n", request);
4948

4949
    HTTP_HttpSendRequestW(request, req->lpszHeader,
4950 4951 4952
            req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
            req->dwContentLength, req->bEndRequest);

4953
    heap_free(req->lpszHeader);
4954 4955 4956
}


4957
static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
4958 4959
{
    DWORD dwBufferSize;
4960
    INT responseLen;
4961
    DWORD res = ERROR_SUCCESS;
4962

4963 4964 4965 4966 4967 4968
    if(!request->netconn) {
        WARN("Not connected\n");
        send_request_complete(request, 0, ERROR_INTERNET_OPERATION_CANCELLED);
        return ERROR_INTERNET_OPERATION_CANCELLED;
    }

4969
    INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4970 4971
                  INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);

4972
    responseLen = HTTP_GetResponseHeaders(request, TRUE);
4973 4974
    if (!responseLen)
        res = ERROR_HTTP_HEADER_NOT_FOUND;
4975

4976
    INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4977 4978 4979
                  INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));

    /* process cookies here. Is this right? */
4980
    http_process_keep_alive(request);
4981
    HTTP_ProcessCookies(request);
4982
    HTTP_ProcessExpires(request);
4983
    HTTP_ProcessLastModified(request);
4984

4985
    if ((res = set_content_length(request)) == ERROR_SUCCESS) {
4986
        if(!request->contentLength)
4987
            http_release_netconn(request, TRUE);
4988
    }
4989

4990
    if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
4991
    {
4992 4993 4994 4995 4996
        switch(request->status_code) {
        case HTTP_STATUS_REDIRECT:
        case HTTP_STATUS_MOVED:
        case HTTP_STATUS_REDIRECT_METHOD:
        case HTTP_STATUS_REDIRECT_KEEP_VERB: {
4997 4998
            WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
            dwBufferSize=sizeof(szNewLocation);
4999 5000 5001 5002 5003
            if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) != ERROR_SUCCESS)
                break;

            if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD) &&
                request->status_code != HTTP_STATUS_REDIRECT_KEEP_VERB)
5004
            {
5005 5006
                heap_free(request->verb);
                request->verb = heap_strdupW(szGET);
5007
            }
5008
            http_release_netconn(request, drain_content(request, FALSE));
5009 5010 5011 5012 5013 5014 5015 5016 5017 5018
            if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
            {
                INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
                                      new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
                res = HTTP_HandleRedirect(request, new_url);
                if (res == ERROR_SUCCESS)
                    res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
                heap_free( new_url );
            }
        }
5019 5020 5021
        }
    }

5022 5023 5024
    if(res == ERROR_SUCCESS)
        create_cache_entry(request);

5025
    if (res == ERROR_SUCCESS && request->contentLength)
5026
        HTTP_ReceiveRequestData(request, TRUE);
5027
    else
5028
        send_request_complete(request, res == ERROR_SUCCESS, res);
5029

5030
    return res;
5031 5032 5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 5049
}

/***********************************************************************
 *           HttpEndRequestA (WININET.@)
 *
 * Ends an HTTP request that was started by HttpSendRequestEx
 *
 * RETURNS
 *    TRUE	if successful
 *    FALSE	on failure
 *
 */
BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
        LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
{
    TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);

    if (lpBuffersOut)
    {
5050
        SetLastError(ERROR_INVALID_PARAMETER);
5051 5052 5053 5054 5055 5056 5057 5058 5059
        return FALSE;
    }

    return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
}

static void AsyncHttpEndRequestProc(WORKREQUEST *work)
{
    struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
5060
    http_request_t *request = (http_request_t*)work->hdr;
5061

5062
    TRACE("%p\n", request);
5063

5064
    HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079
}

/***********************************************************************
 *           HttpEndRequestW (WININET.@)
 *
 * Ends an HTTP request that was started by HttpSendRequestEx
 *
 * RETURNS
 *    TRUE	if successful
 *    FALSE	on failure
 *
 */
BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
        LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
{
5080
    http_request_t *request;
5081
    DWORD res;
5082

5083
    TRACE("%p %p %x %lx -->\n", hRequest, lpBuffersOut, dwFlags, dwContext);
5084 5085 5086

    if (lpBuffersOut)
    {
5087
        SetLastError(ERROR_INVALID_PARAMETER);
5088 5089 5090
        return FALSE;
    }

5091
    request = (http_request_t*) get_handle_object( hRequest );
5092

5093
    if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5094
    {
5095
        SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
5096 5097
        if (request)
            WININET_Release( &request->hdr );
5098 5099
        return FALSE;
    }
5100
    request->hdr.dwFlags |= dwFlags;
5101

5102
    if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
5103 5104
    {
        WORKREQUEST work;
5105
        struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
5106 5107

        work.asyncproc = AsyncHttpEndRequestProc;
5108
        work.hdr = WININET_AddRef( &request->hdr );
5109

5110 5111 5112
        work_endrequest = &work.u.HttpEndRequestW;
        work_endrequest->dwFlags = dwFlags;
        work_endrequest->dwContext = dwContext;
5113 5114

        INTERNET_AsyncCall(&work);
5115
        res = ERROR_IO_PENDING;
5116 5117
    }
    else
5118
        res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
5119

5120
    WININET_Release( &request->hdr );
5121 5122 5123 5124
    TRACE("%u <--\n", res);
    if(res != ERROR_SUCCESS)
        SetLastError(res);
    return res == ERROR_SUCCESS;
5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156
}

/***********************************************************************
 *           HttpSendRequestExA (WININET.@)
 *
 * Sends the specified request to the HTTP server and allows chunked
 * transfers.
 *
 * RETURNS
 *  Success: TRUE
 *  Failure: FALSE, call GetLastError() for more information.
 */
BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
			       LPINTERNET_BUFFERSA lpBuffersIn,
			       LPINTERNET_BUFFERSA lpBuffersOut,
			       DWORD dwFlags, DWORD_PTR dwContext)
{
    INTERNET_BUFFERSW BuffersInW;
    BOOL rc = FALSE;
    DWORD headerlen;
    LPWSTR header = NULL;

    TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
	    lpBuffersOut, dwFlags, dwContext);

    if (lpBuffersIn)
    {
        BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
        if (lpBuffersIn->lpcszHeader)
        {
            headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
                    lpBuffersIn->dwHeadersLength,0,0);
5157
            header = heap_alloc(headerlen*sizeof(WCHAR));
5158 5159
            if (!(BuffersInW.lpcszHeader = header))
            {
5160
                SetLastError(ERROR_OUTOFMEMORY);
5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177
                return FALSE;
            }
            BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
                    lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
                    header, headerlen);
        }
        else
            BuffersInW.lpcszHeader = NULL;
        BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
        BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
        BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
        BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
        BuffersInW.Next = NULL;
    }

    rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);

5178
    heap_free(header);
5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196
    return rc;
}

/***********************************************************************
 *           HttpSendRequestExW (WININET.@)
 *
 * Sends the specified request to the HTTP server and allows chunked
 * transfers
 *
 * RETURNS
 *  Success: TRUE
 *  Failure: FALSE, call GetLastError() for more information.
 */
BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
                   LPINTERNET_BUFFERSW lpBuffersIn,
                   LPINTERNET_BUFFERSW lpBuffersOut,
                   DWORD dwFlags, DWORD_PTR dwContext)
{
5197 5198
    http_request_t *request;
    http_session_t *session;
5199
    appinfo_t *hIC;
5200
    DWORD res;
5201 5202 5203 5204

    TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
            lpBuffersOut, dwFlags, dwContext);

5205
    request = (http_request_t*) get_handle_object( hRequest );
5206

5207
    if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5208
    {
5209
        res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
5210 5211 5212
        goto lend;
    }

5213
    session = request->session;
5214
    assert(session->hdr.htype == WH_HHTTPSESSION);
5215
    hIC = session->appInfo;
5216 5217 5218 5219 5220 5221 5222 5223
    assert(hIC->hdr.htype == WH_HINIT);

    if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
    {
        WORKREQUEST workRequest;
        struct WORKREQ_HTTPSENDREQUESTW *req;

        workRequest.asyncproc = AsyncHttpSendRequestProc;
5224
        workRequest.hdr = WININET_AddRef( &request->hdr );
5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236
        req = &workRequest.u.HttpSendRequestW;
        if (lpBuffersIn)
        {
            DWORD size = 0;

            if (lpBuffersIn->lpcszHeader)
            {
                if (lpBuffersIn->dwHeadersLength == ~0u)
                    size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
                else
                    size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);

5237
                req->lpszHeader = heap_alloc(size);
5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261
                memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
            }
            else req->lpszHeader = NULL;

            req->dwHeaderLength = size / sizeof(WCHAR);
            req->lpOptional = lpBuffersIn->lpvBuffer;
            req->dwOptionalLength = lpBuffersIn->dwBufferLength;
            req->dwContentLength = lpBuffersIn->dwBufferTotal;
        }
        else
        {
            req->lpszHeader = NULL;
            req->dwHeaderLength = 0;
            req->lpOptional = NULL;
            req->dwOptionalLength = 0;
            req->dwContentLength = 0;
        }

        req->bEndRequest = FALSE;

        INTERNET_AsyncCall(&workRequest);
        /*
         * This is from windows.
         */
5262
        res = ERROR_IO_PENDING;
5263 5264 5265 5266
    }
    else
    {
        if (lpBuffersIn)
5267
            res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
5268 5269 5270
                                        lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
                                        lpBuffersIn->dwBufferTotal, FALSE);
        else
5271
            res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
5272 5273 5274
    }

lend:
5275 5276
    if ( request )
        WININET_Release( &request->hdr );
5277 5278

    TRACE("<---\n");
5279
    SetLastError(res);
5280
    return res == ERROR_SUCCESS;
5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295
}

/***********************************************************************
 *           HttpSendRequestW (WININET.@)
 *
 * Sends the specified request to the HTTP server
 *
 * RETURNS
 *    TRUE  on success
 *    FALSE on failure
 *
 */
BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
	DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
{
5296 5297
    http_request_t *request;
    http_session_t *session = NULL;
5298 5299 5300 5301 5302 5303
    appinfo_t *hIC = NULL;
    DWORD res = ERROR_SUCCESS;

    TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
            debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);

5304 5305
    request = (http_request_t*) get_handle_object( hHttpRequest );
    if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
5306 5307 5308 5309 5310
    {
        res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
        goto lend;
    }

5311
    session = request->session;
5312
    if (NULL == session ||  session->hdr.htype != WH_HHTTPSESSION)
5313 5314 5315 5316 5317
    {
        res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
        goto lend;
    }

5318
    hIC = session->appInfo;
5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330
    if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT)
    {
        res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
        goto lend;
    }

    if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
    {
        WORKREQUEST workRequest;
        struct WORKREQ_HTTPSENDREQUESTW *req;

        workRequest.asyncproc = AsyncHttpSendRequestProc;
5331
        workRequest.hdr = WININET_AddRef( &request->hdr );
5332 5333 5334 5335 5336 5337 5338 5339
        req = &workRequest.u.HttpSendRequestW;
        if (lpszHeaders)
        {
            DWORD size;

            if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
            else size = dwHeaderLength * sizeof(WCHAR);

5340
            req->lpszHeader = heap_alloc(size);
5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358
            memcpy(req->lpszHeader, lpszHeaders, size);
        }
        else
            req->lpszHeader = 0;
        req->dwHeaderLength = dwHeaderLength;
        req->lpOptional = lpOptional;
        req->dwOptionalLength = dwOptionalLength;
        req->dwContentLength = dwOptionalLength;
        req->bEndRequest = TRUE;

        INTERNET_AsyncCall(&workRequest);
        /*
         * This is from windows.
         */
        res = ERROR_IO_PENDING;
    }
    else
    {
5359
	res = HTTP_HttpSendRequestW(request, lpszHeaders,
5360 5361 5362 5363
		dwHeaderLength, lpOptional, dwOptionalLength,
		dwOptionalLength, TRUE);
    }
lend:
5364 5365
    if( request )
        WININET_Release( &request->hdr );
5366

5367
    SetLastError(res);
5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 5388 5389
    return res == ERROR_SUCCESS;
}

/***********************************************************************
 *           HttpSendRequestA (WININET.@)
 *
 * Sends the specified request to the HTTP server
 *
 * RETURNS
 *    TRUE  on success
 *    FALSE on failure
 *
 */
BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
	DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
{
    BOOL result;
    LPWSTR szHeaders=NULL;
    DWORD nLen=dwHeaderLength;
    if(lpszHeaders!=NULL)
    {
        nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
5390
        szHeaders = heap_alloc(nLen*sizeof(WCHAR));
5391 5392
        MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
    }
5393 5394
    result = HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
    heap_free(szHeaders);
5395 5396 5397
    return result;
}

5398 5399 5400 5401 5402 5403
/***********************************************************************
 *           HTTPSESSION_Destroy (internal)
 *
 * Deallocate session handle
 *
 */
5404
static void HTTPSESSION_Destroy(object_header_t *hdr)
5405
{
5406
    http_session_t *session = (http_session_t*) hdr;
5407

5408
    TRACE("%p\n", session);
5409

5410
    WININET_Release(&session->appInfo->hdr);
5411

5412 5413 5414
    heap_free(session->hostName);
    heap_free(session->password);
    heap_free(session->userName);
5415 5416
}

5417
static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
5418
{
5419 5420
    http_session_t *ses = (http_session_t *)hdr;

5421 5422 5423 5424 5425 5426 5427 5428 5429 5430
    switch(option) {
    case INTERNET_OPTION_HANDLE_TYPE:
        TRACE("INTERNET_OPTION_HANDLE_TYPE\n");

        if (*size < sizeof(ULONG))
            return ERROR_INSUFFICIENT_BUFFER;

        *size = sizeof(DWORD);
        *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
        return ERROR_SUCCESS;
5431 5432 5433 5434 5435 5436 5437 5438 5439
    case INTERNET_OPTION_CONNECT_TIMEOUT:
        TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");

        if (*size < sizeof(DWORD))
            return ERROR_INSUFFICIENT_BUFFER;

        *size = sizeof(DWORD);
        *(DWORD *)buffer = ses->connect_timeout;
        return ERROR_SUCCESS;
5440 5441 5442 5443 5444 5445 5446 5447 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457 5458 5459

    case INTERNET_OPTION_SEND_TIMEOUT:
        TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");

        if (*size < sizeof(DWORD))
            return ERROR_INSUFFICIENT_BUFFER;

        *size = sizeof(DWORD);
        *(DWORD *)buffer = ses->send_timeout;
        return ERROR_SUCCESS;

    case INTERNET_OPTION_RECEIVE_TIMEOUT:
        TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");

        if (*size < sizeof(DWORD))
            return ERROR_INSUFFICIENT_BUFFER;

        *size = sizeof(DWORD);
        *(DWORD *)buffer = ses->receive_timeout;
        return ERROR_SUCCESS;
5460 5461
    }

5462
    return INET_QueryOption(hdr, option, buffer, size, unicode);
5463
}
5464

5465
static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5466
{
5467
    http_session_t *ses = (http_session_t*)hdr;
5468 5469 5470 5471

    switch(option) {
    case INTERNET_OPTION_USERNAME:
    {
5472
        heap_free(ses->userName);
5473
        if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5474 5475 5476 5477
        return ERROR_SUCCESS;
    }
    case INTERNET_OPTION_PASSWORD:
    {
5478
        heap_free(ses->password);
5479
        if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5480 5481
        return ERROR_SUCCESS;
    }
5482 5483
    case INTERNET_OPTION_CONNECT_TIMEOUT:
    {
5484
        if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
5485 5486 5487
        ses->connect_timeout = *(DWORD *)buffer;
        return ERROR_SUCCESS;
    }
5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499
    case INTERNET_OPTION_SEND_TIMEOUT:
    {
        if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
        ses->send_timeout = *(DWORD *)buffer;
        return ERROR_SUCCESS;
    }
    case INTERNET_OPTION_RECEIVE_TIMEOUT:
    {
        if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
        ses->receive_timeout = *(DWORD *)buffer;
        return ERROR_SUCCESS;
    }
5500 5501 5502
    default: break;
    }

5503
    return INET_SetOption(hdr, option, buffer, size);
5504 5505
}

5506
static const object_vtbl_t HTTPSESSIONVtbl = {
5507
    HTTPSESSION_Destroy,
5508
    NULL,
5509
    HTTPSESSION_QueryOption,
5510
    HTTPSESSION_SetOption,
5511
    NULL,
5512
    NULL,
5513
    NULL,
5514
    NULL,
5515
    NULL
5516 5517 5518
};


5519 5520 5521 5522 5523 5524 5525 5526 5527 5528
/***********************************************************************
 *           HTTP_Connect  (internal)
 *
 * Create http session handle
 *
 * RETURNS
 *   HINTERNET a session handle on success
 *   NULL on failure
 *
 */
5529
DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5530
        INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5531 5532
        LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
        DWORD dwInternalFlags, HINTERNET *ret)
5533
{
5534
    http_session_t *session = NULL;
5535

5536
    TRACE("-->\n");
5537

5538
    if (!lpszServerName || !lpszServerName[0])
5539
        return ERROR_INVALID_PARAMETER;
5540

5541
    assert( hIC->hdr.htype == WH_HINIT );
5542

5543 5544
    session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
    if (!session)
5545
        return ERROR_OUTOFMEMORY;
5546

5547 5548 5549
   /*
    * According to my tests. The name is not resolved until a request is sent
    */
5550

5551 5552 5553 5554
    session->hdr.htype = WH_HHTTPSESSION;
    session->hdr.dwFlags = dwFlags;
    session->hdr.dwContext = dwContext;
    session->hdr.dwInternalFlags |= dwInternalFlags;
5555

5556
    WININET_AddRef( &hIC->hdr );
5557
    session->appInfo = hIC;
5558
    list_add_head( &hIC->hdr.children, &session->hdr.entry );
5559

5560 5561
    if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
        if(hIC->proxyBypass)
5562 5563
            FIXME("Proxy bypass is ignored.\n");
    }
5564
    session->hostName = heap_strdupW(lpszServerName);
5565
    if (lpszUserName && lpszUserName[0])
5566
        session->userName = heap_strdupW(lpszUserName);
5567
    if (lpszPassword && lpszPassword[0])
5568 5569
        session->password = heap_strdupW(lpszPassword);
    session->hostPort = serverPort;
5570
    session->connect_timeout = hIC->connect_timeout;
5571 5572
    session->send_timeout = INFINITE;
    session->receive_timeout = INFINITE;
5573

5574
    /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5575
    if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5576
    {
5577
        INTERNET_SendCallback(&hIC->hdr, dwContext,
5578
                              INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5579
                              sizeof(HINTERNET));
5580 5581
    }

5582
/*
5583
 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5584 5585
 * windows
 */
5586

5587
    TRACE("%p --> %p\n", hIC, session);
5588

5589
    *ret = session->hdr.hInternet;
5590
    return ERROR_SUCCESS;
5591 5592
}

5593 5594 5595 5596 5597
/***********************************************************************
 *           HTTP_clear_response_headers (internal)
 *
 * clear out any old response headers
 */
5598
static void HTTP_clear_response_headers( http_request_t *request )
5599 5600 5601
{
    DWORD i;

5602
    for( i=0; i<request->nCustHeaders; i++)
5603
    {
5604
        if( !request->custHeaders[i].lpszField )
5605
            continue;
5606
        if( !request->custHeaders[i].lpszValue )
5607
            continue;
5608
        if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5609
            continue;
5610
        HTTP_DeleteCustomHeader( request, i );
5611
        i--;
5612 5613 5614
    }
}

5615 5616 5617 5618 5619 5620 5621 5622 5623 5624
/***********************************************************************
 *           HTTP_GetResponseHeaders (internal)
 *
 * Read server response
 *
 * RETURNS
 *
 *   TRUE  on success
 *   FALSE on error
 */
5625
static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5626 5627
{
    INT cbreaks = 0;
5628
    WCHAR buffer[MAX_REPLY_LEN];
5629 5630
    DWORD buflen = MAX_REPLY_LEN;
    BOOL bSuccess = FALSE;
5631
    INT  rc = 0;
5632
    char bufferA[MAX_REPLY_LEN];
5633
    LPWSTR status_code = NULL, status_text = NULL;
5634
    DWORD cchMaxRawHeaders = 1024;
5635
    LPWSTR lpszRawHeaders = NULL;
5636
    LPWSTR temp;
5637
    DWORD cchRawHeaders = 0;
5638
    BOOL codeHundred = FALSE;
5639

5640
    TRACE("-->\n");
5641

5642
    if(!request->netconn)
5643 5644
        goto lend;

5645
    NETCON_set_timeout( request->netconn, FALSE, request->receive_timeout );
5646
    do {
5647
        static const WCHAR szHundred[] = {'1','0','0',0};
5648 5649 5650
        /*
         * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
         */
5651
        buflen = MAX_REPLY_LEN;
5652
        if (!read_line(request, bufferA, &buflen))
5653
            goto lend;
5654 5655 5656

        /* clear old response headers (eg. from a redirect response) */
        if (clear) {
5657
            HTTP_clear_response_headers( request );
5658 5659 5660
            clear = FALSE;
        }

5661
        rc += buflen;
5662
        MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5663 5664 5665 5666 5667 5668 5669 5670
        /* check is this a status code line? */
        if (!strncmpW(buffer, g_szHttp1_0, 4))
        {
            /* split the version from the status code */
            status_code = strchrW( buffer, ' ' );
            if( !status_code )
                goto lend;
            *status_code++=0;
5671

5672 5673 5674 5675 5676
            /* split the status code from the status text */
            status_text = strchrW( status_code, ' ' );
            if( !status_text )
                goto lend;
            *status_text++=0;
5677

5678 5679
            request->status_code = atoiW(status_code);

5680 5681
            TRACE("version [%s] status code [%s] status text [%s]\n",
               debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5682

5683 5684 5685 5686
            codeHundred = (!strcmpW(status_code, szHundred));
        }
        else if (!codeHundred)
        {
5687 5688
            WARN("No status line at head of response (%s)\n", debugstr_w(buffer));

5689 5690
            heap_free(request->version);
            heap_free(request->statusText);
5691

5692
            request->status_code = HTTP_STATUS_OK;
5693 5694
            request->version = heap_strdupW(g_szHttp1_0);
            request->statusText = heap_strdupW(szOK);
5695

5696
            heap_free(request->rawHeaders);
5697
            request->rawHeaders = heap_strdupW(szDefaultHeader);
5698 5699

            bSuccess = TRUE;
5700
            goto lend;
5701 5702
        }
    } while (codeHundred);
5703

5704
    /* Add status code */
5705
    HTTP_ProcessHeader(request, szStatus, status_code,
5706 5707
            HTTP_ADDHDR_FLAG_REPLACE);

5708 5709
    heap_free(request->version);
    heap_free(request->statusText);
5710

5711 5712
    request->version = heap_strdupW(buffer);
    request->statusText = heap_strdupW(status_text);
5713 5714 5715 5716 5717

    /* Restore the spaces */
    *(status_code-1) = ' ';
    *(status_text-1) = ' ';

5718
    /* regenerate raw headers */
5719
    lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
5720 5721
    if (!lpszRawHeaders) goto lend;

5722 5723
    while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
        cchMaxRawHeaders *= 2;
5724
    temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5725 5726
    if (temp == NULL) goto lend;
    lpszRawHeaders = temp;
5727 5728 5729 5730 5731 5732
    memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
    cchRawHeaders += (buflen-1);
    memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
    cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
    lpszRawHeaders[cchRawHeaders] = '\0';

5733 5734 5735 5736
    /* Parse each response line */
    do
    {
	buflen = MAX_REPLY_LEN;
5737
        if (read_line(request, bufferA, &buflen))
5738 5739 5740
        {
            LPWSTR * pFieldAndValue;

5741
            TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5742 5743

            if (!bufferA[0]) break;
5744
            MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5745

5746
            pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5747 5748 5749 5750
            if (pFieldAndValue)
            {
                while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
                    cchMaxRawHeaders *= 2;
5751
                temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5752 5753 5754 5755 5756 5757 5758 5759
                if (temp == NULL) goto lend;
                lpszRawHeaders = temp;
                memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
                cchRawHeaders += (buflen-1);
                memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
                cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
                lpszRawHeaders[cchRawHeaders] = '\0';

5760
                HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5761 5762 5763 5764 5765
                                   HTTP_ADDREQ_FLAG_ADD );

                HTTP_FreeTokens(pFieldAndValue);
            }
        }
5766 5767 5768 5769 5770 5771 5772 5773
	else
	{
	    cbreaks++;
	    if (cbreaks >= 2)
	       break;
	}
    }while(1);

5774 5775 5776 5777 5778 5779
    /* make sure the response header is terminated with an empty line.  Some apps really
       truly care about that empty line being there for some reason.  Just add it to the
       header. */
    if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
    {
        cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5780
        temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5781 5782 5783 5784 5785 5786
        if (temp == NULL) goto lend;
        lpszRawHeaders = temp;
    }

    memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));

5787
    heap_free(request->rawHeaders);
5788
    request->rawHeaders = lpszRawHeaders;
5789
    TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5790 5791 5792 5793
    bSuccess = TRUE;

lend:

5794 5795 5796 5797
    TRACE("<--\n");
    if (bSuccess)
        return rc;
    else
5798
    {
5799
        heap_free(lpszRawHeaders);
5800
        return 0;
5801
    }
5802 5803 5804 5805 5806 5807 5808 5809 5810
}

/***********************************************************************
 *           HTTP_InterpretHttpHeader (internal)
 *
 * Parse server response
 *
 * RETURNS
 *
5811 5812
 *   Pointer to array of field, value, NULL on success.
 *   NULL on error.
5813
 */
Jacek Caban's avatar
Jacek Caban committed
5814
static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5815
{
5816 5817 5818
    LPWSTR * pTokenPair;
    LPWSTR pszColon;
    INT len;
5819

5820
    pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
5821

5822 5823 5824
    pszColon = strchrW(buffer, ':');
    /* must have two tokens */
    if (!pszColon)
5825
    {
5826
        HTTP_FreeTokens(pTokenPair);
5827 5828
        if (buffer[0])
            TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5829
        return NULL;
5830 5831
    }

5832
    pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
5833 5834 5835 5836 5837 5838 5839
    if (!pTokenPair[0])
    {
        HTTP_FreeTokens(pTokenPair);
        return NULL;
    }
    memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
    pTokenPair[0][pszColon - buffer] = '\0';
5840

5841 5842 5843
    /* skip colon */
    pszColon++;
    len = strlenW(pszColon);
5844
    pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
5845
    if (!pTokenPair[1])
5846
    {
5847 5848
        HTTP_FreeTokens(pTokenPair);
        return NULL;
5849
    }
5850
    memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5851

5852 5853 5854 5855 5856
    strip_spaces(pTokenPair[0]);
    strip_spaces(pTokenPair[1]);

    TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
    return pTokenPair;
5857 5858 5859 5860 5861 5862 5863 5864 5865
}

/***********************************************************************
 *           HTTP_ProcessHeader (internal)
 *
 * Stuff header into header tables according to <dwModifier>
 *
 */

Juan Lang's avatar
Juan Lang committed
5866
#define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5867

5868
static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5869
{
5870
    LPHTTPHEADERW lphttpHdr = NULL;
5871 5872
    INT index = -1;
    BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5873
    DWORD res = ERROR_HTTP_INVALID_HEADER;
5874

5875
    TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5876

5877 5878 5879 5880 5881 5882 5883
    /* REPLACE wins out over ADD */
    if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
        dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
    
    if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
        index = -1;
    else
5884
        index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5885 5886

    if (index >= 0)
5887
    {
5888
        if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5889
            return ERROR_HTTP_INVALID_HEADER;
5890
        lphttpHdr = &request->custHeaders[index];
5891 5892 5893 5894
    }
    else if (value)
    {
        HTTPHEADERW hdr;
5895

5896 5897 5898
        hdr.lpszField = (LPWSTR)field;
        hdr.lpszValue = (LPWSTR)value;
        hdr.wFlags = hdr.wCount = 0;
5899

5900 5901
        if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
            hdr.wFlags |= HDR_ISREQUEST;
5902

5903
        return HTTP_InsertCustomHeader(request, &hdr);
5904
    }
5905
    /* no value to delete */
5906
    else return ERROR_SUCCESS;
5907

5908
    if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5909
	    lphttpHdr->wFlags |= HDR_ISREQUEST;
5910 5911
    else
        lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5912

5913
    if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5914
    {
5915
        HTTP_DeleteCustomHeader( request, index );
5916

5917
        if (value)
5918
        {
5919 5920 5921 5922 5923
            HTTPHEADERW hdr;

            hdr.lpszField = (LPWSTR)field;
            hdr.lpszValue = (LPWSTR)value;
            hdr.wFlags = hdr.wCount = 0;
5924 5925

            if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5926 5927
                hdr.wFlags |= HDR_ISREQUEST;

5928
            return HTTP_InsertCustomHeader(request, &hdr);
5929 5930
        }

5931
        return ERROR_SUCCESS;
5932
    }
Juan Lang's avatar
Juan Lang committed
5933
    else if (dwModifier & COALESCEFLAGS)
5934 5935 5936 5937 5938 5939 5940 5941
    {
        LPWSTR lpsztmp;
        WCHAR ch = 0;
        INT len = 0;
        INT origlen = strlenW(lphttpHdr->lpszValue);
        INT valuelen = strlenW(value);

        if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5942
        {
5943 5944
            ch = ',';
            lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5945
        }
5946
        else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5947
        {
5948 5949
            ch = ';';
            lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5950 5951
        }

5952
        len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5953

5954
        lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5955 5956 5957 5958 5959
        if (lpsztmp)
        {
            lphttpHdr->lpszValue = lpsztmp;
    /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
            if (ch > 0)
5960
            {
5961 5962 5963 5964
                lphttpHdr->lpszValue[origlen] = ch;
                origlen++;
                lphttpHdr->lpszValue[origlen] = ' ';
                origlen++;
5965
            }
5966 5967 5968

            memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
            lphttpHdr->lpszValue[len] = '\0';
5969
            res = ERROR_SUCCESS;
5970 5971 5972
        }
        else
        {
5973
            WARN("heap_realloc (%d bytes) failed\n",len+1);
5974
            res = ERROR_OUTOFMEMORY;
5975 5976
        }
    }
5977 5978
    TRACE("<-- %d\n", res);
    return res;
5979 5980 5981 5982 5983 5984 5985 5986
}

/***********************************************************************
 *           HTTP_GetCustomHeaderIndex (internal)
 *
 * Return index of custom header from header array
 *
 */
5987
static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
5988
                                     int requested_index, BOOL request_only)
5989
{
5990
    DWORD index;
5991

5992
    TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5993

5994
    for (index = 0; index < request->nCustHeaders; index++)
5995
    {
5996
        if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
5997 5998
            continue;

5999
        if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
6000 6001
            continue;

6002
        if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
6003
            continue;
6004

6005
        if (requested_index == 0)
6006 6007
            break;
        requested_index --;
6008 6009
    }

6010
    if (index >= request->nCustHeaders)
6011 6012
	index = -1;

6013
    TRACE("Return: %d\n", index);
6014 6015 6016 6017 6018 6019 6020 6021 6022 6023
    return index;
}


/***********************************************************************
 *           HTTP_InsertCustomHeader (internal)
 *
 * Insert header into array
 *
 */
6024
static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
6025 6026
{
    INT count;
6027
    LPHTTPHEADERW lph = NULL;
6028

6029
    TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
6030
    count = request->nCustHeaders + 1;
6031
    if (count > 1)
6032
	lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
6033
    else
6034
	lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
6035

6036 6037
    if (!lph)
        return ERROR_OUTOFMEMORY;
6038

6039 6040 6041 6042 6043
    request->custHeaders = lph;
    request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
    request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
    request->custHeaders[count-1].wFlags = lpHdr->wFlags;
    request->custHeaders[count-1].wCount= lpHdr->wCount;
6044
    request->nCustHeaders++;
6045 6046

    return ERROR_SUCCESS;
6047 6048 6049 6050 6051 6052 6053
}


/***********************************************************************
 *           HTTP_DeleteCustomHeader (internal)
 *
 * Delete header from array
6054
 *  If this function is called, the indexs may change.
6055
 */
6056
static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
6057
{
6058
    if( request->nCustHeaders <= 0 )
6059
        return FALSE;
6060
    if( index >= request->nCustHeaders )
6061
        return FALSE;
6062
    request->nCustHeaders--;
6063

6064 6065
    heap_free(request->custHeaders[index].lpszField);
    heap_free(request->custHeaders[index].lpszValue);
6066

6067
    memmove( &request->custHeaders[index], &request->custHeaders[index+1],
6068
             (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
6069
    memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
6070 6071

    return TRUE;
6072
}
6073

6074 6075 6076 6077 6078 6079 6080

/***********************************************************************
 *           HTTP_VerifyValidHeader (internal)
 *
 * Verify the given header is not invalid for the given http request
 *
 */
6081
static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
6082 6083
{
    /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6084
    if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
6085
        return ERROR_HTTP_INVALID_HEADER;
6086

6087
    return ERROR_SUCCESS;
6088 6089
}

6090 6091 6092 6093 6094 6095 6096 6097
/***********************************************************************
 *          IsHostInProxyBypassList (@)
 *
 * Undocumented
 *
 */
BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
{
6098
   FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
6099 6100
   return FALSE;
}
6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118

/***********************************************************************
 *           InternetShowSecurityInfoByURLA (@)
 */
BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
{
   FIXME("stub: %s %p\n", url, window);
   return FALSE;
}

/***********************************************************************
 *           InternetShowSecurityInfoByURLW (@)
 */
BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
{
   FIXME("stub: %s %p\n", debugstr_w(url), window);
   return FALSE;
}
6119 6120 6121 6122 6123 6124

/***********************************************************************
 *           ShowX509EncodedCertificate (@)
 */
DWORD WINAPI ShowX509EncodedCertificate(HWND parent, LPBYTE cert, DWORD len)
{
6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144
    PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING,
        cert, len);
    DWORD ret;

    if (certContext)
    {
        CRYPTUI_VIEWCERTIFICATE_STRUCTW view;

        memset(&view, 0, sizeof(view));
        view.hwndParent = parent;
        view.pCertContext = certContext;
        if (CryptUIDlgViewCertificateW(&view, NULL))
            ret = ERROR_SUCCESS;
        else
            ret = GetLastError();
        CertFreeCertificateContext(certContext);
    }
    else
        ret = GetLastError();
    return ret;
6145
}