ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

http.c
Go to the documentation of this file.
00001 /*
00002  * Wininet - Http Implementation
00003  *
00004  * Copyright 1999 Corel Corporation
00005  * Copyright 2002 CodeWeavers Inc.
00006  * Copyright 2002 TransGaming Technologies Inc.
00007  * Copyright 2004 Mike McCormack for CodeWeavers
00008  * Copyright 2005 Aric Stewart for CodeWeavers
00009  * Copyright 2006 Robert Shearman for CodeWeavers
00010  * Copyright 2011 Jacek Caban for CodeWeavers
00011  *
00012  * Ulrich Czekalla
00013  * David Hammerton
00014  *
00015  * This library is free software; you can redistribute it and/or
00016  * modify it under the terms of the GNU Lesser General Public
00017  * License as published by the Free Software Foundation; either
00018  * version 2.1 of the License, or (at your option) any later version.
00019  *
00020  * This library is distributed in the hope that it will be useful,
00021  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00022  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00023  * Lesser General Public License for more details.
00024  *
00025  * You should have received a copy of the GNU Lesser General Public
00026  * License along with this library; if not, write to the Free Software
00027  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00028  */
00029 
00030 #include "config.h"
00031 #include "wine/port.h"
00032 
00033 #if defined(__MINGW32__) || defined (_MSC_VER)
00034 #include <ws2tcpip.h>
00035 #endif
00036 
00037 #include <sys/types.h>
00038 #ifdef HAVE_SYS_SOCKET_H
00039 # include <sys/socket.h>
00040 #endif
00041 #ifdef HAVE_ARPA_INET_H
00042 # include <arpa/inet.h>
00043 #endif
00044 #include <stdarg.h>
00045 #include <stdio.h>
00046 #include <stdlib.h>
00047 #ifdef HAVE_UNISTD_H
00048 # include <unistd.h>
00049 #endif
00050 #include <time.h>
00051 #include <assert.h>
00052 #ifdef HAVE_ZLIB
00053 #  include <zlib.h>
00054 #endif
00055 
00056 #include "windef.h"
00057 #include "winbase.h"
00058 #include "wininet.h"
00059 #include "winerror.h"
00060 #include "winternl.h"
00061 #define NO_SHLWAPI_STREAM
00062 #define NO_SHLWAPI_REG
00063 #define NO_SHLWAPI_STRFCNS
00064 #define NO_SHLWAPI_GDI
00065 #include "shlwapi.h"
00066 #include "sspi.h"
00067 #include "wincrypt.h"
00068 
00069 #include "internet.h"
00070 #include "wine/debug.h"
00071 #include "wine/exception.h"
00072 #include "wine/unicode.h"
00073 
00074 #include "inet_ntop.c"
00075 
00076 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
00077 
00078 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
00079 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
00080 static const WCHAR szOK[] = {'O','K',0};
00081 static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
00082 static const WCHAR hostW[] = { 'H','o','s','t',0 };
00083 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
00084 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
00085 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
00086 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
00087 static const WCHAR szGET[] = { 'G','E','T', 0 };
00088 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
00089 static const WCHAR szCrLf[] = {'\r','\n', 0};
00090 
00091 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
00092 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
00093 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
00094 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
00095 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
00096 static const WCHAR szAge[] = { 'A','g','e',0 };
00097 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
00098 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
00099 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
00100 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
00101 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
00102 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
00103 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
00104 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
00105 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
00106 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
00107 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
00108 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 };
00109 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
00110 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
00111 static const WCHAR szDate[] = { 'D','a','t','e',0 };
00112 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
00113 static const WCHAR szETag[] = { 'E','T','a','g',0 };
00114 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
00115 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
00116 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
00117 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
00118 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
00119 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
00120 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
00121 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
00122 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
00123 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
00124 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
00125 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
00126 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
00127 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
00128 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
00129 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
00130 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
00131 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
00132 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
00133 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
00134 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
00135 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 };
00136 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
00137 static const WCHAR szURI[] = { 'U','R','I',0 };
00138 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
00139 static const WCHAR szVary[] = { 'V','a','r','y',0 };
00140 static const WCHAR szVia[] = { 'V','i','a',0 };
00141 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
00142 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
00143 
00144 #define MAXHOSTNAME 100
00145 #define MAX_FIELD_VALUE_LEN 256
00146 #define MAX_FIELD_LEN 256
00147 
00148 #define HTTP_REFERER    szReferer
00149 #define HTTP_ACCEPT     szAccept
00150 #define HTTP_USERAGENT  szUser_Agent
00151 
00152 #define HTTP_ADDHDR_FLAG_ADD                0x20000000
00153 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW         0x10000000
00154 #define HTTP_ADDHDR_FLAG_COALESCE           0x40000000
00155 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA        0x40000000
00156 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON    0x01000000
00157 #define HTTP_ADDHDR_FLAG_REPLACE            0x80000000
00158 #define HTTP_ADDHDR_FLAG_REQ                0x02000000
00159 
00160 #define COLLECT_TIME 60000
00161 
00162 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
00163 
00164 struct HttpAuthInfo
00165 {
00166     LPWSTR scheme;
00167     CredHandle cred;
00168     CtxtHandle ctx;
00169     TimeStamp exp;
00170     ULONG attr;
00171     ULONG max_token;
00172     void *auth_data;
00173     unsigned int auth_data_len;
00174     BOOL finished; /* finished authenticating */
00175 };
00176 
00177 
00178 typedef struct _basicAuthorizationData
00179 {
00180     struct list entry;
00181 
00182     LPWSTR host;
00183     LPWSTR realm;
00184     LPSTR  authorization;
00185     UINT   authorizationLen;
00186 } basicAuthorizationData;
00187 
00188 typedef struct _authorizationData
00189 {
00190     struct list entry;
00191 
00192     LPWSTR host;
00193     LPWSTR scheme;
00194     LPWSTR domain;
00195     UINT   domain_len;
00196     LPWSTR user;
00197     UINT   user_len;
00198     LPWSTR password;
00199     UINT   password_len;
00200 } authorizationData;
00201 
00202 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
00203 static struct list authorizationCache = LIST_INIT(authorizationCache);
00204 
00205 static CRITICAL_SECTION authcache_cs;
00206 static CRITICAL_SECTION_DEBUG critsect_debug =
00207 {
00208     0, 0, &authcache_cs,
00209     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
00210       0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
00211 };
00212 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
00213 
00214 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
00215 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
00216 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
00217 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
00218 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
00219 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
00220 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
00221 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
00222 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
00223 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
00224 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
00225 
00226 static CRITICAL_SECTION connection_pool_cs;
00227 static CRITICAL_SECTION_DEBUG connection_pool_debug =
00228 {
00229     0, 0, &connection_pool_cs,
00230     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
00231       0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
00232 };
00233 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
00234 
00235 static struct list connection_pool = LIST_INIT(connection_pool);
00236 static BOOL collector_running;
00237 
00238 void server_addref(server_t *server)
00239 {
00240     InterlockedIncrement(&server->ref);
00241 }
00242 
00243 void server_release(server_t *server)
00244 {
00245     if(InterlockedDecrement(&server->ref))
00246         return;
00247 
00248 #ifndef __REACTOS__
00249     if(!server->ref)
00250         server->keep_until = (DWORD64)GetTickCount() + COLLECT_TIME;
00251 #else
00252     EnterCriticalSection(&connection_pool_cs);
00253     list_remove(&server->entry);
00254     LeaveCriticalSection(&connection_pool_cs);
00255     
00256     heap_free(server->name);
00257     heap_free(server);
00258 #endif
00259 }
00260 
00261 static server_t *get_server(const WCHAR *name, INTERNET_PORT port)
00262 {
00263     server_t *iter, *server = NULL;
00264 
00265     EnterCriticalSection(&connection_pool_cs);
00266 
00267     LIST_FOR_EACH_ENTRY(iter, &connection_pool, server_t, entry) {
00268         if(iter->port == port && !strcmpW(iter->name, name)) {
00269             server = iter;
00270             server_addref(server);
00271             break;
00272         }
00273     }
00274 
00275     if(!server) {
00276         server = heap_alloc(sizeof(*server));
00277         if(server) {
00278             server->addr_len = 0;
00279             server->ref = 1;
00280             server->port = port;
00281             list_init(&server->conn_pool);
00282             server->name = heap_strdupW(name);
00283             if(server->name) {
00284                 list_add_head(&connection_pool, &server->entry);
00285             }else {
00286                 heap_free(server);
00287                 server = NULL;
00288             }
00289         }
00290     }
00291 
00292     LeaveCriticalSection(&connection_pool_cs);
00293 
00294     return server;
00295 }
00296 
00297 BOOL collect_connections(BOOL collect_all)
00298 {
00299     netconn_t *netconn, *netconn_safe;
00300     server_t *server, *server_safe;
00301     BOOL remaining = FALSE;
00302     DWORD64 now;
00303 
00304     now = GetTickCount();
00305 
00306     LIST_FOR_EACH_ENTRY_SAFE(server, server_safe, &connection_pool, server_t, entry) {
00307         LIST_FOR_EACH_ENTRY_SAFE(netconn, netconn_safe, &server->conn_pool, netconn_t, pool_entry) {
00308             if(collect_all || netconn->keep_until < now) {
00309                 TRACE("freeing %p\n", netconn);
00310                 list_remove(&netconn->pool_entry);
00311                 free_netconn(netconn);
00312             }else {
00313                 remaining = TRUE;
00314             }
00315         }
00316 
00317         if(!server->ref) {
00318             if(collect_all || server->keep_until < now) {
00319                 list_remove(&server->entry);
00320 
00321                 heap_free(server->name);
00322                 heap_free(server);
00323             }else {
00324                 remaining = TRUE;
00325             }
00326         }
00327     }
00328 
00329     return remaining;
00330 }
00331 
00332 static DWORD WINAPI collect_connections_proc(void *arg)
00333 {
00334     BOOL remaining_conns;
00335 
00336     do {
00337         /* FIXME: Use more sophisticated method */
00338         Sleep(5000);
00339 
00340         EnterCriticalSection(&connection_pool_cs);
00341 
00342         remaining_conns = collect_connections(FALSE);
00343         if(!remaining_conns)
00344             collector_running = FALSE;
00345 
00346         LeaveCriticalSection(&connection_pool_cs);
00347     }while(remaining_conns);
00348 
00349     FreeLibraryAndExitThread(WININET_hModule, 0);
00350 }
00351 
00352 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
00353 {
00354     int HeaderIndex = 0;
00355     HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
00356     if (HeaderIndex == -1)
00357         return NULL;
00358     else
00359         return &req->custHeaders[HeaderIndex];
00360 }
00361 
00362 typedef enum {
00363     READMODE_SYNC,
00364     READMODE_ASYNC,
00365     READMODE_NOBLOCK
00366 } read_mode_t;
00367 
00368 struct data_stream_vtbl_t {
00369     DWORD (*get_avail_data)(data_stream_t*,http_request_t*);
00370     BOOL (*end_of_data)(data_stream_t*,http_request_t*);
00371     DWORD (*read)(data_stream_t*,http_request_t*,BYTE*,DWORD,DWORD*,read_mode_t);
00372     BOOL (*drain_content)(data_stream_t*,http_request_t*);
00373     void (*destroy)(data_stream_t*);
00374 };
00375 
00376 typedef struct {
00377     data_stream_t data_stream;
00378 
00379     BYTE buf[READ_BUFFER_SIZE];
00380     DWORD buf_size;
00381     DWORD buf_pos;
00382     DWORD chunk_size;
00383 } chunked_stream_t;
00384 
00385 static inline void destroy_data_stream(data_stream_t *stream)
00386 {
00387     stream->vtbl->destroy(stream);
00388 }
00389 
00390 static void reset_data_stream(http_request_t *req)
00391 {
00392     destroy_data_stream(req->data_stream);
00393     req->data_stream = &req->netconn_stream.data_stream;
00394     req->read_pos = req->read_size = req->netconn_stream.content_read = 0;
00395     req->read_chunked = req->read_gzip = FALSE;
00396 }
00397 
00398 #ifdef HAVE_ZLIB
00399 
00400 typedef struct {
00401     data_stream_t stream;
00402     data_stream_t *parent_stream;
00403     z_stream zstream;
00404     BYTE buf[READ_BUFFER_SIZE];
00405     DWORD buf_size;
00406     DWORD buf_pos;
00407     BOOL end_of_data;
00408 } gzip_stream_t;
00409 
00410 static DWORD gzip_get_avail_data(data_stream_t *stream, http_request_t *req)
00411 {
00412     /* Allow reading only from read buffer */
00413     return 0;
00414 }
00415 
00416 static BOOL gzip_end_of_data(data_stream_t *stream, http_request_t *req)
00417 {
00418     gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
00419     return gzip_stream->end_of_data;
00420 }
00421 
00422 static DWORD gzip_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
00423         DWORD *read, read_mode_t read_mode)
00424 {
00425     gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
00426     z_stream *zstream = &gzip_stream->zstream;
00427     DWORD current_read, ret_read = 0;
00428     BOOL end;
00429     int zres;
00430     DWORD res = ERROR_SUCCESS;
00431 
00432     while(size && !gzip_stream->end_of_data) {
00433         end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
00434 
00435         if(gzip_stream->buf_size <= 64 && !end) {
00436             if(gzip_stream->buf_pos) {
00437                 if(gzip_stream->buf_size)
00438                     memmove(gzip_stream->buf, gzip_stream->buf+gzip_stream->buf_pos, gzip_stream->buf_size);
00439                 gzip_stream->buf_pos = 0;
00440             }
00441             res = gzip_stream->parent_stream->vtbl->read(gzip_stream->parent_stream, req, gzip_stream->buf+gzip_stream->buf_size,
00442                     sizeof(gzip_stream->buf)-gzip_stream->buf_size, &current_read, read_mode);
00443             gzip_stream->buf_size += current_read;
00444             if(res != ERROR_SUCCESS)
00445                 break;
00446             end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
00447             if(!current_read && !end) {
00448                 if(read_mode != READMODE_NOBLOCK) {
00449                     WARN("unexpected end of data\n");
00450                     gzip_stream->end_of_data = TRUE;
00451                 }
00452                 break;
00453             }
00454             if(gzip_stream->buf_size <= 64 && !end)
00455                 continue;
00456         }
00457 
00458         zstream->next_in = gzip_stream->buf+gzip_stream->buf_pos;
00459         zstream->avail_in = gzip_stream->buf_size-(end ? 0 : 64);
00460         zstream->next_out = buf+ret_read;
00461         zstream->avail_out = size;
00462         zres = inflate(&gzip_stream->zstream, 0);
00463         current_read = size - zstream->avail_out;
00464         size -= current_read;
00465         ret_read += current_read;
00466         gzip_stream->buf_size -= zstream->next_in - (gzip_stream->buf+gzip_stream->buf_pos);
00467         gzip_stream->buf_pos = zstream->next_in-gzip_stream->buf;
00468         if(zres == Z_STREAM_END) {
00469             TRACE("end of data\n");
00470             gzip_stream->end_of_data = TRUE;
00471             inflateEnd(zstream);
00472         }else if(zres != Z_OK) {
00473             WARN("inflate failed %d: %s\n", zres, debugstr_a(zstream->msg));
00474             if(!ret_read)
00475                 res = ERROR_INTERNET_DECODING_FAILED;
00476             break;
00477         }
00478 
00479         if(ret_read && read_mode == READMODE_ASYNC)
00480             read_mode = READMODE_NOBLOCK;
00481     }
00482 
00483     TRACE("read %u bytes\n", ret_read);
00484     *read = ret_read;
00485     return res;
00486 }
00487 
00488 static BOOL gzip_drain_content(data_stream_t *stream, http_request_t *req)
00489 {
00490     gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
00491     return gzip_stream->parent_stream->vtbl->drain_content(gzip_stream->parent_stream, req);
00492 }
00493 
00494 static void gzip_destroy(data_stream_t *stream)
00495 {
00496     gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
00497 
00498     destroy_data_stream(gzip_stream->parent_stream);
00499 
00500     if(!gzip_stream->end_of_data)
00501         inflateEnd(&gzip_stream->zstream);
00502     heap_free(gzip_stream);
00503 }
00504 
00505 static const data_stream_vtbl_t gzip_stream_vtbl = {
00506     gzip_get_avail_data,
00507     gzip_end_of_data,
00508     gzip_read,
00509     gzip_drain_content,
00510     gzip_destroy
00511 };
00512 
00513 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
00514 {
00515     return heap_alloc(items*size);
00516 }
00517 
00518 static void wininet_zfree(voidpf opaque, voidpf address)
00519 {
00520     HeapFree(GetProcessHeap(), 0, address);
00521 }
00522 
00523 static DWORD init_gzip_stream(http_request_t *req)
00524 {
00525     gzip_stream_t *gzip_stream;
00526     int index, zres;
00527 
00528     gzip_stream = heap_alloc_zero(sizeof(gzip_stream_t));
00529     if(!gzip_stream)
00530         return ERROR_OUTOFMEMORY;
00531 
00532     gzip_stream->stream.vtbl = &gzip_stream_vtbl;
00533     gzip_stream->zstream.zalloc = wininet_zalloc;
00534     gzip_stream->zstream.zfree = wininet_zfree;
00535 
00536     zres = inflateInit2(&gzip_stream->zstream, 0x1f);
00537     if(zres != Z_OK) {
00538         ERR("inflateInit failed: %d\n", zres);
00539         HeapFree(GetProcessHeap(), 0, gzip_stream);
00540         return ERROR_OUTOFMEMORY;
00541     }
00542 
00543     index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
00544     if(index != -1)
00545         HTTP_DeleteCustomHeader(req, index);
00546 
00547     if(req->read_size) {
00548         memcpy(gzip_stream->buf, req->read_buf+req->read_pos, req->read_size);
00549         gzip_stream->buf_size = req->read_size;
00550         req->read_pos = req->read_size = 0;
00551     }
00552 
00553     req->read_gzip = TRUE;
00554     gzip_stream->parent_stream = req->data_stream;
00555     req->data_stream = &gzip_stream->stream;
00556     return ERROR_SUCCESS;
00557 }
00558 
00559 #else
00560 
00561 static DWORD init_gzip_stream(http_request_t *req)
00562 {
00563     ERR("gzip stream not supported, missing zlib.\n");
00564     return ERROR_SUCCESS;
00565 }
00566 
00567 #endif
00568 
00569 /***********************************************************************
00570  *           HTTP_Tokenize (internal)
00571  *
00572  *  Tokenize a string, allocating memory for the tokens.
00573  */
00574 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
00575 {
00576     LPWSTR * token_array;
00577     int tokens = 0;
00578     int i;
00579     LPCWSTR next_token;
00580 
00581     if (string)
00582     {
00583         /* empty string has no tokens */
00584         if (*string)
00585             tokens++;
00586         /* count tokens */
00587         for (i = 0; string[i]; i++)
00588         {
00589             if (!strncmpW(string+i, token_string, strlenW(token_string)))
00590             {
00591                 DWORD j;
00592                 tokens++;
00593                 /* we want to skip over separators, but not the null terminator */
00594                 for (j = 0; j < strlenW(token_string) - 1; j++)
00595                     if (!string[i+j])
00596                         break;
00597                 i += j;
00598             }
00599         }
00600     }
00601 
00602     /* add 1 for terminating NULL */
00603     token_array = heap_alloc((tokens+1) * sizeof(*token_array));
00604     token_array[tokens] = NULL;
00605     if (!tokens)
00606         return token_array;
00607     for (i = 0; i < tokens; i++)
00608     {
00609         int len;
00610         next_token = strstrW(string, token_string);
00611         if (!next_token) next_token = string+strlenW(string);
00612         len = next_token - string;
00613         token_array[i] = heap_alloc((len+1)*sizeof(WCHAR));
00614         memcpy(token_array[i], string, len*sizeof(WCHAR));
00615         token_array[i][len] = '\0';
00616         string = next_token+strlenW(token_string);
00617     }
00618     return token_array;
00619 }
00620 
00621 /***********************************************************************
00622  *           HTTP_FreeTokens (internal)
00623  *
00624  *  Frees memory returned from HTTP_Tokenize.
00625  */
00626 static void HTTP_FreeTokens(LPWSTR * token_array)
00627 {
00628     int i;
00629     for (i = 0; token_array[i]; i++)
00630         HeapFree(GetProcessHeap(), 0, token_array[i]);
00631     HeapFree(GetProcessHeap(), 0, token_array);
00632 }
00633 
00634 static void HTTP_FixURL(http_request_t *request)
00635 {
00636     static const WCHAR szSlash[] = { '/',0 };
00637     static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
00638 
00639     /* If we don't have a path we set it to root */
00640     if (NULL == request->path)
00641         request->path = heap_strdupW(szSlash);
00642     else /* remove \r and \n*/
00643     {
00644         int nLen = strlenW(request->path);
00645         while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n')))
00646         {
00647             nLen--;
00648             request->path[nLen]='\0';
00649         }
00650         /* Replace '\' with '/' */
00651         while (nLen>0) {
00652             nLen--;
00653             if (request->path[nLen] == '\\') request->path[nLen]='/';
00654         }
00655     }
00656 
00657     if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
00658                        request->path, strlenW(request->path), szHttp, strlenW(szHttp) )
00659        && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */
00660     {
00661         WCHAR *fixurl = heap_alloc((strlenW(request->path) + 2)*sizeof(WCHAR));
00662         *fixurl = '/';
00663         strcpyW(fixurl + 1, request->path);
00664         HeapFree( GetProcessHeap(), 0, request->path );
00665         request->path = fixurl;
00666     }
00667 }
00668 
00669 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *request, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
00670 {
00671     LPWSTR requestString;
00672     DWORD len, n;
00673     LPCWSTR *req;
00674     UINT i;
00675     LPWSTR p;
00676 
00677     static const WCHAR szSpace[] = { ' ',0 };
00678     static const WCHAR szColon[] = { ':',' ',0 };
00679     static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
00680 
00681     /* allocate space for an array of all the string pointers to be added */
00682     len = (request->nCustHeaders)*4 + 10;
00683     req = heap_alloc(len*sizeof(LPCWSTR));
00684 
00685     /* add the verb, path and HTTP version string */
00686     n = 0;
00687     req[n++] = verb;
00688     req[n++] = szSpace;
00689     req[n++] = path;
00690     req[n++] = szSpace;
00691     req[n++] = version;
00692 
00693     /* Append custom request headers */
00694     for (i = 0; i < request->nCustHeaders; i++)
00695     {
00696         if (request->custHeaders[i].wFlags & HDR_ISREQUEST)
00697         {
00698             req[n++] = szCrLf;
00699             req[n++] = request->custHeaders[i].lpszField;
00700             req[n++] = szColon;
00701             req[n++] = request->custHeaders[i].lpszValue;
00702 
00703             TRACE("Adding custom header %s (%s)\n",
00704                    debugstr_w(request->custHeaders[i].lpszField),
00705                    debugstr_w(request->custHeaders[i].lpszValue));
00706         }
00707     }
00708 
00709     if( n >= len )
00710         ERR("oops. buffer overrun\n");
00711 
00712     req[n] = NULL;
00713     requestString = HTTP_build_req( req, 4 );
00714     HeapFree( GetProcessHeap(), 0, req );
00715 
00716     /*
00717      * Set (header) termination string for request
00718      * Make sure there's exactly two new lines at the end of the request
00719      */
00720     p = &requestString[strlenW(requestString)-1];
00721     while ( (*p == '\n') || (*p == '\r') )
00722        p--;
00723     strcpyW( p+1, sztwocrlf );
00724     
00725     return requestString;
00726 }
00727 
00728 static void HTTP_ProcessCookies( http_request_t *request )
00729 {
00730     int HeaderIndex;
00731     int numCookies = 0;
00732     LPHTTPHEADERW setCookieHeader;
00733 
00734     if(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES)
00735         return;
00736 
00737     while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, szSet_Cookie, numCookies++, FALSE)) != -1)
00738     {
00739         HTTPHEADERW *host;
00740         const WCHAR *data;
00741         WCHAR *name;
00742 
00743         setCookieHeader = &request->custHeaders[HeaderIndex];
00744 
00745         if (!setCookieHeader->lpszValue)
00746             continue;
00747 
00748         host = HTTP_GetHeader(request, hostW);
00749         if(!host)
00750             continue;
00751 
00752         data = strchrW(setCookieHeader->lpszValue, '=');
00753         if(!data)
00754             continue;
00755 
00756         name = heap_strndupW(setCookieHeader->lpszValue, data-setCookieHeader->lpszValue);
00757         if(!name)
00758             continue;
00759 
00760         data++;
00761         set_cookie(host->lpszValue, request->path, name, data);
00762         heap_free(name);
00763     }
00764 }
00765 
00766 static void strip_spaces(LPWSTR start)
00767 {
00768     LPWSTR str = start;
00769     LPWSTR end;
00770 
00771     while (*str == ' ' && *str != '\0')
00772         str++;
00773 
00774     if (str != start)
00775         memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
00776 
00777     end = start + strlenW(start) - 1;
00778     while (end >= start && *end == ' ')
00779     {
00780         *end = '\0';
00781         end--;
00782     }
00783 }
00784 
00785 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
00786 {
00787     static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
00788     static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
00789     BOOL is_basic;
00790     is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
00791         ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
00792     if (is_basic && pszRealm)
00793     {
00794         LPCWSTR token;
00795         LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
00796         LPCWSTR realm;
00797         ptr++;
00798         *pszRealm=NULL;
00799         token = strchrW(ptr,'=');
00800         if (!token)
00801             return TRUE;
00802         realm = ptr;
00803         while (*realm == ' ' && *realm != '\0')
00804             realm++;
00805         if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
00806             (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
00807         {
00808             token++;
00809             while (*token == ' ' && *token != '\0')
00810                 token++;
00811             if (*token == '\0')
00812                 return TRUE;
00813             *pszRealm = heap_strdupW(token);
00814             strip_spaces(*pszRealm);
00815         }
00816     }
00817 
00818     return is_basic;
00819 }
00820 
00821 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
00822 {
00823     if (!authinfo) return;
00824 
00825     if (SecIsValidHandle(&authinfo->ctx))
00826         DeleteSecurityContext(&authinfo->ctx);
00827     if (SecIsValidHandle(&authinfo->cred))
00828         FreeCredentialsHandle(&authinfo->cred);
00829 
00830     HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
00831     HeapFree(GetProcessHeap(), 0, authinfo->scheme);
00832     HeapFree(GetProcessHeap(), 0, authinfo);
00833 }
00834 
00835 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
00836 {
00837     basicAuthorizationData *ad;
00838     UINT rc = 0;
00839 
00840     TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
00841 
00842     EnterCriticalSection(&authcache_cs);
00843     LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
00844     {
00845         if (!strcmpiW(host,ad->host) && !strcmpW(realm,ad->realm))
00846         {
00847             TRACE("Authorization found in cache\n");
00848             *auth_data = heap_alloc(ad->authorizationLen);
00849             memcpy(*auth_data,ad->authorization,ad->authorizationLen);
00850             rc = ad->authorizationLen;
00851             break;
00852         }
00853     }
00854     LeaveCriticalSection(&authcache_cs);
00855     return rc;
00856 }
00857 
00858 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
00859 {
00860     struct list *cursor;
00861     basicAuthorizationData* ad = NULL;
00862 
00863     TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
00864 
00865     EnterCriticalSection(&authcache_cs);
00866     LIST_FOR_EACH(cursor, &basicAuthorizationCache)
00867     {
00868         basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
00869         if (!strcmpiW(host,check->host) && !strcmpW(realm,check->realm))
00870         {
00871             ad = check;
00872             break;
00873         }
00874     }
00875 
00876     if (ad)
00877     {
00878         TRACE("Found match in cache, replacing\n");
00879         HeapFree(GetProcessHeap(),0,ad->authorization);
00880         ad->authorization = heap_alloc(auth_data_len);
00881         memcpy(ad->authorization, auth_data, auth_data_len);
00882         ad->authorizationLen = auth_data_len;
00883     }
00884     else
00885     {
00886         ad = heap_alloc(sizeof(basicAuthorizationData));
00887         ad->host = heap_strdupW(host);
00888         ad->host = heap_strdupW(realm);
00889         ad->authorization = heap_alloc(auth_data_len);
00890         memcpy(ad->authorization, auth_data, auth_data_len);
00891         ad->authorizationLen = auth_data_len;
00892         list_add_head(&basicAuthorizationCache,&ad->entry);
00893         TRACE("authorization cached\n");
00894     }
00895     LeaveCriticalSection(&authcache_cs);
00896 }
00897 
00898 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
00899         SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
00900 {
00901     authorizationData *ad;
00902 
00903     TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
00904 
00905     EnterCriticalSection(&authcache_cs);
00906     LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
00907         if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
00908             TRACE("Authorization found in cache\n");
00909 
00910             nt_auth_identity->User = heap_strdupW(ad->user);
00911             nt_auth_identity->Password = heap_strdupW(ad->password);
00912             nt_auth_identity->Domain = heap_alloc(sizeof(WCHAR)*ad->domain_len);
00913             if(!nt_auth_identity->User || !nt_auth_identity->Password ||
00914                     (!nt_auth_identity->Domain && ad->domain_len)) {
00915                 HeapFree(GetProcessHeap(), 0, nt_auth_identity->User);
00916                 HeapFree(GetProcessHeap(), 0, nt_auth_identity->Password);
00917                 HeapFree(GetProcessHeap(), 0, nt_auth_identity->Domain);
00918                 break;
00919             }
00920 
00921             nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
00922             nt_auth_identity->UserLength = ad->user_len;
00923             nt_auth_identity->PasswordLength = ad->password_len;
00924             memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
00925             nt_auth_identity->DomainLength = ad->domain_len;
00926             LeaveCriticalSection(&authcache_cs);
00927             return TRUE;
00928         }
00929     }
00930     LeaveCriticalSection(&authcache_cs);
00931 
00932     return FALSE;
00933 }
00934 
00935 static void cache_authorization(LPWSTR host, LPWSTR scheme,
00936         SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
00937 {
00938     authorizationData *ad;
00939     BOOL found = FALSE;
00940 
00941     TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
00942 
00943     EnterCriticalSection(&authcache_cs);
00944     LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
00945         if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
00946             found = TRUE;
00947             break;
00948         }
00949 
00950     if(found) {
00951         HeapFree(GetProcessHeap(), 0, ad->user);
00952         HeapFree(GetProcessHeap(), 0, ad->password);
00953         HeapFree(GetProcessHeap(), 0, ad->domain);
00954     } else {
00955         ad = heap_alloc(sizeof(authorizationData));
00956         if(!ad) {
00957             LeaveCriticalSection(&authcache_cs);
00958             return;
00959         }
00960 
00961         ad->host = heap_strdupW(host);
00962         ad->scheme = heap_strdupW(scheme);
00963         list_add_head(&authorizationCache, &ad->entry);
00964     }
00965 
00966     ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
00967     ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
00968     ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
00969     ad->user_len = nt_auth_identity->UserLength;
00970     ad->password_len = nt_auth_identity->PasswordLength;
00971     ad->domain_len = nt_auth_identity->DomainLength;
00972 
00973     if(!ad->host || !ad->scheme || !ad->user || !ad->password
00974             || (nt_auth_identity->Domain && !ad->domain)) {
00975         HeapFree(GetProcessHeap(), 0, ad->host);
00976         HeapFree(GetProcessHeap(), 0, ad->scheme);
00977         HeapFree(GetProcessHeap(), 0, ad->user);
00978         HeapFree(GetProcessHeap(), 0, ad->password);
00979         HeapFree(GetProcessHeap(), 0, ad->domain);
00980         list_remove(&ad->entry);
00981         HeapFree(GetProcessHeap(), 0, ad);
00982     }
00983 
00984     LeaveCriticalSection(&authcache_cs);
00985 }
00986 
00987 static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue,
00988                                   struct HttpAuthInfo **ppAuthInfo,
00989                                   LPWSTR domain_and_username, LPWSTR password,
00990                                   LPWSTR host )
00991 {
00992     SECURITY_STATUS sec_status;
00993     struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
00994     BOOL first = FALSE;
00995     LPWSTR szRealm = NULL;
00996 
00997     TRACE("%s\n", debugstr_w(pszAuthValue));
00998 
00999     if (!pAuthInfo)
01000     {
01001         TimeStamp exp;
01002 
01003         first = TRUE;
01004         pAuthInfo = heap_alloc(sizeof(*pAuthInfo));
01005         if (!pAuthInfo)
01006             return FALSE;
01007 
01008         SecInvalidateHandle(&pAuthInfo->cred);
01009         SecInvalidateHandle(&pAuthInfo->ctx);
01010         memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
01011         pAuthInfo->attr = 0;
01012         pAuthInfo->auth_data = NULL;
01013         pAuthInfo->auth_data_len = 0;
01014         pAuthInfo->finished = FALSE;
01015 
01016         if (is_basic_auth_value(pszAuthValue,NULL))
01017         {
01018             static const WCHAR szBasic[] = {'B','a','s','i','c',0};
01019             pAuthInfo->scheme = heap_strdupW(szBasic);
01020             if (!pAuthInfo->scheme)
01021             {
01022                 HeapFree(GetProcessHeap(), 0, pAuthInfo);
01023                 return FALSE;
01024             }
01025         }
01026         else
01027         {
01028             PVOID pAuthData;
01029             SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
01030 
01031             pAuthInfo->scheme = heap_strdupW(pszAuthValue);
01032             if (!pAuthInfo->scheme)
01033             {
01034                 HeapFree(GetProcessHeap(), 0, pAuthInfo);
01035                 return FALSE;
01036             }
01037 
01038             if (domain_and_username)
01039             {
01040                 WCHAR *user = strchrW(domain_and_username, '\\');
01041                 WCHAR *domain = domain_and_username;
01042 
01043                 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
01044 
01045                 pAuthData = &nt_auth_identity;
01046 
01047                 if (user) user++;
01048                 else
01049                 {
01050                     user = domain_and_username;
01051                     domain = NULL;
01052                 }
01053 
01054                 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
01055                 nt_auth_identity.User = user;
01056                 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
01057                 nt_auth_identity.Domain = domain;
01058                 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
01059                 nt_auth_identity.Password = password;
01060                 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
01061 
01062                 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
01063             }
01064             else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
01065                 pAuthData = &nt_auth_identity;
01066             else
01067                 /* use default credentials */
01068                 pAuthData = NULL;
01069 
01070             sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
01071                                                    SECPKG_CRED_OUTBOUND, NULL,
01072                                                    pAuthData, NULL,
01073                                                    NULL, &pAuthInfo->cred,
01074                                                    &exp);
01075 
01076             if(pAuthData && !domain_and_username) {
01077                 HeapFree(GetProcessHeap(), 0, nt_auth_identity.User);
01078                 HeapFree(GetProcessHeap(), 0, nt_auth_identity.Domain);
01079                 HeapFree(GetProcessHeap(), 0, nt_auth_identity.Password);
01080             }
01081 
01082             if (sec_status == SEC_E_OK)
01083             {
01084                 PSecPkgInfoW sec_pkg_info;
01085                 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
01086                 if (sec_status == SEC_E_OK)
01087                 {
01088                     pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
01089                     FreeContextBuffer(sec_pkg_info);
01090                 }
01091             }
01092             if (sec_status != SEC_E_OK)
01093             {
01094                 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
01095                      debugstr_w(pAuthInfo->scheme), sec_status);
01096                 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
01097                 HeapFree(GetProcessHeap(), 0, pAuthInfo);
01098                 return FALSE;
01099             }
01100         }
01101         *ppAuthInfo = pAuthInfo;
01102     }
01103     else if (pAuthInfo->finished)
01104         return FALSE;
01105 
01106     if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
01107         strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
01108     {
01109         ERR("authentication scheme changed from %s to %s\n",
01110             debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
01111         return FALSE;
01112     }
01113 
01114     if (is_basic_auth_value(pszAuthValue,&szRealm))
01115     {
01116         int userlen;
01117         int passlen;
01118         char *auth_data = NULL;
01119         UINT auth_data_len = 0;
01120 
01121         TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
01122 
01123         if (!domain_and_username)
01124         {
01125             if (host && szRealm)
01126                 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
01127             if (auth_data_len == 0)
01128             {
01129                 HeapFree(GetProcessHeap(),0,szRealm);
01130                 return FALSE;
01131             }
01132         }
01133         else
01134         {
01135             userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
01136             passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
01137 
01138             /* length includes a nul terminator, which will be re-used for the ':' */
01139             auth_data = heap_alloc(userlen + 1 + passlen);
01140             if (!auth_data)
01141             {
01142                 HeapFree(GetProcessHeap(),0,szRealm);
01143                 return FALSE;
01144             }
01145 
01146             WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
01147             auth_data[userlen] = ':';
01148             WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
01149             auth_data_len = userlen + 1 + passlen;
01150             if (host && szRealm)
01151                 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
01152         }
01153 
01154         pAuthInfo->auth_data = auth_data;
01155         pAuthInfo->auth_data_len = auth_data_len;
01156         pAuthInfo->finished = TRUE;
01157         HeapFree(GetProcessHeap(),0,szRealm);
01158 
01159         return TRUE;
01160     }
01161     else
01162     {
01163         LPCWSTR pszAuthData;
01164         SecBufferDesc out_desc, in_desc;
01165         SecBuffer out, in;
01166         unsigned char *buffer;
01167         ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
01168             ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
01169 
01170         in.BufferType = SECBUFFER_TOKEN;
01171         in.cbBuffer = 0;
01172         in.pvBuffer = NULL;
01173 
01174         in_desc.ulVersion = 0;
01175         in_desc.cBuffers = 1;
01176         in_desc.pBuffers = &in;
01177 
01178         pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
01179         if (*pszAuthData == ' ')
01180         {
01181             pszAuthData++;
01182             in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
01183             in.pvBuffer = heap_alloc(in.cbBuffer);
01184             HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
01185         }
01186 
01187         buffer = heap_alloc(pAuthInfo->max_token);
01188 
01189         out.BufferType = SECBUFFER_TOKEN;
01190         out.cbBuffer = pAuthInfo->max_token;
01191         out.pvBuffer = buffer;
01192 
01193         out_desc.ulVersion = 0;
01194         out_desc.cBuffers = 1;
01195         out_desc.pBuffers = &out;
01196 
01197         sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
01198                                                 first ? NULL : &pAuthInfo->ctx,
01199                                                 first ? request->session->serverName : NULL,
01200                                                 context_req, 0, SECURITY_NETWORK_DREP,
01201                                                 in.pvBuffer ? &in_desc : NULL,
01202                                                 0, &pAuthInfo->ctx, &out_desc,
01203                                                 &pAuthInfo->attr, &pAuthInfo->exp);
01204         if (sec_status == SEC_E_OK)
01205         {
01206             pAuthInfo->finished = TRUE;
01207             pAuthInfo->auth_data = out.pvBuffer;
01208             pAuthInfo->auth_data_len = out.cbBuffer;
01209             TRACE("sending last auth packet\n");
01210         }
01211         else if (sec_status == SEC_I_CONTINUE_NEEDED)
01212         {
01213             pAuthInfo->auth_data = out.pvBuffer;
01214             pAuthInfo->auth_data_len = out.cbBuffer;
01215             TRACE("sending next auth packet\n");
01216         }
01217         else
01218         {
01219             ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
01220             HeapFree(GetProcessHeap(), 0, out.pvBuffer);
01221             destroy_authinfo(pAuthInfo);
01222             *ppAuthInfo = NULL;
01223             return FALSE;
01224         }
01225     }
01226 
01227     return TRUE;
01228 }
01229 
01230 /***********************************************************************
01231  *           HTTP_HttpAddRequestHeadersW (internal)
01232  */
01233 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *request,
01234     LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
01235 {
01236     LPWSTR lpszStart;
01237     LPWSTR lpszEnd;
01238     LPWSTR buffer;
01239     DWORD len, res = ERROR_HTTP_INVALID_HEADER;
01240 
01241     TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
01242 
01243     if( dwHeaderLength == ~0U )
01244         len = strlenW(lpszHeader);
01245     else
01246         len = dwHeaderLength;
01247     buffer = heap_alloc(sizeof(WCHAR)*(len+1));
01248     lstrcpynW( buffer, lpszHeader, len + 1);
01249 
01250     lpszStart = buffer;
01251 
01252     do
01253     {
01254         LPWSTR * pFieldAndValue;
01255 
01256         lpszEnd = lpszStart;
01257 
01258         while (*lpszEnd != '\0')
01259         {
01260             if (*lpszEnd == '\r' || *lpszEnd == '\n')
01261                  break;
01262             lpszEnd++;
01263         }
01264 
01265         if (*lpszStart == '\0')
01266         break;
01267 
01268         if (*lpszEnd == '\r' || *lpszEnd == '\n')
01269         {
01270             *lpszEnd = '\0';
01271             lpszEnd++; /* Jump over newline */
01272         }
01273         TRACE("interpreting header %s\n", debugstr_w(lpszStart));
01274         if (*lpszStart == '\0')
01275         {
01276             /* Skip 0-length headers */
01277             lpszStart = lpszEnd;
01278             res = ERROR_SUCCESS;
01279             continue;
01280         }
01281         pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
01282         if (pFieldAndValue)
01283         {
01284             res = HTTP_VerifyValidHeader(request, pFieldAndValue[0]);
01285             if (res == ERROR_SUCCESS)
01286                 res = HTTP_ProcessHeader(request, pFieldAndValue[0],
01287                     pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
01288             HTTP_FreeTokens(pFieldAndValue);
01289         }
01290 
01291         lpszStart = lpszEnd;
01292     } while (res == ERROR_SUCCESS);
01293 
01294     HeapFree(GetProcessHeap(), 0, buffer);
01295 
01296     return res;
01297 }
01298 
01299 /***********************************************************************
01300  *           HttpAddRequestHeadersW (WININET.@)
01301  *
01302  * Adds one or more HTTP header to the request handler
01303  *
01304  * NOTE
01305  * On Windows if dwHeaderLength includes the trailing '\0', then
01306  * HttpAddRequestHeadersW() adds it too. However this results in an
01307  * invalid Http header which is rejected by some servers so we probably
01308  * don't need to match Windows on that point.
01309  *
01310  * RETURNS
01311  *    TRUE  on success
01312  *    FALSE on failure
01313  *
01314  */
01315 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
01316     LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
01317 {
01318     http_request_t *request;
01319     DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
01320 
01321     TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
01322 
01323     if (!lpszHeader) 
01324       return TRUE;
01325 
01326     request = (http_request_t*) get_handle_object( hHttpRequest );
01327     if (request && request->hdr.htype == WH_HHTTPREQ)
01328         res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier );
01329     if( request )
01330         WININET_Release( &request->hdr );
01331 
01332     if(res != ERROR_SUCCESS)
01333         SetLastError(res);
01334     return res == ERROR_SUCCESS;
01335 }
01336 
01337 /***********************************************************************
01338  *           HttpAddRequestHeadersA (WININET.@)
01339  *
01340  * Adds one or more HTTP header to the request handler
01341  *
01342  * RETURNS
01343  *    TRUE  on success
01344  *    FALSE on failure
01345  *
01346  */
01347 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
01348     LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
01349 {
01350     DWORD len;
01351     LPWSTR hdr;
01352     BOOL r;
01353 
01354     TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
01355 
01356     len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
01357     hdr = heap_alloc(len*sizeof(WCHAR));
01358     MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
01359     if( dwHeaderLength != ~0U )
01360         dwHeaderLength = len;
01361 
01362     r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
01363 
01364     HeapFree( GetProcessHeap(), 0, hdr );
01365 
01366     return r;
01367 }
01368 
01369 /***********************************************************************
01370  *           HttpOpenRequestA (WININET.@)
01371  *
01372  * Open a HTTP request handle
01373  *
01374  * RETURNS
01375  *    HINTERNET  a HTTP request handle on success
01376  *    NULL   on failure
01377  *
01378  */
01379 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
01380     LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
01381     LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
01382     DWORD dwFlags, DWORD_PTR dwContext)
01383 {
01384     LPWSTR szVerb = NULL, szObjectName = NULL;
01385     LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
01386     INT acceptTypesCount;
01387     HINTERNET rc = FALSE;
01388     LPCSTR *types;
01389 
01390     TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
01391           debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
01392           debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
01393           dwFlags, dwContext);
01394 
01395     if (lpszVerb)
01396     {
01397         szVerb = heap_strdupAtoW(lpszVerb);
01398         if ( !szVerb )
01399             goto end;
01400     }
01401 
01402     if (lpszObjectName)
01403     {
01404         szObjectName = heap_strdupAtoW(lpszObjectName);
01405         if ( !szObjectName )
01406             goto end;
01407     }
01408 
01409     if (lpszVersion)
01410     {
01411         szVersion = heap_strdupAtoW(lpszVersion);
01412         if ( !szVersion )
01413             goto end;
01414     }
01415 
01416     if (lpszReferrer)
01417     {
01418         szReferrer = heap_strdupAtoW(lpszReferrer);
01419         if ( !szReferrer )
01420             goto end;
01421     }
01422 
01423     if (lpszAcceptTypes)
01424     {
01425         acceptTypesCount = 0;
01426         types = lpszAcceptTypes;
01427         while (*types)
01428         {
01429             __TRY
01430             {
01431                 /* find out how many there are */
01432                 if (*types && **types)
01433                 {
01434                     TRACE("accept type: %s\n", debugstr_a(*types));
01435                     acceptTypesCount++;
01436                 }
01437             }
01438             __EXCEPT_PAGE_FAULT
01439             {
01440                 WARN("invalid accept type pointer\n");
01441             }
01442             __ENDTRY;
01443             types++;
01444         }
01445         szAcceptTypes = heap_alloc(sizeof(WCHAR *) * (acceptTypesCount+1));
01446         if (!szAcceptTypes) goto end;
01447 
01448         acceptTypesCount = 0;
01449         types = lpszAcceptTypes;
01450         while (*types)
01451         {
01452             __TRY
01453             {
01454                 if (*types && **types)
01455                     szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
01456             }
01457             __EXCEPT_PAGE_FAULT
01458             {
01459                 /* ignore invalid pointer */
01460             }
01461             __ENDTRY;
01462             types++;
01463         }
01464         szAcceptTypes[acceptTypesCount] = NULL;
01465     }
01466 
01467     rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
01468                           szVersion, szReferrer,
01469                           (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
01470 
01471 end:
01472     if (szAcceptTypes)
01473     {
01474         acceptTypesCount = 0;
01475         while (szAcceptTypes[acceptTypesCount])
01476         {
01477             HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
01478             acceptTypesCount++;
01479         }
01480         HeapFree(GetProcessHeap(), 0, szAcceptTypes);
01481     }
01482     HeapFree(GetProcessHeap(), 0, szReferrer);
01483     HeapFree(GetProcessHeap(), 0, szVersion);
01484     HeapFree(GetProcessHeap(), 0, szObjectName);
01485     HeapFree(GetProcessHeap(), 0, szVerb);
01486 
01487     return rc;
01488 }
01489 
01490 /***********************************************************************
01491  *  HTTP_EncodeBase64
01492  */
01493 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
01494 {
01495     UINT n = 0, x;
01496     static const CHAR HTTP_Base64Enc[] =
01497         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
01498 
01499     while( len > 0 )
01500     {
01501         /* first 6 bits, all from bin[0] */
01502         base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
01503         x = (bin[0] & 3) << 4;
01504 
01505         /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
01506         if( len == 1 )
01507         {
01508             base64[n++] = HTTP_Base64Enc[x];
01509             base64[n++] = '=';
01510             base64[n++] = '=';
01511             break;
01512         }
01513         base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
01514         x = ( bin[1] & 0x0f ) << 2;
01515 
01516         /* next 6 bits 4 from bin[1] and 2 from bin[2] */
01517         if( len == 2 )
01518         {
01519             base64[n++] = HTTP_Base64Enc[x];
01520             base64[n++] = '=';
01521             break;
01522         }
01523         base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
01524 
01525         /* last 6 bits, all from bin [2] */
01526         base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
01527         bin += 3;
01528         len -= 3;
01529     }
01530     base64[n] = 0;
01531     return n;
01532 }
01533 
01534 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
01535                ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
01536                ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
01537                ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
01538 static const signed char HTTP_Base64Dec[256] =
01539 {
01540     CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
01541     CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
01542     CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
01543     CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
01544     CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
01545     CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
01546     CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
01547     CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
01548     CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
01549     CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
01550     CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
01551     CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
01552     CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
01553     CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
01554     CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
01555     CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
01556     CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
01557     CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
01558     CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
01559     CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
01560     CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
01561     CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
01562     CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
01563     CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
01564     CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
01565     CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
01566 };
01567 #undef CH
01568 
01569 /***********************************************************************
01570  *  HTTP_DecodeBase64
01571  */
01572 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
01573 {
01574     unsigned int n = 0;
01575 
01576     while(*base64)
01577     {
01578         signed char in[4];
01579 
01580         if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
01581             ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
01582             base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
01583             ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
01584         {
01585             WARN("invalid base64: %s\n", debugstr_w(base64));
01586             return 0;
01587         }
01588         if (bin)
01589             bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
01590         n++;
01591 
01592         if ((base64[2] == '=') && (base64[3] == '='))
01593             break;
01594         if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
01595             ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
01596         {
01597             WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
01598             return 0;
01599         }
01600         if (bin)
01601             bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
01602         n++;
01603 
01604         if (base64[3] == '=')
01605             break;
01606         if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
01607             ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
01608         {
01609             WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
01610             return 0;
01611         }
01612         if (bin)
01613             bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
01614         n++;
01615 
01616         base64 += 4;
01617     }
01618 
01619     return n;
01620 }
01621 
01622 /***********************************************************************
01623  *  HTTP_InsertAuthorization
01624  *
01625  *   Insert or delete the authorization field in the request header.
01626  */
01627 static BOOL HTTP_InsertAuthorization( http_request_t *request, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
01628 {
01629     if (pAuthInfo)
01630     {
01631         static const WCHAR wszSpace[] = {' ',0};
01632         static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
01633         unsigned int len;
01634         WCHAR *authorization = NULL;
01635 
01636         if (pAuthInfo->auth_data_len)
01637         {
01638             /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
01639             len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
01640             authorization = heap_alloc((len+1)*sizeof(WCHAR));
01641             if (!authorization)
01642                 return FALSE;
01643 
01644             strcpyW(authorization, pAuthInfo->scheme);
01645             strcatW(authorization, wszSpace);
01646             HTTP_EncodeBase64(pAuthInfo->auth_data,
01647                               pAuthInfo->auth_data_len,
01648                               authorization+strlenW(authorization));
01649 
01650             /* clear the data as it isn't valid now that it has been sent to the
01651              * server, unless it's Basic authentication which doesn't do
01652              * connection tracking */
01653             if (strcmpiW(pAuthInfo->scheme, wszBasic))
01654             {
01655                 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
01656                 pAuthInfo->auth_data = NULL;
01657                 pAuthInfo->auth_data_len = 0;
01658             }
01659         }
01660 
01661         TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
01662 
01663         HTTP_ProcessHeader(request, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
01664 
01665         HeapFree(GetProcessHeap(), 0, authorization);
01666     }
01667     return TRUE;
01668 }
01669 
01670 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
01671 {
01672     WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
01673     DWORD size;
01674 
01675     size = sizeof(new_location);
01676     if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
01677     {
01678         if (!(url = heap_alloc(size + sizeof(WCHAR)))) return NULL;
01679         strcpyW( url, new_location );
01680     }
01681     else
01682     {
01683         static const WCHAR slash[] = { '/',0 };
01684         static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','u',0 };
01685         static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','u',0 };
01686         http_session_t *session = req->session;
01687 
01688         size = 16; /* "https://" + sizeof(port#) + ":/\0" */
01689         size += strlenW( session->hostName ) + strlenW( req->path );
01690 
01691         if (!(url = heap_alloc(size * sizeof(WCHAR)))) return NULL;
01692 
01693         if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
01694             sprintfW( url, formatSSL, session->hostName, session->hostPort );
01695         else
01696             sprintfW( url, format, session->hostName, session->hostPort );
01697         if (req->path[0] != '/') strcatW( url, slash );
01698         strcatW( url, req->path );
01699     }
01700     TRACE("url=%s\n", debugstr_w(url));
01701     return url;
01702 }
01703 
01704 /***********************************************************************
01705  *           HTTP_DealWithProxy
01706  */
01707 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_request_t *request)
01708 {
01709     WCHAR buf[MAXHOSTNAME];
01710     WCHAR protoProxy[MAXHOSTNAME + 15];
01711     DWORD protoProxyLen = sizeof(protoProxy) / sizeof(protoProxy[0]);
01712     WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
01713     static WCHAR szNul[] = { 0 };
01714     URL_COMPONENTSW UrlComponents;
01715     static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
01716     static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
01717     static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
01718 
01719     memset( &UrlComponents, 0, sizeof UrlComponents );
01720     UrlComponents.dwStructSize = sizeof UrlComponents;
01721     UrlComponents.lpszHostName = buf;
01722     UrlComponents.dwHostNameLength = MAXHOSTNAME;
01723 
01724     if (!INTERNET_FindProxyForProtocol(hIC->proxy, protoHttp, protoProxy, &protoProxyLen))
01725         return FALSE;
01726     if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
01727                                  protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
01728         sprintfW(proxy, szFormat, protoProxy);
01729     else
01730     strcpyW(proxy, protoProxy);
01731     if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
01732         return FALSE;
01733     if( UrlComponents.dwHostNameLength == 0 )
01734         return FALSE;
01735 
01736     if( !request->path )
01737         request->path = szNul;
01738 
01739     if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
01740         UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
01741 
01742     HeapFree(GetProcessHeap(), 0, session->serverName);
01743     session->serverName = heap_strdupW(UrlComponents.lpszHostName);
01744     session->serverPort = UrlComponents.nPort;
01745 
01746     TRACE("proxy server=%s port=%d\n", debugstr_w(session->serverName), session->serverPort);
01747     return TRUE;
01748 }
01749 
01750 static DWORD HTTP_ResolveName(http_request_t *request, server_t *server)
01751 {
01752     socklen_t addr_len;
01753     const void *addr;
01754 
01755     if(server->addr_len)
01756         return ERROR_SUCCESS;
01757 
01758     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
01759                           INTERNET_STATUS_RESOLVING_NAME,
01760                           server->name,
01761                           (strlenW(server->name)+1) * sizeof(WCHAR));
01762 
01763     addr_len = sizeof(server->addr);
01764     if (!GetAddress(server->name, server->port, (struct sockaddr *)&server->addr, &addr_len))
01765         return ERROR_INTERNET_NAME_NOT_RESOLVED;
01766 
01767     switch(server->addr.ss_family) {
01768     case AF_INET:
01769         addr = &((struct sockaddr_in *)&server->addr)->sin_addr;
01770         break;
01771     case AF_INET6:
01772         addr = &((struct sockaddr_in6 *)&server->addr)->sin6_addr;
01773         break;
01774     default:
01775         WARN("unsupported family %d\n", server->addr.ss_family);
01776         return ERROR_INTERNET_NAME_NOT_RESOLVED;
01777     }
01778 
01779     server->addr_len = addr_len;
01780     inet_ntop(server->addr.ss_family, addr, server->addr_str, sizeof(server->addr_str));
01781     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
01782                           INTERNET_STATUS_NAME_RESOLVED,
01783                           server->addr_str, strlen(server->addr_str)+1);
01784 
01785     TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str);
01786     return ERROR_SUCCESS;
01787 }
01788 
01789 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
01790 {
01791     static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
01792     static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
01793     static const WCHAR slash[] = { '/',0 };
01794     LPHTTPHEADERW host_header;
01795     LPCWSTR scheme;
01796 
01797     host_header = HTTP_GetHeader(req, hostW);
01798     if(!host_header)
01799         return FALSE;
01800 
01801     if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
01802         scheme = https;
01803     else
01804         scheme = http;
01805     strcpyW(buf, scheme);
01806     strcatW(buf, host_header->lpszValue);
01807     if (req->path[0] != '/')
01808         strcatW(buf, slash);
01809     strcatW(buf, req->path);
01810     return TRUE;
01811 }
01812 
01813 
01814 /***********************************************************************
01815  *           HTTPREQ_Destroy (internal)
01816  *
01817  * Deallocate request handle
01818  *
01819  */
01820 static void HTTPREQ_Destroy(object_header_t *hdr)
01821 {
01822     http_request_t *request = (http_request_t*) hdr;
01823     DWORD i;
01824 
01825     TRACE("\n");
01826 
01827     if(request->hCacheFile) {
01828         WCHAR url[INTERNET_MAX_URL_LENGTH];
01829 
01830         CloseHandle(request->hCacheFile);
01831 
01832         if(HTTP_GetRequestURL(request, url)) {
01833             DWORD headersLen;
01834 
01835             headersLen = request->rawHeaders ? strlenW(request->rawHeaders) : 0;
01836             CommitUrlCacheEntryW(url, request->cacheFile, request->expires,
01837                     request->last_modified, NORMAL_CACHE_ENTRY,
01838                     request->rawHeaders, headersLen, NULL, 0);
01839         }
01840     }
01841 
01842     HeapFree(GetProcessHeap(), 0, request->cacheFile);
01843 
01844     DeleteCriticalSection( &request->read_section );
01845     WININET_Release(&request->session->hdr);
01846 
01847     destroy_authinfo(request->authInfo);
01848     destroy_authinfo(request->proxyAuthInfo);
01849 
01850     HeapFree(GetProcessHeap(), 0, request->path);
01851     HeapFree(GetProcessHeap(), 0, request->verb);
01852     HeapFree(GetProcessHeap(), 0, request->rawHeaders);
01853     HeapFree(GetProcessHeap(), 0, request->version);
01854     HeapFree(GetProcessHeap(), 0, request->statusText);
01855 
01856     for (i = 0; i < request->nCustHeaders; i++)
01857     {
01858         HeapFree(GetProcessHeap(), 0, request->custHeaders[i].lpszField);
01859         HeapFree(GetProcessHeap(), 0, request->custHeaders[i].lpszValue);
01860     }
01861 
01862     destroy_data_stream(request->data_stream);
01863     HeapFree(GetProcessHeap(), 0, request->custHeaders);
01864 }
01865 
01866 static void http_release_netconn(http_request_t *req, BOOL reuse)
01867 {
01868     TRACE("%p %p\n",req, req->netconn);
01869 
01870     if(!req->netconn)
01871         return;
01872 
01873 #ifndef __REACTOS__
01874     if(reuse && req->netconn->keep_alive) {
01875         BOOL run_collector;
01876 
01877         EnterCriticalSection(&connection_pool_cs);
01878 
01879         list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry);
01880         req->netconn->keep_until = (DWORD64)GetTickCount() + COLLECT_TIME;
01881         req->netconn = NULL;
01882 
01883         run_collector = !collector_running;
01884         collector_running = TRUE;
01885 
01886         LeaveCriticalSection(&connection_pool_cs);
01887 
01888         if(run_collector) {
01889             HANDLE thread = NULL;
01890             HMODULE module;
01891 
01892             GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
01893             if(module)
01894                 thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL);
01895             if(!thread) {
01896                 EnterCriticalSection(&connection_pool_cs);
01897                 collector_running = FALSE;
01898                 LeaveCriticalSection(&connection_pool_cs);
01899 
01900                 if(module)
01901                     FreeLibrary(module);
01902             }
01903             else
01904                 CloseHandle(thread);
01905         }
01906         return;
01907     }
01908 #endif
01909 
01910     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
01911                           INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
01912 
01913     free_netconn(req->netconn);
01914     req->netconn = NULL;
01915 
01916     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
01917                           INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
01918 }
01919 
01920 static void drain_content(http_request_t *req)
01921 {
01922     BOOL try_reuse;
01923 
01924     if (!req->netconn) return;
01925 
01926     if (req->contentLength == -1)
01927         try_reuse = FALSE;
01928     else if(!strcmpW(req->verb, szHEAD))
01929         try_reuse = TRUE;
01930     else
01931         try_reuse = req->data_stream->vtbl->drain_content(req->data_stream, req);
01932 
01933     http_release_netconn(req, try_reuse);
01934 }
01935 
01936 static BOOL HTTP_KeepAlive(http_request_t *request)
01937 {
01938     WCHAR szVersion[10];
01939     WCHAR szConnectionResponse[20];
01940     DWORD dwBufferSize = sizeof(szVersion);
01941     BOOL keepalive = FALSE;
01942 
01943     /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
01944      * the connection is keep-alive by default */
01945     if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
01946         && !strcmpiW(szVersion, g_szHttp1_1))
01947     {
01948         keepalive = TRUE;
01949     }
01950 
01951     dwBufferSize = sizeof(szConnectionResponse);
01952     if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
01953         || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
01954     {
01955         keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
01956     }
01957 
01958     return keepalive;
01959 }
01960 
01961 static void HTTPREQ_CloseConnection(object_header_t *hdr)
01962 {
01963     http_request_t *req = (http_request_t*)hdr;
01964 
01965     drain_content(req);
01966 }
01967 
01968 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
01969 {
01970     http_request_t *req = (http_request_t*)hdr;
01971 
01972     switch(option) {
01973     case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
01974     {
01975         http_session_t *session = req->session;
01976         INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
01977 
01978         FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
01979 
01980         if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
01981             return ERROR_INSUFFICIENT_BUFFER;
01982         *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
01983         /* FIXME: can't get a SOCKET from our connection since we don't use
01984          * winsock
01985          */
01986         info->Socket = 0;
01987         /* FIXME: get source port from req->netConnection */
01988         info->SourcePort = 0;
01989         info->DestPort = session->hostPort;
01990         info->Flags = 0;
01991         if (HTTP_KeepAlive(req))
01992             info->Flags |= IDSI_FLAG_KEEP_ALIVE;
01993         if (session->appInfo->proxy && session->appInfo->proxy[0] != 0)
01994             info->Flags |= IDSI_FLAG_PROXY;
01995         if (req->netconn->useSSL)
01996             info->Flags |= IDSI_FLAG_SECURE;
01997 
01998         return ERROR_SUCCESS;
01999     }
02000 
02001     case INTERNET_OPTION_SECURITY_FLAGS:
02002     {
02003         DWORD flags;
02004 
02005         if (*size < sizeof(ULONG))
02006             return ERROR_INSUFFICIENT_BUFFER;
02007 
02008         *size = sizeof(DWORD);
02009         flags = 0;
02010         if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
02011             flags |= SECURITY_FLAG_SECURE;
02012         flags |= req->security_flags;
02013         if(req->netconn) {
02014             int bits = NETCON_GetCipherStrength(req->netconn);
02015             if (bits >= 128)
02016                 flags |= SECURITY_FLAG_STRENGTH_STRONG;
02017             else if (bits >= 56)
02018                 flags |= SECURITY_FLAG_STRENGTH_MEDIUM;
02019             else
02020                 flags |= SECURITY_FLAG_STRENGTH_WEAK;
02021         }
02022         *(DWORD *)buffer = flags;
02023         return ERROR_SUCCESS;
02024     }
02025 
02026     case INTERNET_OPTION_HANDLE_TYPE:
02027         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
02028 
02029         if (*size < sizeof(ULONG))
02030             return ERROR_INSUFFICIENT_BUFFER;
02031 
02032         *size = sizeof(DWORD);
02033         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
02034         return ERROR_SUCCESS;
02035 
02036     case INTERNET_OPTION_URL: {
02037         WCHAR url[INTERNET_MAX_URL_LENGTH];
02038         HTTPHEADERW *host;
02039         DWORD len;
02040         WCHAR *pch;
02041 
02042         static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
02043 
02044         TRACE("INTERNET_OPTION_URL\n");
02045 
02046         host = HTTP_GetHeader(req, hostW);
02047         strcpyW(url, httpW);
02048         strcatW(url, host->lpszValue);
02049         if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
02050             *pch = 0;
02051         strcatW(url, req->path);
02052 
02053         TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
02054 
02055         if(unicode) {
02056             len = (strlenW(url)+1) * sizeof(WCHAR);
02057             if(*size < len)
02058                 return ERROR_INSUFFICIENT_BUFFER;
02059 
02060             *size = len;
02061             strcpyW(buffer, url);
02062             return ERROR_SUCCESS;
02063         }else {
02064             len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
02065             if(len > *size)
02066                 return ERROR_INSUFFICIENT_BUFFER;
02067 
02068             *size = len;
02069             return ERROR_SUCCESS;
02070         }
02071     }
02072 
02073     case INTERNET_OPTION_CACHE_TIMESTAMPS: {
02074         INTERNET_CACHE_ENTRY_INFOW *info;
02075         INTERNET_CACHE_TIMESTAMPS *ts = buffer;
02076         WCHAR url[INTERNET_MAX_URL_LENGTH];
02077         DWORD nbytes, error;
02078         BOOL ret;
02079 
02080         TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
02081 
02082         if (*size < sizeof(*ts))
02083         {
02084             *size = sizeof(*ts);
02085             return ERROR_INSUFFICIENT_BUFFER;
02086         }
02087         nbytes = 0;
02088         HTTP_GetRequestURL(req, url);
02089         ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
02090         error = GetLastError();
02091         if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
02092         {
02093             if (!(info = heap_alloc(nbytes)))
02094                 return ERROR_OUTOFMEMORY;
02095 
02096             GetUrlCacheEntryInfoW(url, info, &nbytes);
02097 
02098             ts->ftExpires = info->ExpireTime;
02099             ts->ftLastModified = info->LastModifiedTime;
02100 
02101             HeapFree(GetProcessHeap(), 0, info);
02102             *size = sizeof(*ts);
02103             return ERROR_SUCCESS;
02104         }
02105         return error;
02106     }
02107 
02108     case INTERNET_OPTION_DATAFILE_NAME: {
02109         DWORD req_size;
02110 
02111         TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
02112 
02113         if(!req->cacheFile) {
02114             *size = 0;
02115             return ERROR_INTERNET_ITEM_NOT_FOUND;
02116         }
02117 
02118         if(unicode) {
02119             req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
02120             if(*size < req_size)
02121                 return ERROR_INSUFFICIENT_BUFFER;
02122 
02123             *size = req_size;
02124             memcpy(buffer, req->cacheFile, *size);
02125             return ERROR_SUCCESS;
02126         }else {
02127             req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
02128             if (req_size > *size)
02129                 return ERROR_INSUFFICIENT_BUFFER;
02130 
02131             *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
02132                     -1, buffer, *size, NULL, NULL);
02133             return ERROR_SUCCESS;
02134         }
02135     }
02136 
02137     case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
02138         PCCERT_CONTEXT context;
02139 
02140         if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
02141             *size = sizeof(INTERNET_CERTIFICATE_INFOA);
02142             return ERROR_INSUFFICIENT_BUFFER;
02143         }
02144 
02145         context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
02146         if(context) {
02147             INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
02148             DWORD len;
02149 
02150             memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
02151             info->ftExpiry = context->pCertInfo->NotAfter;
02152             info->ftStart = context->pCertInfo->NotBefore;
02153             len = CertNameToStrA(context->dwCertEncodingType,
02154                      &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
02155             info->lpszSubjectInfo = LocalAlloc(0, len);
02156             if(info->lpszSubjectInfo)
02157                 CertNameToStrA(context->dwCertEncodingType,
02158                          &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
02159                          info->lpszSubjectInfo, len);
02160             len = CertNameToStrA(context->dwCertEncodingType,
02161                      &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
02162             info->lpszIssuerInfo = LocalAlloc(0, len);
02163             if(info->lpszIssuerInfo)
02164                 CertNameToStrA(context->dwCertEncodingType,
02165                          &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
02166                          info->lpszIssuerInfo, len);
02167             info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
02168             CertFreeCertificateContext(context);
02169             return ERROR_SUCCESS;
02170         }
02171     }
02172     }
02173 
02174     return INET_QueryOption(hdr, option, buffer, size, unicode);
02175 }
02176 
02177 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
02178 {
02179     http_request_t *req = (http_request_t*)hdr;
02180 
02181     switch(option) {
02182     case INTERNET_OPTION_SECURITY_FLAGS:
02183     {
02184         DWORD flags;
02185 
02186         if (!buffer || size != sizeof(DWORD))
02187             return ERROR_INVALID_PARAMETER;
02188         flags = *(DWORD *)buffer;
02189         TRACE("%08x\n", flags);
02190         req->security_flags = flags;
02191         if(req->netconn)
02192             req->netconn->security_flags = flags;
02193         return ERROR_SUCCESS;
02194     }
02195     case INTERNET_OPTION_SEND_TIMEOUT:
02196     case INTERNET_OPTION_RECEIVE_TIMEOUT:
02197         TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
02198 
02199         if (size != sizeof(DWORD))
02200             return ERROR_INVALID_PARAMETER;
02201 
02202         if(!req->netconn) {
02203             FIXME("unsupported without active connection\n");
02204             return ERROR_SUCCESS;
02205         }
02206 
02207         return NETCON_set_timeout(req->netconn, option == INTERNET_OPTION_SEND_TIMEOUT,
02208                     *(DWORD*)buffer);
02209 
02210     case INTERNET_OPTION_USERNAME:
02211         HeapFree(GetProcessHeap(), 0, req->session->userName);
02212         if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
02213         return ERROR_SUCCESS;
02214 
02215     case INTERNET_OPTION_PASSWORD:
02216         HeapFree(GetProcessHeap(), 0, req->session->password);
02217         if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
02218         return ERROR_SUCCESS;
02219     case INTERNET_OPTION_HTTP_DECODING:
02220         if(size != sizeof(BOOL))
02221             return ERROR_INVALID_PARAMETER;
02222         req->decoding = *(BOOL*)buffer;
02223         return ERROR_SUCCESS;
02224     }
02225 
02226     return ERROR_INTERNET_INVALID_OPTION;
02227 }
02228 
02229 /* read some more data into the read buffer (the read section must be held) */
02230 static DWORD read_more_data( http_request_t *req, int maxlen )
02231 {
02232     DWORD res;
02233     int len;
02234 
02235     if (req->read_pos)
02236     {
02237         /* move existing data to the start of the buffer */
02238         if(req->read_size)
02239             memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
02240         req->read_pos = 0;
02241     }
02242 
02243     if (maxlen == -1) maxlen = sizeof(req->read_buf);
02244 
02245     res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
02246                        maxlen - req->read_size, 0, &len );
02247     if(res == ERROR_SUCCESS)
02248         req->read_size += len;
02249 
02250     return res;
02251 }
02252 
02253 /* remove some amount of data from the read buffer (the read section must be held) */
02254 static void remove_data( http_request_t *req, int count )
02255 {
02256     if (!(req->read_size -= count)) req->read_pos = 0;
02257     else req->read_pos += count;
02258 }
02259 
02260 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
02261 {
02262     int count, bytes_read, pos = 0;
02263     DWORD res;
02264 
02265     EnterCriticalSection( &req->read_section );
02266     for (;;)
02267     {
02268         BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
02269 
02270         if (eol)
02271         {
02272             count = eol - (req->read_buf + req->read_pos);
02273             bytes_read = count + 1;
02274         }
02275         else count = bytes_read = req->read_size;
02276 
02277         count = min( count, *len - pos );
02278         memcpy( buffer + pos, req->read_buf + req->read_pos, count );
02279         pos += count;
02280         remove_data( req, bytes_read );
02281         if (eol) break;
02282 
02283         if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
02284         {
02285             *len = 0;
02286             TRACE( "returning empty string %u\n", res);
02287             LeaveCriticalSection( &req->read_section );
02288             INTERNET_SetLastError(res);
02289             return FALSE;
02290         }
02291     }
02292     LeaveCriticalSection( &req->read_section );
02293 
02294     if (pos < *len)
02295     {
02296         if (pos && buffer[pos - 1] == '\r') pos--;
02297         *len = pos + 1;
02298     }
02299     buffer[*len - 1] = 0;
02300     TRACE( "returning %s\n", debugstr_a(buffer));
02301     return TRUE;
02302 }
02303 
02304 /* check if we have reached the end of the data to read (the read section must be held) */
02305 static BOOL end_of_read_data( http_request_t *req )
02306 {
02307     return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
02308 }
02309 
02310 /* fetch some more data into the read buffer (the read section must be held) */
02311 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode, DWORD *read_bytes)
02312 {
02313     DWORD res, read=0;
02314 
02315     if(req->read_size == sizeof(req->read_buf))
02316         return ERROR_SUCCESS;
02317 
02318     if(req->read_pos) {
02319         if(req->read_size)
02320             memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
02321         req->read_pos = 0;
02322     }
02323 
02324     res = req->data_stream->vtbl->read(req->data_stream, req, req->read_buf+req->read_size,
02325             sizeof(req->read_buf)-req->read_size, &read, read_mode);
02326     req->read_size += read;
02327 
02328     TRACE("read %u bytes, read_size %u\n", read, req->read_size);
02329     if(read_bytes)
02330         *read_bytes = read;
02331     return res;
02332 }
02333 
02334 /* return the size of data available to be read immediately (the read section must be held) */
02335 static DWORD get_avail_data( http_request_t *req )
02336 {
02337     return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
02338 }
02339 
02340 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
02341 {
02342     netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
02343     DWORD avail = 0;
02344 
02345     if(req->netconn)
02346         NETCON_query_data_available(req->netconn, &avail);
02347     return netconn_stream->content_length == ~0u
02348         ? avail
02349         : min(avail, netconn_stream->content_length-netconn_stream->content_read);
02350 }
02351 
02352 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
02353 {
02354     netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
02355     return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
02356 }
02357 
02358 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
02359         DWORD *read, read_mode_t read_mode)
02360 {
02361     netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
02362     int len = 0;
02363 
02364     size = min(size, netconn_stream->content_length-netconn_stream->content_read);
02365 
02366     if(read_mode == READMODE_NOBLOCK)
02367         size = min(size, netconn_get_avail_data(stream, req));
02368 
02369     if(size && req->netconn) {
02370         if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
02371             len = 0;
02372     }
02373 
02374     netconn_stream->content_read += *read = len;
02375     TRACE("read %u bytes\n", len);
02376     return ERROR_SUCCESS;
02377 }
02378 
02379 static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
02380 {
02381     netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
02382     BYTE buf[1024];
02383     DWORD avail;
02384     int len;
02385 
02386     if(netconn_end_of_data(stream, req))
02387         return TRUE;
02388 
02389     do {
02390         avail = netconn_get_avail_data(stream, req);
02391         if(!avail)
02392             return FALSE;
02393 
02394         if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
02395             return FALSE;
02396 
02397         netconn_stream->content_read += len;
02398     }while(netconn_stream->content_read < netconn_stream->content_length);
02399 
02400     return TRUE;
02401 }
02402 
02403 static void netconn_destroy(data_stream_t *stream)
02404 {
02405 }
02406 
02407 static const data_stream_vtbl_t netconn_stream_vtbl = {
02408     netconn_get_avail_data,
02409     netconn_end_of_data,
02410     netconn_read,
02411     netconn_drain_content,
02412     netconn_destroy
02413 };
02414 
02415 /* read some more data into the read buffer (the read section must be held) */
02416 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
02417 {
02418     DWORD res;
02419     int len;
02420 
02421     if (stream->buf_pos)
02422     {
02423         /* move existing data to the start of the buffer */
02424         if(stream->buf_size)
02425             memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
02426         stream->buf_pos = 0;
02427     }
02428 
02429     if (maxlen == -1) maxlen = sizeof(stream->buf);
02430 
02431     res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
02432                        maxlen - stream->buf_size, 0, &len );
02433     if(res == ERROR_SUCCESS)
02434         stream->buf_size += len;
02435 
02436     return res;
02437 }
02438 
02439 /* remove some amount of data from the read buffer (the read section must be held) */
02440 static void remove_chunked_data(chunked_stream_t *stream, int count)
02441 {
02442     if (!(stream->buf_size -= count)) stream->buf_pos = 0;
02443     else stream->buf_pos += count;
02444 }
02445 
02446 /* discard data contents until we reach end of line (the read section must be held) */
02447 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
02448 {
02449     DWORD res;
02450 
02451     do
02452     {
02453         BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
02454         if (eol)
02455         {
02456             remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
02457             break;
02458         }
02459         stream->buf_pos = stream->buf_size = 0;  /* discard everything */
02460         if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
02461     } while (stream->buf_size);
02462     return ERROR_SUCCESS;
02463 }
02464 
02465 /* read the size of the next chunk (the read section must be held) */
02466 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
02467 {
02468     /* TODOO */
02469     DWORD chunk_size = 0, res;
02470 
02471     if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
02472         return res;
02473 
02474     for (;;)
02475     {
02476         while (stream->buf_size)
02477         {
02478             char ch = stream->buf[stream->buf_pos];
02479             if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
02480             else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
02481             else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
02482             else if (ch == ';' || ch == '\r' || ch == '\n')
02483             {
02484                 TRACE( "reading %u byte chunk\n", chunk_size );
02485                 stream->chunk_size = chunk_size;
02486                 req->contentLength += chunk_size;
02487                 return discard_chunked_eol(stream, req);
02488             }
02489             remove_chunked_data(stream, 1);
02490         }
02491         if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
02492         if (!stream->buf_size)
02493         {
02494             stream->chunk_size = 0;
02495             return ERROR_SUCCESS;
02496         }
02497     }
02498 }
02499 
02500 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
02501 {
02502     /* Allow reading only from read buffer */
02503     return 0;
02504 }
02505 
02506 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
02507 {
02508     chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
02509     return !chunked_stream->chunk_size;
02510 }
02511 
02512 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
02513         DWORD *read, read_mode_t read_mode)
02514 {
02515     chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
02516     DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
02517 
02518     if(chunked_stream->chunk_size == ~0u) {
02519         res = start_next_chunk(chunked_stream, req);
02520         if(res != ERROR_SUCCESS)
02521             return res;
02522     }
02523 
02524     while(size && chunked_stream->chunk_size) {
02525         if(chunked_stream->buf_size) {
02526             read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
02527 
02528             /* this could block */
02529             if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
02530                 break;
02531 
02532             memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
02533             remove_chunked_data(chunked_stream, read_bytes);
02534         }else {
02535             read_bytes = min(size, chunked_stream->chunk_size);
02536 
02537             if(read_mode == READMODE_NOBLOCK) {
02538                 DWORD avail;
02539 
02540                 if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
02541                     break;
02542                 if(read_bytes > avail)
02543                     read_bytes = avail;
02544 
02545                 /* this could block */
02546                 if(read_bytes == chunked_stream->chunk_size)
02547                     break;
02548             }
02549 
02550             res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
02551             if(res != ERROR_SUCCESS)
02552                 break;
02553         }
02554 
02555         chunked_stream->chunk_size -= read_bytes;
02556         size -= read_bytes;
02557         ret_read += read_bytes;
02558         if(!chunked_stream->chunk_size) {
02559             assert(read_mode != READMODE_NOBLOCK);
02560             res = start_next_chunk(chunked_stream, req);
02561             if(res != ERROR_SUCCESS)
02562                 break;
02563         }
02564 
02565         if(read_mode == READMODE_ASYNC)
02566             read_mode = READMODE_NOBLOCK;
02567     }
02568 
02569     TRACE("read %u bytes\n", ret_read);
02570     *read = ret_read;
02571     return res;
02572 }
02573 
02574 static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
02575 {
02576     chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
02577 
02578     /* FIXME: we can do better */
02579     return !chunked_stream->chunk_size;
02580 }
02581 
02582 static void chunked_destroy(data_stream_t *stream)
02583 {
02584     chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
02585     heap_free(chunked_stream);
02586 }
02587 
02588 static const data_stream_vtbl_t chunked_stream_vtbl = {
02589     chunked_get_avail_data,
02590     chunked_end_of_data,
02591     chunked_read,
02592     chunked_drain_content,
02593     chunked_destroy
02594 };
02595 
02596 /* set the request content length based on the headers */
02597 static DWORD set_content_length(http_request_t *request, DWORD status_code)
02598 {
02599     static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
02600     WCHAR encoding[20];
02601     DWORD size;
02602 
02603     if(status_code == HTTP_STATUS_NO_CONTENT) {
02604         request->contentLength = request->netconn_stream.content_length = 0;
02605         return ERROR_SUCCESS;
02606     }
02607 
02608     size = sizeof(request->contentLength);
02609     if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
02610                             &request->contentLength, &size, NULL) != ERROR_SUCCESS)
02611         request->contentLength = ~0u;
02612     request->netconn_stream.content_length = request->contentLength;
02613     request->netconn_stream.content_read = request->read_size;
02614 
02615     size = sizeof(encoding);
02616     if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
02617         !strcmpiW(encoding, szChunked))
02618     {
02619         chunked_stream_t *chunked_stream;
02620 
02621         chunked_stream = heap_alloc(sizeof(*chunked_stream));
02622         if(!chunked_stream)
02623             return ERROR_OUTOFMEMORY;
02624 
02625         chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
02626         chunked_stream->buf_size = chunked_stream->buf_pos = 0;
02627         chunked_stream->chunk_size = ~0u;
02628 
02629         if(request->read_size) {
02630             memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
02631             chunked_stream->buf_size = request->read_size;
02632             request->read_size = request->read_pos = 0;
02633         }
02634 
02635         request->data_stream = &chunked_stream->data_stream;
02636         request->contentLength = ~0u;
02637         request->read_chunked = TRUE;
02638     }
02639 
02640     if(request->decoding) {
02641         int encoding_idx;
02642 
02643         static const WCHAR gzipW[] = {'g','z','i','p',0};
02644 
02645         encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
02646         if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
02647             return init_gzip_stream(request);
02648     }
02649 
02650     return ERROR_SUCCESS;
02651 }
02652 
02653 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
02654 {
02655     INTERNET_ASYNC_RESULT iar;
02656     DWORD res, read = 0;
02657     read_mode_t mode;
02658 
02659     TRACE("%p\n", req);
02660 
02661     EnterCriticalSection( &req->read_section );
02662 
02663     mode = first_notif && req->read_size ? READMODE_NOBLOCK : READMODE_ASYNC;
02664     res = refill_read_buffer(req, mode, &read);
02665     if(res == ERROR_SUCCESS) {
02666         iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
02667         iar.dwError = first_notif ? 0 : get_avail_data(req);
02668     }else {
02669         iar.dwResult = 0;
02670         iar.dwError = res;
02671     }
02672 
02673     LeaveCriticalSection( &req->read_section );
02674 
02675     if(res != ERROR_SUCCESS || (mode != READMODE_NOBLOCK && !read)) {
02676         WARN("res %u read %u, closing connection\n", res, read);
02677         http_release_netconn(req, FALSE);
02678     }
02679 
02680     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
02681                           sizeof(INTERNET_ASYNC_RESULT));
02682 }
02683 
02684 /* read data from the http connection (the read section must be held) */
02685 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
02686 {
02687     DWORD current_read = 0, ret_read = 0;
02688     read_mode_t read_mode;
02689     DWORD res = ERROR_SUCCESS;
02690 
02691     read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
02692 
02693     EnterCriticalSection( &req->read_section );
02694 
02695     if(req->read_size) {
02696         ret_read = min(size, req->read_size);
02697         memcpy(buffer, req->read_buf+req->read_pos, ret_read);
02698         req->read_size -= ret_read;
02699         req->read_pos += ret_read;
02700         if(read_mode == READMODE_ASYNC)
02701             read_mode = READMODE_NOBLOCK;
02702     }
02703 
02704     if(ret_read < size) {
02705         res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, read_mode);
02706         ret_read += current_read;
02707     }
02708 
02709     LeaveCriticalSection( &req->read_section );
02710 
02711     *read = ret_read;
02712     TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
02713 
02714     if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
02715         BOOL res;
02716         DWORD written;
02717 
02718         res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
02719         if(!res)
02720             WARN("WriteFile failed: %u\n", GetLastError());
02721     }
02722 
02723     if(size && !ret_read)
02724         http_release_netconn(req, res == ERROR_SUCCESS);
02725 
02726     return res;
02727 }
02728 
02729 
02730 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
02731 {
02732     http_request_t *req = (http_request_t*)hdr;
02733     DWORD res;
02734 
02735     EnterCriticalSection( &req->read_section );
02736     if(hdr->dwError == INTERNET_HANDLE_IN_USE)
02737         hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
02738 
02739     res = HTTPREQ_Read(req, buffer, size, read, TRUE);
02740     if(res == ERROR_SUCCESS)
02741         res = hdr->dwError;
02742     LeaveCriticalSection( &req->read_section );
02743 
02744     return res;
02745 }
02746 
02747 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
02748 {
02749     struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
02750     http_request_t *req = (http_request_t*)workRequest->hdr;
02751     INTERNET_ASYNC_RESULT iar;
02752     DWORD res;
02753 
02754     TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
02755 
02756     res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
02757             data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
02758 
02759     iar.dwResult = res == ERROR_SUCCESS;
02760     iar.dwError = res;
02761 
02762     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
02763                           INTERNET_STATUS_REQUEST_COMPLETE, &iar,
02764                           sizeof(INTERNET_ASYNC_RESULT));
02765 }
02766 
02767 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
02768         DWORD flags, DWORD_PTR context)
02769 {
02770     http_request_t *req = (http_request_t*)hdr;
02771     DWORD res, size, read, error = ERROR_SUCCESS;
02772 
02773     if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
02774         FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
02775 
02776     if (buffers->dwStructSize != sizeof(*buffers))
02777         return ERROR_INVALID_PARAMETER;
02778 
02779     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
02780 
02781     if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
02782     {
02783         WORKREQUEST workRequest;
02784 
02785         if (TryEnterCriticalSection( &req->read_section ))
02786         {
02787             if (get_avail_data(req))
02788             {
02789                 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
02790                                    &buffers->dwBufferLength, FALSE);
02791                 size = buffers->dwBufferLength;
02792                 LeaveCriticalSection( &req->read_section );
02793                 goto done;
02794             }
02795             LeaveCriticalSection( &req->read_section );
02796         }
02797 
02798         workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
02799         workRequest.hdr = WININET_AddRef(&req->hdr);
02800         workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
02801 
02802         INTERNET_AsyncCall(&workRequest);
02803 
02804         return ERROR_IO_PENDING;
02805     }
02806 
02807     read = 0;
02808     size = buffers->dwBufferLength;
02809 
02810     EnterCriticalSection( &req->read_section );
02811     if(hdr->dwError == ERROR_SUCCESS)
02812         hdr->dwError = INTERNET_HANDLE_IN_USE;
02813     else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
02814         hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
02815 
02816     while(1) {
02817         res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
02818                 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
02819         if(res != ERROR_SUCCESS)
02820             break;
02821 
02822         read += buffers->dwBufferLength;
02823         if(read == size || end_of_read_data(req))
02824             break;
02825 
02826         LeaveCriticalSection( &req->read_section );
02827 
02828         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
02829                 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
02830         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
02831                 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
02832 
02833         EnterCriticalSection( &req->read_section );
02834     }
02835 
02836     if(hdr->dwError == INTERNET_HANDLE_IN_USE)
02837         hdr->dwError = ERROR_SUCCESS;
02838     else
02839         error = hdr->dwError;
02840 
02841     LeaveCriticalSection( &req->read_section );
02842     size = buffers->dwBufferLength;
02843     buffers->dwBufferLength = read;
02844 
02845 done:
02846     if (res == ERROR_SUCCESS) {
02847         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
02848                 &size, sizeof(size));
02849     }
02850 
02851     return res==ERROR_SUCCESS ? error : res;
02852 }
02853 
02854 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
02855 {
02856     struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
02857     http_request_t *req = (http_request_t*)workRequest->hdr;
02858     INTERNET_ASYNC_RESULT iar;
02859     DWORD res;
02860 
02861     TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
02862 
02863     res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
02864             data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
02865 
02866     iar.dwResult = res == ERROR_SUCCESS;
02867     iar.dwError = res;
02868 
02869     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
02870                           INTERNET_STATUS_REQUEST_COMPLETE, &iar,
02871                           sizeof(INTERNET_ASYNC_RESULT));
02872 }
02873 
02874 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
02875         DWORD flags, DWORD_PTR context)
02876 {
02877 
02878     http_request_t *req = (http_request_t*)hdr;
02879     DWORD res, size, read, error = ERROR_SUCCESS;
02880 
02881     if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
02882         FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
02883 
02884     if (buffers->dwStructSize != sizeof(*buffers))
02885         return ERROR_INVALID_PARAMETER;
02886 
02887     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
02888 
02889     if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
02890     {
02891         WORKREQUEST workRequest;
02892 
02893         if (TryEnterCriticalSection( &req->read_section ))
02894         {
02895             if (get_avail_data(req))
02896             {
02897                 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
02898                                    &buffers->dwBufferLength, FALSE);
02899                 size = buffers->dwBufferLength;
02900                 LeaveCriticalSection( &req->read_section );
02901                 goto done;
02902             }
02903             LeaveCriticalSection( &req->read_section );
02904         }
02905 
02906         workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
02907         workRequest.hdr = WININET_AddRef(&req->hdr);
02908         workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
02909 
02910         INTERNET_AsyncCall(&workRequest);
02911 
02912         return ERROR_IO_PENDING;
02913     }
02914 
02915     read = 0;
02916     size = buffers->dwBufferLength;
02917 
02918     EnterCriticalSection( &req->read_section );
02919     if(hdr->dwError == ERROR_SUCCESS)
02920         hdr->dwError = INTERNET_HANDLE_IN_USE;
02921     else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
02922         hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
02923 
02924     while(1) {
02925         res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
02926                 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
02927         if(res != ERROR_SUCCESS)
02928             break;
02929 
02930         read += buffers->dwBufferLength;
02931         if(read == size || end_of_read_data(req))
02932             break;
02933 
02934         LeaveCriticalSection( &req->read_section );
02935 
02936         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
02937                 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
02938         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
02939                 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
02940 
02941         EnterCriticalSection( &req->read_section );
02942     }
02943 
02944     if(hdr->dwError == INTERNET_HANDLE_IN_USE)
02945         hdr->dwError = ERROR_SUCCESS;
02946     else
02947         error = hdr->dwError;
02948 
02949     LeaveCriticalSection( &req->read_section );
02950     size = buffers->dwBufferLength;
02951     buffers->dwBufferLength = read;
02952 
02953 done:
02954     if (res == ERROR_SUCCESS) {
02955         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
02956                 &size, sizeof(size));
02957     }
02958 
02959     return res==ERROR_SUCCESS ? error : res;
02960 }
02961 
02962 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
02963 {
02964     DWORD res;
02965     http_request_t *request = (http_request_t*)hdr;
02966 
02967     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
02968 
02969     *written = 0;
02970     res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
02971     if (res == ERROR_SUCCESS)
02972         request->bytesWritten += *written;
02973 
02974     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
02975     return res;
02976 }
02977 
02978 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
02979 {
02980     http_request_t *req = (http_request_t*)workRequest->hdr;
02981 
02982     HTTP_ReceiveRequestData(req, FALSE);
02983 }
02984 
02985 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
02986 {
02987     http_request_t *req = (http_request_t*)hdr;
02988 
02989     TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
02990 
02991     if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
02992     {
02993         WORKREQUEST workRequest;
02994 
02995         /* never wait, if we can't enter the section we queue an async request right away */
02996         if (TryEnterCriticalSection( &req->read_section ))
02997         {
02998             refill_read_buffer(req, READMODE_NOBLOCK, NULL);
02999             if ((*available = get_avail_data( req ))) goto done;
03000             if (end_of_read_data( req )) goto done;
03001             LeaveCriticalSection( &req->read_section );
03002         }
03003 
03004         workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
03005         workRequest.hdr = WININET_AddRef( &req->hdr );
03006 
03007         INTERNET_AsyncCall(&workRequest);
03008 
03009         return ERROR_IO_PENDING;
03010     }
03011 
03012     EnterCriticalSection( &req->read_section );
03013 
03014     if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
03015     {
03016         refill_read_buffer( req, READMODE_ASYNC, NULL );
03017         *available = get_avail_data( req );
03018     }
03019 
03020 done:
03021     LeaveCriticalSection( &req->read_section );
03022 
03023     TRACE( "returning %u\n", *available );
03024     return ERROR_SUCCESS;
03025 }
03026 
03027 static const object_vtbl_t HTTPREQVtbl = {
03028     HTTPREQ_Destroy,
03029     HTTPREQ_CloseConnection,
03030     HTTPREQ_QueryOption,
03031     HTTPREQ_SetOption,
03032     HTTPREQ_ReadFile,
03033     HTTPREQ_ReadFileExA,
03034     HTTPREQ_ReadFileExW,
03035     HTTPREQ_WriteFile,
03036     HTTPREQ_QueryDataAvailable,
03037     NULL
03038 };
03039 
03040 /***********************************************************************
03041  *           HTTP_HttpOpenRequestW (internal)
03042  *
03043  * Open a HTTP request handle
03044  *
03045  * RETURNS
03046  *    HINTERNET  a HTTP request handle on success
03047  *    NULL   on failure
03048  *
03049  */
03050 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
03051         LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
03052         LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
03053         DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
03054 {
03055     appinfo_t *hIC = session->appInfo;
03056     http_request_t *request;
03057     DWORD len, res = ERROR_SUCCESS;
03058 
03059     TRACE("-->\n");
03060 
03061     request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
03062     if(!request)
03063         return ERROR_OUTOFMEMORY;
03064 
03065     request->hdr.htype = WH_HHTTPREQ;
03066     request->hdr.dwFlags = dwFlags;
03067     request->hdr.dwContext = dwContext;
03068     request->contentLength = ~0u;
03069 
03070     request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
03071     request->data_stream = &request->netconn_stream.data_stream;
03072 
03073     InitializeCriticalSection( &request->read_section );
03074 
03075     WININET_AddRef( &session->hdr );
03076     request->session = session;
03077     list_add_head( &session->hdr.children, &request->hdr.entry );
03078 
03079     if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
03080         request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
03081     if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
03082         request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
03083 
03084     if (lpszObjectName && *lpszObjectName) {
03085         HRESULT rc;
03086 
03087         len = 0;
03088         rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
03089         if (rc != E_POINTER)
03090             len = strlenW(lpszObjectName)+1;
03091         request->path = heap_alloc(len*sizeof(WCHAR));
03092         rc = UrlEscapeW(lpszObjectName, request->path, &len,
03093                    URL_ESCAPE_SPACES_ONLY);
03094         if (rc != S_OK)
03095         {
03096             ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
03097             strcpyW(request->path,lpszObjectName);
03098         }
03099     }else {
03100         static const WCHAR slashW[] = {'/',0};
03101 
03102         request->path = heap_strdupW(slashW);
03103     }
03104 
03105     if (lpszReferrer && *lpszReferrer)
03106         HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
03107 
03108     if (lpszAcceptTypes)
03109     {
03110         int i;
03111         for (i = 0; lpszAcceptTypes[i]; i++)
03112         {
03113             if (!*lpszAcceptTypes[i]) continue;
03114             HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
03115                                HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
03116                                HTTP_ADDHDR_FLAG_REQ |
03117                                (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
03118         }
03119     }
03120 
03121     request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
03122     request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
03123 
03124     if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
03125         session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
03126         session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
03127     {
03128         WCHAR *host_name;
03129 
03130         static const WCHAR host_formatW[] = {'%','s',':','%','u',0};
03131 
03132         host_name = heap_alloc((strlenW(session->hostName) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR));
03133         if (!host_name) {
03134             res = ERROR_OUTOFMEMORY;
03135             goto lend;
03136         }
03137 
03138         sprintfW(host_name, host_formatW, session->hostName, session->hostPort);
03139         HTTP_ProcessHeader(request, hostW, host_name, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
03140         heap_free(host_name);
03141     }
03142     else
03143         HTTP_ProcessHeader(request, hostW, session->hostName,
03144                 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
03145 
03146     if (session->serverPort == INTERNET_INVALID_PORT_NUMBER)
03147         session->serverPort = (dwFlags & INTERNET_FLAG_SECURE ?
03148                         INTERNET_DEFAULT_HTTPS_PORT :
03149                         INTERNET_DEFAULT_HTTP_PORT);
03150 
03151     if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
03152         session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
03153                         INTERNET_DEFAULT_HTTPS_PORT :
03154                         INTERNET_DEFAULT_HTTP_PORT);
03155 
03156     if (hIC->proxy && hIC->proxy[0])
03157         HTTP_DealWithProxy( hIC, session, request );
03158 
03159     INTERNET_SendCallback(&session->hdr, dwContext,
03160                           INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
03161                           sizeof(HINTERNET));
03162 
03163 lend:
03164     TRACE("<-- %u (%p)\n", res, request);
03165 
03166     if(res != ERROR_SUCCESS) {
03167         WININET_Release( &request->hdr );
03168         *ret = NULL;
03169         return res;
03170     }
03171 
03172     *ret = request->hdr.hInternet;
03173     return ERROR_SUCCESS;
03174 }
03175 
03176 /***********************************************************************
03177  *           HttpOpenRequestW (WININET.@)
03178  *
03179  * Open a HTTP request handle
03180  *
03181  * RETURNS
03182  *    HINTERNET  a HTTP request handle on success
03183  *    NULL   on failure
03184  *
03185  */
03186 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
03187     LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
03188     LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
03189     DWORD dwFlags, DWORD_PTR dwContext)
03190 {
03191     http_session_t *session;
03192     HINTERNET handle = NULL;
03193     DWORD res;
03194 
03195     TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
03196           debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
03197           debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
03198           dwFlags, dwContext);
03199     if(lpszAcceptTypes!=NULL)
03200     {
03201         int i;
03202         for(i=0;lpszAcceptTypes[i]!=NULL;i++)
03203             TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
03204     }
03205 
03206     session = (http_session_t*) get_handle_object( hHttpSession );
03207     if (NULL == session ||  session->hdr.htype != WH_HHTTPSESSION)
03208     {
03209         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
03210         goto lend;
03211     }
03212 
03213     /*
03214      * My tests seem to show that the windows version does not
03215      * become asynchronous until after this point. And anyhow
03216      * if this call was asynchronous then how would you get the
03217      * necessary HINTERNET pointer returned by this function.
03218      *
03219      */
03220     res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
03221                                 lpszVersion, lpszReferrer, lpszAcceptTypes,
03222                                 dwFlags, dwContext, &handle);
03223 lend:
03224     if( session )
03225         WININET_Release( &session->hdr );
03226     TRACE("returning %p\n", handle);
03227     if(res != ERROR_SUCCESS)
03228         SetLastError(res);
03229     return handle;
03230 }
03231 
03232 static const LPCWSTR header_lookup[] = {
03233     szMime_Version,     /* HTTP_QUERY_MIME_VERSION = 0 */
03234     szContent_Type,     /* HTTP_QUERY_CONTENT_TYPE = 1 */
03235     szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
03236     szContent_ID,       /* HTTP_QUERY_CONTENT_ID = 3 */
03237     NULL,           /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
03238     szContent_Length,       /* HTTP_QUERY_CONTENT_LENGTH =  5 */
03239     szContent_Language,     /* HTTP_QUERY_CONTENT_LANGUAGE =  6 */
03240     szAllow,            /* HTTP_QUERY_ALLOW = 7 */
03241     szPublic,           /* HTTP_QUERY_PUBLIC = 8 */
03242     szDate,         /* HTTP_QUERY_DATE = 9 */
03243     szExpires,          /* HTTP_QUERY_EXPIRES = 10 */
03244     szLast_Modified,        /* HTTP_QUERY_LAST_MODIFIED = 11 */
03245     NULL,           /* HTTP_QUERY_MESSAGE_ID = 12 */
03246     szURI,          /* HTTP_QUERY_URI = 13 */
03247     szFrom,         /* HTTP_QUERY_DERIVED_FROM = 14 */
03248     NULL,           /* HTTP_QUERY_COST = 15 */
03249     NULL,           /* HTTP_QUERY_LINK = 16 */
03250     szPragma,           /* HTTP_QUERY_PRAGMA = 17 */
03251     NULL,           /* HTTP_QUERY_VERSION = 18 */
03252     szStatus,           /* HTTP_QUERY_STATUS_CODE = 19 */
03253     NULL,           /* HTTP_QUERY_STATUS_TEXT = 20 */
03254     NULL,           /* HTTP_QUERY_RAW_HEADERS = 21 */
03255     NULL,           /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
03256     szConnection,       /* HTTP_QUERY_CONNECTION = 23 */
03257     szAccept,           /* HTTP_QUERY_ACCEPT = 24 */
03258     szAccept_Charset,       /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
03259     szAccept_Encoding,      /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
03260     szAccept_Language,      /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
03261     szAuthorization,        /* HTTP_QUERY_AUTHORIZATION = 28 */
03262     szContent_Encoding,     /* HTTP_QUERY_CONTENT_ENCODING = 29 */
03263     NULL,           /* HTTP_QUERY_FORWARDED = 30 */
03264     NULL,           /* HTTP_QUERY_FROM = 31 */
03265     szIf_Modified_Since,    /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
03266     szLocation,         /* HTTP_QUERY_LOCATION = 33 */
03267     NULL,           /* HTTP_QUERY_ORIG_URI = 34 */
03268     szReferer,          /* HTTP_QUERY_REFERER = 35 */
03269     szRetry_After,      /* HTTP_QUERY_RETRY_AFTER = 36 */
03270     szServer,           /* HTTP_QUERY_SERVER = 37 */
03271     NULL,           /* HTTP_TITLE = 38 */
03272     szUser_Agent,       /* HTTP_QUERY_USER_AGENT = 39 */
03273     szWWW_Authenticate,     /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
03274     szProxy_Authenticate,   /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
03275     szAccept_Ranges,        /* HTTP_QUERY_ACCEPT_RANGES = 42 */
03276     szSet_Cookie,       /* HTTP_QUERY_SET_COOKIE = 43 */
03277     szCookie,           /* HTTP_QUERY_COOKIE = 44 */
03278     NULL,           /* HTTP_QUERY_REQUEST_METHOD = 45 */
03279     NULL,           /* HTTP_QUERY_REFRESH = 46 */
03280     NULL,           /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
03281     szAge,          /* HTTP_QUERY_AGE = 48 */
03282     szCache_Control,        /* HTTP_QUERY_CACHE_CONTROL = 49 */
03283     szContent_Base,     /* HTTP_QUERY_CONTENT_BASE = 50 */
03284     szContent_Location,     /* HTTP_QUERY_CONTENT_LOCATION = 51 */
03285     szContent_MD5,      /* HTTP_QUERY_CONTENT_MD5 = 52 */
03286     szContent_Range,        /* HTTP_QUERY_CONTENT_RANGE = 53 */
03287     szETag,         /* HTTP_QUERY_ETAG = 54 */
03288     hostW,          /* HTTP_QUERY_HOST = 55 */
03289     szIf_Match,         /* HTTP_QUERY_IF_MATCH = 56 */
03290     szIf_None_Match,        /* HTTP_QUERY_IF_NONE_MATCH = 57 */
03291     szIf_Range,         /* HTTP_QUERY_IF_RANGE = 58 */
03292     szIf_Unmodified_Since,  /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
03293     szMax_Forwards,     /* HTTP_QUERY_MAX_FORWARDS = 60 */
03294     szProxy_Authorization,  /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
03295     szRange,            /* HTTP_QUERY_RANGE = 62 */
03296     szTransfer_Encoding,    /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
03297     szUpgrade,          /* HTTP_QUERY_UPGRADE = 64 */
03298     szVary,         /* HTTP_QUERY_VARY = 65 */
03299     szVia,          /* HTTP_QUERY_VIA = 66 */
03300     szWarning,          /* HTTP_QUERY_WARNING = 67 */
03301     szExpect,           /* HTTP_QUERY_EXPECT = 68 */
03302     szProxy_Connection,     /* HTTP_QUERY_PROXY_CONNECTION = 69 */
03303     szUnless_Modified_Since,    /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
03304 };
03305 
03306 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
03307 
03308 /***********************************************************************
03309  *           HTTP_HttpQueryInfoW (internal)
03310  */
03311 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
03312         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
03313 {
03314     LPHTTPHEADERW lphttpHdr = NULL;
03315     BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
03316     INT requested_index = lpdwIndex ? *lpdwIndex : 0;
03317     DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
03318     INT index = -1;
03319 
03320     /* Find requested header structure */
03321     switch (level)
03322     {
03323     case HTTP_QUERY_CUSTOM:
03324         if (!lpBuffer) return ERROR_INVALID_PARAMETER;
03325         index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
03326         break;
03327     case HTTP_QUERY_RAW_HEADERS_CRLF:
03328         {
03329             LPWSTR headers;
03330             DWORD len = 0;
03331             DWORD res = ERROR_INVALID_PARAMETER;
03332 
03333             if (request_only)
03334                 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
03335             else
03336                 headers = request->rawHeaders;
03337 
03338             if (headers)
03339                 len = strlenW(headers) * sizeof(WCHAR);
03340 
03341             if (len + sizeof(WCHAR) > *lpdwBufferLength)
03342             {
03343                 len += sizeof(WCHAR);
03344                 res = ERROR_INSUFFICIENT_BUFFER;
03345             }
03346             else if (lpBuffer)
03347             {
03348                 if (headers)
03349                     memcpy(lpBuffer, headers, len + sizeof(WCHAR));
03350                 else
03351                 {
03352                     len = strlenW(szCrLf) * sizeof(WCHAR);
03353                     memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
03354                 }
03355                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
03356                 res = ERROR_SUCCESS;
03357             }
03358             *lpdwBufferLength = len;
03359 
03360             if (request_only)
03361                 HeapFree(GetProcessHeap(), 0, headers);
03362             return res;
03363         }
03364     case HTTP_QUERY_RAW_HEADERS:
03365         {
03366             LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
03367             DWORD i, size = 0;
03368             LPWSTR pszString = lpBuffer;
03369 
03370             for (i = 0; ppszRawHeaderLines[i]; i++)
03371                 size += strlenW(ppszRawHeaderLines[i]) + 1;
03372 
03373             if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
03374             {
03375                 HTTP_FreeTokens(ppszRawHeaderLines);
03376                 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
03377                 return ERROR_INSUFFICIENT_BUFFER;
03378             }
03379             if (pszString)
03380             {
03381                 for (i = 0; ppszRawHeaderLines[i]; i++)
03382                 {
03383                     DWORD len = strlenW(ppszRawHeaderLines[i]);
03384                     memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
03385                     pszString += len+1;
03386                 }
03387                 *pszString = '\0';
03388                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
03389             }
03390             *lpdwBufferLength = size * sizeof(WCHAR);
03391             HTTP_FreeTokens(ppszRawHeaderLines);
03392 
03393             return ERROR_SUCCESS;
03394         }
03395     case HTTP_QUERY_STATUS_TEXT:
03396         if (request->statusText)
03397         {
03398             DWORD len = strlenW(request->statusText);
03399             if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
03400             {
03401                 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
03402                 return ERROR_INSUFFICIENT_BUFFER;
03403             }
03404             if (lpBuffer)
03405             {
03406                 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
03407                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
03408             }
03409             *lpdwBufferLength = len * sizeof(WCHAR);
03410             return ERROR_SUCCESS;
03411         }
03412         break;
03413     case HTTP_QUERY_VERSION:
03414         if (request->version)
03415         {
03416             DWORD len = strlenW(request->version);
03417             if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
03418             {
03419                 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
03420                 return ERROR_INSUFFICIENT_BUFFER;
03421             }
03422             if (lpBuffer)
03423             {
03424                 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
03425                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
03426             }
03427             *lpdwBufferLength = len * sizeof(WCHAR);
03428             return ERROR_SUCCESS;
03429         }
03430         break;
03431     case HTTP_QUERY_CONTENT_ENCODING:
03432         index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
03433                 requested_index,request_only);
03434         break;
03435     default:
03436         assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
03437 
03438         if (level < LAST_TABLE_HEADER && header_lookup[level])
03439             index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
03440                                               requested_index,request_only);
03441     }
03442 
03443     if (index >= 0)
03444         lphttpHdr = &request->custHeaders[index];
03445 
03446     /* Ensure header satisfies requested attributes */
03447     if (!lphttpHdr ||
03448         ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
03449          (~lphttpHdr->wFlags & HDR_ISREQUEST)))
03450     {
03451         return ERROR_HTTP_HEADER_NOT_FOUND;
03452     }
03453 
03454     if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
03455 
03456     /* coalesce value to requested type */
03457     if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
03458     {
03459         *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
03460         TRACE(" returning number: %d\n", *(int *)lpBuffer);
03461      }
03462     else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
03463     {
03464         time_t tmpTime;
03465         struct tm tmpTM;
03466         SYSTEMTIME *STHook;
03467 
03468         tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
03469 
03470         tmpTM = *gmtime(&tmpTime);
03471         STHook = (SYSTEMTIME *)lpBuffer;
03472         STHook->wDay = tmpTM.tm_mday;
03473         STHook->wHour = tmpTM.tm_hour;
03474         STHook->wMilliseconds = 0;
03475         STHook->wMinute = tmpTM.tm_min;
03476         STHook->wDayOfWeek = tmpTM.tm_wday;
03477         STHook->wMonth = tmpTM.tm_mon + 1;
03478         STHook->wSecond = tmpTM.tm_sec;
03479         STHook->wYear = tmpTM.tm_year;
03480 
03481         TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
03482               STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
03483               STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
03484     }
03485     else if (lphttpHdr->lpszValue)
03486     {
03487         DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
03488 
03489         if (len > *lpdwBufferLength)
03490         {
03491             *lpdwBufferLength = len;
03492             return ERROR_INSUFFICIENT_BUFFER;
03493         }
03494         if (lpBuffer)
03495         {
03496             memcpy(lpBuffer, lphttpHdr->lpszValue, len);
03497             TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
03498         }
03499         *lpdwBufferLength = len - sizeof(WCHAR);
03500     }
03501     return ERROR_SUCCESS;
03502 }
03503 
03504 /***********************************************************************
03505  *           HttpQueryInfoW (WININET.@)
03506  *
03507  * Queries for information about an HTTP request
03508  *
03509  * RETURNS
03510  *    TRUE  on success
03511  *    FALSE on failure
03512  *
03513  */
03514 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
03515         LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
03516 {
03517     http_request_t *request;
03518     DWORD res;
03519 
03520     if (TRACE_ON(wininet)) {
03521 #define FE(x) { x, #x }
03522     static const wininet_flag_info query_flags[] = {
03523         FE(HTTP_QUERY_MIME_VERSION),
03524         FE(HTTP_QUERY_CONTENT_TYPE),
03525         FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
03526         FE(HTTP_QUERY_CONTENT_ID),
03527         FE(HTTP_QUERY_CONTENT_DESCRIPTION),
03528         FE(HTTP_QUERY_CONTENT_LENGTH),
03529         FE(HTTP_QUERY_CONTENT_LANGUAGE),
03530         FE(HTTP_QUERY_ALLOW),
03531         FE(HTTP_QUERY_PUBLIC),
03532         FE(HTTP_QUERY_DATE),
03533         FE(HTTP_QUERY_EXPIRES),
03534         FE(HTTP_QUERY_LAST_MODIFIED),
03535         FE(HTTP_QUERY_MESSAGE_ID),
03536         FE(HTTP_QUERY_URI),
03537         FE(HTTP_QUERY_DERIVED_FROM),
03538         FE(HTTP_QUERY_COST),
03539         FE(HTTP_QUERY_LINK),
03540         FE(HTTP_QUERY_PRAGMA),
03541         FE(HTTP_QUERY_VERSION),
03542         FE(HTTP_QUERY_STATUS_CODE),
03543         FE(HTTP_QUERY_STATUS_TEXT),
03544         FE(HTTP_QUERY_RAW_HEADERS),
03545         FE(HTTP_QUERY_RAW_HEADERS_CRLF),
03546         FE(HTTP_QUERY_CONNECTION),
03547         FE(HTTP_QUERY_ACCEPT),
03548         FE(HTTP_QUERY_ACCEPT_CHARSET),
03549         FE(HTTP_QUERY_ACCEPT_ENCODING),
03550         FE(HTTP_QUERY_ACCEPT_LANGUAGE),
03551         FE(HTTP_QUERY_AUTHORIZATION),
03552         FE(HTTP_QUERY_CONTENT_ENCODING),
03553         FE(HTTP_QUERY_FORWARDED),
03554         FE(HTTP_QUERY_FROM),
03555         FE(HTTP_QUERY_IF_MODIFIED_SINCE),
03556         FE(HTTP_QUERY_LOCATION),
03557         FE(HTTP_QUERY_ORIG_URI),
03558         FE(HTTP_QUERY_REFERER),
03559         FE(HTTP_QUERY_RETRY_AFTER),
03560         FE(HTTP_QUERY_SERVER),
03561         FE(HTTP_QUERY_TITLE),
03562         FE(HTTP_QUERY_USER_AGENT),
03563         FE(HTTP_QUERY_WWW_AUTHENTICATE),
03564         FE(HTTP_QUERY_PROXY_AUTHENTICATE),
03565         FE(HTTP_QUERY_ACCEPT_RANGES),
03566         FE(HTTP_QUERY_SET_COOKIE),
03567         FE(HTTP_QUERY_COOKIE),
03568         FE(HTTP_QUERY_REQUEST_METHOD),
03569         FE(HTTP_QUERY_REFRESH),
03570         FE(HTTP_QUERY_CONTENT_DISPOSITION),
03571         FE(HTTP_QUERY_AGE),
03572         FE(HTTP_QUERY_CACHE_CONTROL),
03573         FE(HTTP_QUERY_CONTENT_BASE),
03574         FE(HTTP_QUERY_CONTENT_LOCATION),
03575         FE(HTTP_QUERY_CONTENT_MD5),
03576         FE(HTTP_QUERY_CONTENT_RANGE),
03577         FE(HTTP_QUERY_ETAG),
03578         FE(HTTP_QUERY_HOST),
03579         FE(HTTP_QUERY_IF_MATCH),
03580         FE(HTTP_QUERY_IF_NONE_MATCH),
03581         FE(HTTP_QUERY_IF_RANGE),
03582         FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
03583         FE(HTTP_QUERY_MAX_FORWARDS),
03584         FE(HTTP_QUERY_PROXY_AUTHORIZATION),
03585         FE(HTTP_QUERY_RANGE),
03586         FE(HTTP_QUERY_TRANSFER_ENCODING),
03587         FE(HTTP_QUERY_UPGRADE),
03588         FE(HTTP_QUERY_VARY),
03589         FE(HTTP_QUERY_VIA),
03590         FE(HTTP_QUERY_WARNING),
03591         FE(HTTP_QUERY_CUSTOM)
03592     };
03593     static const wininet_flag_info modifier_flags[] = {
03594         FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
03595         FE(HTTP_QUERY_FLAG_SYSTEMTIME),
03596         FE(HTTP_QUERY_FLAG_NUMBER),
03597         FE(HTTP_QUERY_FLAG_COALESCE)
03598     };
03599 #undef FE
03600     DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
03601     DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
03602     DWORD i;
03603 
03604     TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
03605     TRACE("  Attribute:");
03606     for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
03607         if (query_flags[i].val == info) {
03608         TRACE(" %s", query_flags[i].name);
03609         break;
03610         }
03611     }
03612     if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
03613         TRACE(" Unknown (%08x)", info);
03614     }
03615 
03616     TRACE(" Modifier:");
03617     for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
03618         if (modifier_flags[i].val & info_mod) {
03619         TRACE(" %s", modifier_flags[i].name);
03620         info_mod &= ~ modifier_flags[i].val;
03621         }
03622     }
03623     
03624     if (info_mod) {
03625         TRACE(" Unknown (%08x)", info_mod);
03626     }
03627     TRACE("\n");
03628     }
03629     
03630     request = (http_request_t*) get_handle_object( hHttpRequest );
03631     if (NULL == request ||  request->hdr.htype != WH_HHTTPREQ)
03632     {
03633         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
03634         goto lend;
03635     }
03636 
03637     if (lpBuffer == NULL)
03638         *lpdwBufferLength = 0;
03639     res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
03640                                lpBuffer, lpdwBufferLength, lpdwIndex);
03641 
03642 lend:
03643     if( request )
03644          WININET_Release( &request->hdr );
03645 
03646     TRACE("%u <--\n", res);
03647     if(res != ERROR_SUCCESS)
03648         SetLastError(res);
03649     return res == ERROR_SUCCESS;
03650 }
03651 
03652 /***********************************************************************
03653  *           HttpQueryInfoA (WININET.@)
03654  *
03655  * Queries for information about an HTTP request
03656  *
03657  * RETURNS
03658  *    TRUE  on success
03659  *    FALSE on failure
03660  *
03661  */
03662 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
03663     LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
03664 {
03665     BOOL result;
03666     DWORD len;
03667     WCHAR* bufferW;
03668 
03669     if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
03670        (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
03671     {
03672         return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
03673                                lpdwBufferLength, lpdwIndex );
03674     }
03675 
03676     if (lpBuffer)
03677     {
03678         DWORD alloclen;
03679         len = (*lpdwBufferLength)*sizeof(WCHAR);
03680         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
03681         {
03682             alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
03683             if (alloclen < len)
03684                 alloclen = len;
03685         }
03686         else
03687             alloclen = len;
03688         bufferW = heap_alloc(alloclen);
03689         /* buffer is in/out because of HTTP_QUERY_CUSTOM */
03690         if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
03691             MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
03692     } else
03693     {
03694         bufferW = NULL;
03695         len = 0;
03696     }
03697 
03698     result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
03699                            &len, lpdwIndex );
03700     if( result )
03701     {
03702         len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
03703                                      lpBuffer, *lpdwBufferLength, NULL, NULL );
03704         *lpdwBufferLength = len - 1;
03705 
03706         TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
03707     }
03708     else
03709         /* since the strings being returned from HttpQueryInfoW should be
03710          * only ASCII characters, it is reasonable to assume that all of
03711          * the Unicode characters can be reduced to a single byte */
03712         *lpdwBufferLength = len / sizeof(WCHAR);
03713 
03714     HeapFree(GetProcessHeap(), 0, bufferW );
03715 
03716     return result;
03717 }
03718 
03719 /***********************************************************************
03720  *           HTTP_GetRedirectURL (internal)
03721  */
03722 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
03723 {
03724     static WCHAR szHttp[] = {'h','t','t','p',0};
03725     static WCHAR szHttps[] = {'h','t','t','p','s',0};
03726     http_session_t *session = request->session;
03727     URL_COMPONENTSW urlComponents;
03728     DWORD url_length = 0;
03729     LPWSTR orig_url;
03730     LPWSTR combined_url;
03731 
03732     urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
03733     urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
03734     urlComponents.dwSchemeLength = 0;
03735     urlComponents.lpszHostName = session->hostName;
03736     urlComponents.dwHostNameLength = 0;
03737     urlComponents.nPort = session->hostPort;
03738     urlComponents.lpszUserName = session->userName;
03739     urlComponents.dwUserNameLength = 0;
03740     urlComponents.lpszPassword = NULL;
03741     urlComponents.dwPasswordLength = 0;
03742     urlComponents.lpszUrlPath = request->path;
03743     urlComponents.dwUrlPathLength = 0;
03744     urlComponents.lpszExtraInfo = NULL;
03745     urlComponents.dwExtraInfoLength = 0;
03746 
03747     if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
03748         (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
03749         return NULL;
03750 
03751     orig_url = heap_alloc(url_length);
03752 
03753     /* convert from bytes to characters */
03754     url_length = url_length / sizeof(WCHAR) - 1;
03755     if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
03756     {
03757         HeapFree(GetProcessHeap(), 0, orig_url);
03758         return NULL;
03759     }
03760 
03761     url_length = 0;
03762     if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
03763         (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
03764     {
03765         HeapFree(GetProcessHeap(), 0, orig_url);
03766         return NULL;
03767     }
03768     combined_url = heap_alloc(url_length * sizeof(WCHAR));
03769 
03770     if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
03771     {
03772         HeapFree(GetProcessHeap(), 0, orig_url);
03773         HeapFree(GetProcessHeap(), 0, combined_url);
03774         return NULL;
03775     }
03776     HeapFree(GetProcessHeap(), 0, orig_url);
03777     return combined_url;
03778 }
03779 
03780 
03781 /***********************************************************************
03782  *           HTTP_HandleRedirect (internal)
03783  */
03784 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
03785 {
03786     http_session_t *session = request->session;
03787     appinfo_t *hIC = session->appInfo;
03788     BOOL using_proxy = hIC->proxy && hIC->proxy[0];
03789     WCHAR path[INTERNET_MAX_URL_LENGTH];
03790     int index;
03791 
03792     if(lpszUrl[0]=='/')
03793     {
03794         /* if it's an absolute path, keep the same session info */
03795         lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
03796     }
03797     else
03798     {
03799         URL_COMPONENTSW urlComponents;
03800         WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
03801         static WCHAR szHttp[] = {'h','t','t','p',0};
03802         static WCHAR szHttps[] = {'h','t','t','p','s',0};
03803 
03804         userName[0] = 0;
03805         hostName[0] = 0;
03806         protocol[0] = 0;
03807 
03808         urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
03809         urlComponents.lpszScheme = protocol;
03810         urlComponents.dwSchemeLength = 32;
03811         urlComponents.lpszHostName = hostName;
03812         urlComponents.dwHostNameLength = MAXHOSTNAME;
03813         urlComponents.lpszUserName = userName;
03814         urlComponents.dwUserNameLength = 1024;
03815         urlComponents.lpszPassword = NULL;
03816         urlComponents.dwPasswordLength = 0;
03817         urlComponents.lpszUrlPath = path;
03818         urlComponents.dwUrlPathLength = 2048;
03819         urlComponents.lpszExtraInfo = NULL;
03820         urlComponents.dwExtraInfoLength = 0;
03821         if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
03822             return INTERNET_GetLastError();
03823 
03824         if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
03825             (request->hdr.dwFlags & INTERNET_FLAG_SECURE))
03826         {
03827             TRACE("redirect from secure page to non-secure page\n");
03828             /* FIXME: warn about from secure redirect to non-secure page */
03829             request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
03830         }
03831         if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
03832             !(request->hdr.dwFlags & INTERNET_FLAG_SECURE))
03833         {
03834             TRACE("redirect from non-secure page to secure page\n");
03835             /* FIXME: notify about redirect to secure page */
03836             request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
03837         }
03838 
03839         if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
03840         {
03841             if (lstrlenW(protocol)>4) /*https*/
03842                 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
03843             else /*http*/
03844                 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
03845         }
03846 
03847 #if 0
03848         /*
03849          * This upsets redirects to binary files on sourceforge.net 
03850          * and gives an html page instead of the target file
03851          * Examination of the HTTP request sent by native wininet.dll
03852          * reveals that it doesn't send a referrer in that case.
03853          * Maybe there's a flag that enables this, or maybe a referrer
03854          * shouldn't be added in case of a redirect.
03855          */
03856 
03857         /* consider the current host as the referrer */
03858         if (session->lpszServerName && *session->lpszServerName)
03859             HTTP_ProcessHeader(request, HTTP_REFERER, session->lpszServerName,
03860                            HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
03861                            HTTP_ADDHDR_FLAG_ADD_IF_NEW);
03862 #endif
03863         
03864         HeapFree(GetProcessHeap(), 0, session->hostName);
03865         if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
03866             urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
03867         {
03868             int len;
03869             static const WCHAR fmt[] = {'%','s',':','%','u',0};
03870             len = lstrlenW(hostName);
03871             len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
03872             session->hostName = heap_alloc(len*sizeof(WCHAR));
03873             sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
03874         }
03875         else
03876             session->hostName = heap_strdupW(hostName);
03877 
03878         HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
03879 
03880         HeapFree(GetProcessHeap(), 0, session->userName);
03881         session->userName = NULL;
03882         if (userName[0])
03883             session->userName = heap_strdupW(userName);
03884 
03885         reset_data_stream(request);
03886 
03887         if(!using_proxy) {
03888             if(strcmpiW(session->serverName, hostName)) {
03889                 HeapFree(GetProcessHeap(), 0, session->serverName);
03890                 session->serverName = heap_strdupW(hostName);
03891             }
03892             session->serverPort = urlComponents.nPort;
03893         }
03894     }
03895 
03896     HeapFree(GetProcessHeap(), 0, request->path);
03897     request->path=NULL;
03898     if (*path)
03899     {
03900         DWORD needed = 0;
03901         HRESULT rc;
03902 
03903         rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
03904         if (rc != E_POINTER)
03905             needed = strlenW(path)+1;
03906         request->path = heap_alloc(needed*sizeof(WCHAR));
03907         rc = UrlEscapeW(path, request->path, &needed,
03908                         URL_ESCAPE_SPACES_ONLY);
03909         if (rc != S_OK)
03910         {
03911             ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
03912             strcpyW(request->path,path);
03913         }
03914     }
03915 
03916     /* Remove custom content-type/length headers on redirects.  */
03917     index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
03918     if (0 <= index)
03919         HTTP_DeleteCustomHeader(request, index);
03920     index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
03921     if (0 <= index)
03922         HTTP_DeleteCustomHeader(request, index);
03923 
03924     return ERROR_SUCCESS;
03925 }
03926 
03927 /***********************************************************************
03928  *           HTTP_build_req (internal)
03929  *
03930  *  concatenate all the strings in the request together
03931  */
03932 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
03933 {
03934     LPCWSTR *t;
03935     LPWSTR str;
03936 
03937     for( t = list; *t ; t++  )
03938         len += strlenW( *t );
03939     len++;
03940 
03941     str = heap_alloc(len*sizeof(WCHAR));
03942     *str = 0;
03943 
03944     for( t = list; *t ; t++ )
03945         strcatW( str, *t );
03946 
03947     return str;
03948 }
03949 
03950 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
03951 {
03952     LPWSTR lpszPath;
03953     LPWSTR requestString;
03954     INT len;
03955     INT cnt;
03956     INT responseLen;
03957     char *ascii_req;
03958     DWORD res;
03959     static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
03960     static const WCHAR szFormat[] = {'%','s',':','%','u',0};
03961     http_session_t *session = request->session;
03962 
03963     TRACE("\n");
03964 
03965     lpszPath = heap_alloc((lstrlenW( session->hostName ) + 13)*sizeof(WCHAR));
03966     sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
03967     requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
03968     HeapFree( GetProcessHeap(), 0, lpszPath );
03969 
03970     len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
03971                                 NULL, 0, NULL, NULL );
03972     len--; /* the nul terminator isn't needed */
03973     ascii_req = heap_alloc(len);
03974     WideCharToMultiByte( CP_ACP, 0, requestString, -1,
03975                             ascii_req, len, NULL, NULL );
03976     HeapFree( GetProcessHeap(), 0, requestString );
03977 
03978     TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
03979 
03980     res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
03981     HeapFree( GetProcessHeap(), 0, ascii_req );
03982     if (res != ERROR_SUCCESS)
03983         return res;
03984 
03985     responseLen = HTTP_GetResponseHeaders( request, TRUE );
03986     if (!responseLen)
03987         return ERROR_HTTP_INVALID_HEADER;
03988 
03989     return ERROR_SUCCESS;
03990 }
03991 
03992 static void HTTP_InsertCookies(http_request_t *request)
03993 {
03994     DWORD cookie_size, size, cnt = 0;
03995     HTTPHEADERW *host;
03996     WCHAR *cookies;
03997 
03998     static const WCHAR cookieW[] = {'C','o','o','k','i','e',':',' ',0};
03999 
04000     host = HTTP_GetHeader(request, hostW);
04001     if(!host)
04002         return;
04003 
04004     if(!get_cookie(host->lpszValue, request->path, NULL, &cookie_size))
04005         return;
04006 
04007     size = sizeof(cookieW) + cookie_size * sizeof(WCHAR) + sizeof(szCrLf);
04008     if(!(cookies = heap_alloc(size)))
04009         return;
04010 
04011     cnt += sprintfW(cookies, cookieW);
04012     get_cookie(host->lpszValue, request->path, cookies+cnt, &cookie_size);
04013     strcatW(cookies, szCrLf);
04014 
04015     HTTP_HttpAddRequestHeadersW(request, cookies, strlenW(cookies), HTTP_ADDREQ_FLAG_REPLACE);
04016 
04017     heap_free(cookies);
04018 }
04019 
04020 static WORD HTTP_ParseDay(LPCWSTR day)
04021 {
04022     static const WCHAR days[7][4] = {{ 's','u','n',0 },
04023                                      { 'm','o','n',0 },
04024                                      { 't','u','e',0 },
04025                                      { 'w','e','d',0 },
04026                                      { 't','h','u',0 },
04027                                      { 'f','r','i',0 },
04028                                      { 's','a','t',0 }};
04029     int i;
04030     for (i = 0; i < sizeof(days)/sizeof(*days); i++)
04031         if (!strcmpiW(day, days[i]))
04032             return i;
04033 
04034     /* Invalid */
04035     return 7;
04036 }
04037 
04038 static WORD HTTP_ParseMonth(LPCWSTR month)
04039 {
04040     static const WCHAR jan[] = { 'j','a','n',0 };
04041     static const WCHAR feb[] = { 'f','e','b',0 };
04042     static const WCHAR mar[] = { 'm','a','r',0 };
04043     static const WCHAR apr[] = { 'a','p','r',0 };
04044     static const WCHAR may[] = { 'm','a','y',0 };
04045     static const WCHAR jun[] = { 'j','u','n',0 };
04046     static const WCHAR jul[] = { 'j','u','l',0 };
04047     static const WCHAR aug[] = { 'a','u','g',0 };
04048     static const WCHAR sep[] = { 's','e','p',0 };
04049     static const WCHAR oct[] = { 'o','c','t',0 };
04050     static const WCHAR nov[] = { 'n','o','v',0 };
04051     static const WCHAR dec[] = { 'd','e','c',0 };
04052 
04053     if (!strcmpiW(month, jan)) return 1;
04054     if (!strcmpiW(month, feb)) return 2;
04055     if (!strcmpiW(month, mar)) return 3;
04056     if (!strcmpiW(month, apr)) return 4;
04057     if (!strcmpiW(month, may)) return 5;
04058     if (!strcmpiW(month, jun)) return 6;
04059     if (!strcmpiW(month, jul)) return 7;
04060     if (!strcmpiW(month, aug)) return 8;
04061     if (!strcmpiW(month, sep)) return 9;
04062     if (!strcmpiW(month, oct)) return 10;
04063     if (!strcmpiW(month, nov)) return 11;
04064     if (!strcmpiW(month, dec)) return 12;
04065     /* Invalid */
04066     return 0;
04067 }
04068 
04069 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
04070  * optionally preceded by whitespace.
04071  * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
04072  * st, and sets *str to the first character after the time format.
04073  */
04074 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
04075 {
04076     LPCWSTR ptr = *str;
04077     WCHAR *nextPtr;
04078     unsigned long num;
04079 
04080     while (isspaceW(*ptr))
04081         ptr++;
04082 
04083     num = strtoulW(ptr, &nextPtr, 10);
04084     if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
04085     {
04086         ERR("unexpected time format %s\n", debugstr_w(ptr));
04087         return FALSE;
04088     }
04089     if (num > 23)
04090     {
04091         ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
04092         return FALSE;
04093     }
04094     ptr = nextPtr + 1;
04095     st->wHour = (WORD)num;
04096     num = strtoulW(ptr, &nextPtr, 10);
04097     if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
04098     {
04099         ERR("unexpected time format %s\n", debugstr_w(ptr));
04100         return FALSE;
04101     }
04102     if (num > 59)
04103     {
04104         ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
04105         return FALSE;
04106     }
04107     ptr = nextPtr + 1;
04108     st->wMinute = (WORD)num;
04109     num = strtoulW(ptr, &nextPtr, 10);
04110     if (!nextPtr || nextPtr <= ptr)
04111     {
04112         ERR("unexpected time format %s\n", debugstr_w(ptr));
04113         return FALSE;
04114     }
04115     if (num > 59)
04116     {
04117         ERR("unexpected second in time format %s\n", debugstr_w(ptr));
04118         return FALSE;
04119     }
04120     ptr = nextPtr + 1;
04121     *str = ptr;
04122     st->wSecond = (WORD)num;
04123     return TRUE;
04124 }
04125 
04126 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
04127 {
04128     static const WCHAR gmt[]= { 'G','M','T',0 };
04129     WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
04130     LPCWSTR ptr;
04131     SYSTEMTIME st = { 0 };
04132     unsigned long num;
04133 
04134     for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
04135          dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
04136         *dayPtr = *ptr;
04137     *dayPtr = 0;
04138     st.wDayOfWeek = HTTP_ParseDay(day);
04139     if (st.wDayOfWeek >= 7)
04140     {
04141         ERR("unexpected weekday %s\n", debugstr_w(day));
04142         return FALSE;
04143     }
04144 
04145     while (isspaceW(*ptr))
04146         ptr++;
04147 
04148     for (monthPtr = month; !isspace(*ptr) &&
04149          monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
04150          monthPtr++, ptr++)
04151         *monthPtr = *ptr;
04152     *monthPtr = 0;
04153     st.wMonth = HTTP_ParseMonth(month);
04154     if (!st.wMonth || st.wMonth > 12)
04155     {
04156         ERR("unexpected month %s\n", debugstr_w(month));
04157         return FALSE;
04158     }
04159 
04160     while (isspaceW(*ptr))
04161         ptr++;
04162 
04163     num = strtoulW(ptr, &nextPtr, 10);
04164     if (!nextPtr || nextPtr <= ptr || !num || num > 31)
04165     {
04166         ERR("unexpected day %s\n", debugstr_w(ptr));
04167         return FALSE;
04168     }
04169     ptr = nextPtr;
04170     st.wDay = (WORD)num;
04171 
04172     while (isspaceW(*ptr))
04173         ptr++;
04174 
04175     if (!HTTP_ParseTime(&st, &ptr))
04176         return FALSE;
04177 
04178     while (isspaceW(*ptr))
04179         ptr++;
04180 
04181     num = strtoulW(ptr, &nextPtr, 10);
04182     if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
04183     {
04184         ERR("unexpected year %s\n", debugstr_w(ptr));
04185         return FALSE;
04186     }
04187     ptr = nextPtr;
04188     st.wYear = (WORD)num;
04189 
04190     while (isspaceW(*ptr))
04191         ptr++;
04192 
04193     /* asctime() doesn't report a timezone, but some web servers do, so accept
04194      * with or without GMT.
04195      */
04196     if (*ptr && strcmpW(ptr, gmt))
04197     {
04198         ERR("unexpected timezone %s\n", debugstr_w(ptr));
04199         return FALSE;
04200     }
04201     return SystemTimeToFileTime(&st, ft);
04202 }
04203 
04204 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
04205 {
04206     static const WCHAR gmt[]= { 'G','M','T',0 };
04207     WCHAR *nextPtr, day[4], month[4], *monthPtr;
04208     LPCWSTR ptr;
04209     unsigned long num;
04210     SYSTEMTIME st = { 0 };
04211 
04212     ptr = strchrW(value, ',');
04213     if (!ptr)
04214         return FALSE;
04215     if (ptr - value != 3)
04216     {
04217         ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
04218         return FALSE;
04219     }
04220     memcpy(day, value, (ptr - value) * sizeof(WCHAR));
04221     day[3] = 0;
04222     st.wDayOfWeek = HTTP_ParseDay(day);
04223     if (st.wDayOfWeek > 6)
04224     {
04225         ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
04226         return FALSE;
04227     }
04228     ptr++;
04229 
04230     while (isspaceW(*ptr))
04231         ptr++;
04232 
04233     num = strtoulW(ptr, &nextPtr, 10);
04234     if (!nextPtr || nextPtr <= ptr || !num || num > 31)
04235     {
04236         ERR("unexpected day %s\n", debugstr_w(value));
04237         return FALSE;
04238     }
04239     ptr = nextPtr;
04240     st.wDay = (WORD)num;
04241 
04242     while (isspaceW(*ptr))
04243         ptr++;
04244 
04245     for (monthPtr = month; !isspace(*ptr) &&
04246          monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
04247          monthPtr++, ptr++)
04248         *monthPtr = *ptr;
04249     *monthPtr = 0;
04250     st.wMonth = HTTP_ParseMonth(month);
04251     if (!st.wMonth || st.wMonth > 12)
04252     {
04253         ERR("unexpected month %s\n", debugstr_w(month));
04254         return FALSE;
04255     }
04256 
04257     while (isspaceW(*ptr))
04258         ptr++;
04259 
04260     num = strtoulW(ptr, &nextPtr, 10);
04261     if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
04262     {
04263         ERR("unexpected year %s\n", debugstr_w(value));
04264         return FALSE;
04265     }
04266     ptr = nextPtr;
04267     st.wYear = (WORD)num;
04268 
04269     if (!HTTP_ParseTime(&st, &ptr))
04270         return FALSE;
04271 
04272     while (isspaceW(*ptr))
04273         ptr++;
04274 
04275     if (strcmpW(ptr, gmt))
04276     {
04277         ERR("unexpected time zone %s\n", debugstr_w(ptr));
04278         return FALSE;
04279     }
04280     return SystemTimeToFileTime(&st, ft);
04281 }
04282 
04283 /* FIXME: only accepts dates in RFC 1123 format and asctime() format,
04284  * which may not be the only formats actually seen in the wild.
04285  * http://www.hackcraft.net/web/datetime/ suggests at least RFC 850 dates
04286  * should be accepted as well.
04287  */
04288 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
04289 {
04290     static const WCHAR zero[] = { '0',0 };
04291     BOOL ret;
04292 
04293     if (!strcmpW(value, zero))
04294     {
04295         ft->dwLowDateTime = ft->dwHighDateTime = 0;
04296         ret = TRUE;
04297     }
04298     else if (strchrW(value, ','))
04299         ret = HTTP_ParseRfc1123Date(value, ft);
04300     else
04301     {
04302         ret = HTTP_ParseDateAsAsctime(value, ft);
04303         if (!ret)
04304             ERR("unexpected date format %s\n", debugstr_w(value));
04305     }
04306     return ret;
04307 }
04308 
04309 static void HTTP_ProcessExpires(http_request_t *request)
04310 {
04311     BOOL expirationFound = FALSE;
04312     int headerIndex;
04313 
04314     /* Look for a Cache-Control header with a max-age directive, as it takes
04315      * precedence over the Expires header.
04316      */
04317     headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
04318     if (headerIndex != -1)
04319     {
04320         LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
04321         LPWSTR ptr;
04322 
04323         for (ptr = ccHeader->lpszValue; ptr && *ptr; )
04324         {
04325             LPWSTR comma = strchrW(ptr, ','), end, equal;
04326 
04327             if (comma)
04328                 end = comma;
04329             else
04330                 end = ptr + strlenW(ptr);
04331             for (equal = end - 1; equal > ptr && *equal != '='; equal--)
04332                 ;
04333             if (*equal == '=')
04334             {
04335                 static const WCHAR max_age[] = {
04336                     'm','a','x','-','a','g','e',0 };
04337 
04338                 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
04339                 {
04340                     LPWSTR nextPtr;
04341                     unsigned long age;
04342 
04343                     age = strtoulW(equal + 1, &nextPtr, 10);
04344                     if (nextPtr > equal + 1)
04345                     {
04346                         LARGE_INTEGER ft;
04347 
04348                         NtQuerySystemTime( &ft );
04349                         /* Age is in seconds, FILETIME resolution is in
04350                          * 100 nanosecond intervals.
04351                          */
04352                         ft.QuadPart += age * (ULONGLONG)1000000;
04353                         request->expires.dwLowDateTime = ft.u.LowPart;
04354                         request->expires.dwHighDateTime = ft.u.HighPart;
04355                         expirationFound = TRUE;
04356                     }
04357                 }
04358             }
04359             if (comma)
04360             {
04361                 ptr = comma + 1;
04362                 while (isspaceW(*ptr))
04363                     ptr++;
04364             }
04365             else
04366                 ptr = NULL;
04367         }
04368     }
04369     if (!expirationFound)
04370     {
04371         headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
04372         if (headerIndex != -1)
04373         {
04374             LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
04375             FILETIME ft;
04376 
04377             if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
04378             {
04379                 expirationFound = TRUE;
04380                 request->expires = ft;
04381             }
04382         }
04383     }
04384     if (!expirationFound)
04385     {
04386         LARGE_INTEGER t;
04387 
04388         /* With no known age, default to 10 minutes until expiration. */
04389         NtQuerySystemTime( &t );
04390         t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
04391         request->expires.dwLowDateTime = t.u.LowPart;
04392         request->expires.dwHighDateTime = t.u.HighPart;
04393     }
04394 }
04395 
04396 static void HTTP_ProcessLastModified(http_request_t *request)
04397 {
04398     int headerIndex;
04399 
04400     headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
04401     if (headerIndex != -1)
04402     {
04403         LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
04404         FILETIME ft;
04405 
04406         if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
04407             request->last_modified = ft;
04408     }
04409 }
04410 
04411 static void http_process_keep_alive(http_request_t *req)
04412 {
04413     int index;
04414 
04415     index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
04416     if(index != -1)
04417         req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
04418     else
04419         req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
04420 }
04421 
04422 static void HTTP_CacheRequest(http_request_t *request)
04423 {
04424     WCHAR url[INTERNET_MAX_URL_LENGTH];
04425     WCHAR cacheFileName[MAX_PATH+1];
04426     BOOL b;
04427 
04428     b = HTTP_GetRequestURL(request, url);
04429     if(!b) {
04430         WARN("Could not get URL\n");
04431         return;
04432     }
04433 
04434     b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
04435     if(b) {
04436         HeapFree(GetProcessHeap(), 0, request->cacheFile);
04437         CloseHandle(request->hCacheFile);
04438 
04439         request->cacheFile = heap_strdupW(cacheFileName);
04440         request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
04441                   NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
04442         if(request->hCacheFile == INVALID_HANDLE_VALUE) {
04443             WARN("Could not create file: %u\n", GetLastError());
04444             request->hCacheFile = NULL;
04445         }
04446     }else {
04447         WARN("Could not create cache entry: %08x\n", GetLastError());
04448     }
04449 }
04450 
04451 static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
04452 {
04453     const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
04454     http_session_t *session = request->session;
04455     netconn_t *netconn = NULL;
04456     server_t *server;
04457     DWORD res;
04458 
04459     assert(!request->netconn);
04460     reset_data_stream(request);
04461 
04462     server = get_server(session->serverName, session->serverPort);
04463     if(!server)
04464         return ERROR_OUTOFMEMORY;
04465 
04466     res = HTTP_ResolveName(request, server);
04467     if(res != ERROR_SUCCESS) {
04468         server_release(server);
04469         return res;
04470     }
04471 
04472     EnterCriticalSection(&connection_pool_cs);
04473 
04474     while(!list_empty(&server->conn_pool)) {
04475         netconn = LIST_ENTRY(list_head(&server->conn_pool), netconn_t, pool_entry);
04476         list_remove(&netconn->pool_entry);
04477 
04478         if(NETCON_is_alive(netconn))
04479             break;
04480 
04481         TRACE("connection %p closed during idle\n", netconn);
04482         free_netconn(netconn);
04483         netconn = NULL;
04484     }
04485 
04486     LeaveCriticalSection(&connection_pool_cs);
04487 
04488     if(netconn) {
04489         TRACE("<-- reusing %p netconn\n", netconn);
04490         request->netconn = netconn;
04491         *reusing = TRUE;
04492         return ERROR_SUCCESS;
04493     }
04494 
04495     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
04496                           INTERNET_STATUS_CONNECTING_TO_SERVER,
04497                           server->addr_str,
04498                           strlen(server->addr_str)+1);
04499 
04500     res = create_netconn(is_https, server, request->security_flags, &netconn);
04501     server_release(server);
04502     if(res != ERROR_SUCCESS) {
04503         ERR("create_netconn failed: %u\n", res);
04504         return res;
04505     }
04506 
04507     request->netconn = netconn;
04508 
04509     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
04510             INTERNET_STATUS_CONNECTED_TO_SERVER,
04511             server->addr_str, strlen(server->addr_str)+1);
04512 
04513     if(is_https) {
04514         /* Note: we differ from Microsoft's WinINet here. they seem to have
04515          * a bug that causes no status callbacks to be sent when starting
04516          * a tunnel to a proxy server using the CONNECT verb. i believe our
04517          * behaviour to be more correct and to not cause any incompatibilities
04518          * because using a secure connection through a proxy server is a rare
04519          * case that would be hard for anyone to depend on */
04520         if(session->appInfo->proxy)
04521             res = HTTP_SecureProxyConnect(request);
04522         if(res == ERROR_SUCCESS)
04523             res = NETCON_secure_connect(request->netconn, session->hostName);
04524         if(res != ERROR_SUCCESS)
04525         {
04526             WARN("Couldn't connect securely to host\n");
04527 
04528             if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
04529                     res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
04530                     || res == ERROR_INTERNET_INVALID_CA
04531                     || res == ERROR_INTERNET_SEC_CERT_NO_REV
04532                     || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
04533                     || res == ERROR_INTERNET_SEC_CERT_REVOKED
04534                     || res == ERROR_INTERNET_SEC_INVALID_CERT
04535                     || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
04536                 res = ERROR_INTERNET_SEC_CERT_ERRORS;
04537         }
04538     }
04539 
04540     if(res != ERROR_SUCCESS) {
04541         http_release_netconn(request, FALSE);
04542         return res;
04543     }
04544 
04545     *reusing = FALSE;
04546     TRACE("Created connection to %s: %p\n", debugstr_w(server->name), netconn);
04547     return ERROR_SUCCESS;
04548 }
04549 
04550 /***********************************************************************
04551  *           HTTP_HttpSendRequestW (internal)
04552  *
04553  * Sends the specified request to the HTTP server
04554  *
04555  * RETURNS
04556  *    ERROR_SUCCESS on success
04557  *    win32 error code on failure
04558  *
04559  */
04560 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
04561     DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
04562     DWORD dwContentLength, BOOL bEndRequest)
04563 {
04564     INT cnt;
04565     BOOL redirected = FALSE;
04566     LPWSTR requestString = NULL;
04567     INT responseLen;
04568     BOOL loop_next;
04569     INTERNET_ASYNC_RESULT iar;
04570     static const WCHAR szPost[] = { 'P','O','S','T',0 };
04571     static const WCHAR szContentLength[] =
04572         { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
04573     WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
04574     DWORD res;
04575 
04576     TRACE("--> %p\n", request);
04577 
04578     assert(request->hdr.htype == WH_HHTTPREQ);
04579 
04580     /* if the verb is NULL default to GET */
04581     if (!request->verb)
04582         request->verb = heap_strdupW(szGET);
04583 
04584     if (dwContentLength || strcmpW(request->verb, szGET))
04585     {
04586         sprintfW(contentLengthStr, szContentLength, dwContentLength);
04587         HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
04588         request->bytesToWrite = dwContentLength;
04589     }
04590     if (request->session->appInfo->agent)
04591     {
04592         WCHAR *agent_header;
04593         static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
04594         int len;
04595 
04596         len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
04597         agent_header = heap_alloc(len * sizeof(WCHAR));
04598         sprintfW(agent_header, user_agent, request->session->appInfo->agent);
04599 
04600         HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
04601         HeapFree(GetProcessHeap(), 0, agent_header);
04602     }
04603     if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
04604     {
04605         static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
04606         HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
04607     }
04608     if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
04609     {
04610         static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
04611                                               ' ','n','o','-','c','a','c','h','e','\r','\n',0};
04612         HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
04613     }
04614 
04615     do
04616     {
04617         DWORD len;
04618         BOOL reusing_connection;
04619         char *ascii_req;
04620 
04621         loop_next = FALSE;
04622 
04623         /* like native, just in case the caller forgot to call InternetReadFile
04624          * for all the data */
04625         drain_content(request);
04626         if(redirected) {
04627             request->contentLength = ~0u;
04628             request->bytesToWrite = 0;
04629         }
04630 
04631         if (TRACE_ON(wininet))
04632         {
04633             LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
04634             TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
04635         }
04636 
04637         HTTP_FixURL(request);
04638         if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
04639         {
04640             HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
04641         }
04642         HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
04643         HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
04644 
04645         if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
04646             HTTP_InsertCookies(request);
04647 
04648         /* add the headers the caller supplied */
04649         if( lpszHeaders && dwHeaderLength )
04650         {
04651             HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength,
04652                         HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
04653         }
04654 
04655         if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
04656         {
04657             WCHAR *url = HTTP_BuildProxyRequestUrl(request);
04658             requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
04659             HeapFree(GetProcessHeap(), 0, url);
04660         }
04661         else
04662             requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
04663 
04664  
04665         TRACE("Request header -> %s\n", debugstr_w(requestString) );
04666 
04667         if ((res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
04668             break;
04669 
04670         /* send the request as ASCII, tack on the optional data */
04671         if (!lpOptional || redirected)
04672             dwOptionalLength = 0;
04673         len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
04674                                    NULL, 0, NULL, NULL );
04675         ascii_req = heap_alloc(len + dwOptionalLength);
04676         WideCharToMultiByte( CP_ACP, 0, requestString, -1,
04677                              ascii_req, len, NULL, NULL );
04678         if( lpOptional )
04679             memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
04680         len = (len + dwOptionalLength - 1);
04681         ascii_req[len] = 0;
04682         TRACE("full request -> %s\n", debugstr_a(ascii_req) );
04683 
04684         INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
04685                               INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
04686 
04687         res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
04688         HeapFree( GetProcessHeap(), 0, ascii_req );
04689         if(res != ERROR_SUCCESS) {
04690             TRACE("send failed: %u\n", res);
04691             if(!reusing_connection)
04692                 break;
04693             http_release_netconn(request, FALSE);
04694             loop_next = TRUE;
04695             continue;
04696         }
04697 
04698         request->bytesWritten = dwOptionalLength;
04699 
04700         INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
04701                               INTERNET_STATUS_REQUEST_SENT,
04702                               &len, sizeof(DWORD));
04703 
04704         if (bEndRequest)
04705         {
04706             DWORD dwBufferSize;
04707             DWORD dwStatusCode;
04708 
04709             INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
04710                                 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
04711     
04712             responseLen = HTTP_GetResponseHeaders(request, TRUE);
04713             /* FIXME: We should know that connection is closed before sending
04714              * headers. Otherwise wrong callbacks are executed */
04715             if(!responseLen && reusing_connection) {
04716                 TRACE("Connection closed by server, reconnecting\n");
04717                 http_release_netconn(request, FALSE);
04718                 loop_next = TRUE;
04719                 continue;
04720             }
04721 
04722             INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
04723                                 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
04724                                 sizeof(DWORD));
04725 
04726             http_process_keep_alive(request);
04727             HTTP_ProcessCookies(request);
04728             HTTP_ProcessExpires(request);
04729             HTTP_ProcessLastModified(request);
04730 
04731             dwBufferSize = sizeof(dwStatusCode);
04732             if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
04733                                     &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
04734                 dwStatusCode = 0;
04735 
04736             res = set_content_length(request, dwStatusCode);
04737             if(res != ERROR_SUCCESS)
04738                 goto lend;
04739             if(!request->contentLength)
04740                 http_release_netconn(request, TRUE);
04741 
04742             if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
04743             {
04744                 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
04745                 dwBufferSize=sizeof(szNewLocation);
04746                 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
04747                      dwStatusCode == HTTP_STATUS_MOVED ||
04748                      dwStatusCode == HTTP_STATUS_REDIRECT_KEEP_VERB ||
04749                      dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
04750                     HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
04751                 {
04752                     if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD))
04753                     {
04754                         HeapFree(GetProcessHeap(), 0, request->verb);
04755                         request->verb = heap_strdupW(szGET);
04756                     }
04757                     drain_content(request);
04758                     if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
04759                     {
04760                         INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
04761                                               new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
04762                         res = HTTP_HandleRedirect(request, new_url);
04763                         if (res == ERROR_SUCCESS)
04764                         {
04765                             HeapFree(GetProcessHeap(), 0, requestString);
04766                             loop_next = TRUE;
04767                         }
04768                         HeapFree( GetProcessHeap(), 0, new_url );
04769                     }
04770                     redirected = TRUE;
04771                 }
04772             }
04773             if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
04774             {
04775                 WCHAR szAuthValue[2048];
04776                 dwBufferSize=2048;
04777                 if (dwStatusCode == HTTP_STATUS_DENIED)
04778                 {
04779                     LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
04780                     DWORD dwIndex = 0;
04781                     while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
04782                     {
04783                         if (HTTP_DoAuthorization(request, szAuthValue,
04784                                                  &request->authInfo,
04785                                                  request->session->userName,
04786                                                  request->session->password,
04787                                                  Host->lpszValue))
04788                         {
04789                             HeapFree(GetProcessHeap(), 0, requestString);
04790                             loop_next = TRUE;
04791                             break;
04792                         }
04793                     }
04794 
04795                     if(!loop_next) {
04796                         TRACE("Cleaning wrong authorization data\n");
04797                         destroy_authinfo(request->authInfo);
04798                         request->authInfo = NULL;
04799                     }
04800                 }
04801                 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
04802                 {
04803                     DWORD dwIndex = 0;
04804                     while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
04805                     {
04806                         if (HTTP_DoAuthorization(request, szAuthValue,
04807                                                  &request->proxyAuthInfo,
04808                                                  request->session->appInfo->proxyUsername,
04809                                                  request->session->appInfo->proxyPassword,
04810                                                  NULL))
04811                         {
04812                             loop_next = TRUE;
04813                             break;
04814                         }
04815                     }
04816 
04817                     if(!loop_next) {
04818                         TRACE("Cleaning wrong proxy authorization data\n");
04819                         destroy_authinfo(request->proxyAuthInfo);
04820                         request->proxyAuthInfo = NULL;
04821                     }
04822                 }
04823             }
04824         }
04825         else
04826             res = ERROR_SUCCESS;
04827     }
04828     while (loop_next);
04829 
04830     if(res == ERROR_SUCCESS)
04831         HTTP_CacheRequest(request);
04832 
04833 lend:
04834 
04835     HeapFree(GetProcessHeap(), 0, requestString);
04836 
04837     /* TODO: send notification for P3P header */
04838 
04839     if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
04840     {
04841         if (res == ERROR_SUCCESS && request->contentLength && request->bytesWritten == request->bytesToWrite)
04842             HTTP_ReceiveRequestData(request, TRUE);
04843         else
04844         {
04845             iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)request->hdr.hInternet : 0);
04846             iar.dwError = res;
04847 
04848             INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
04849                                   INTERNET_STATUS_REQUEST_COMPLETE, &iar,
04850                                   sizeof(INTERNET_ASYNC_RESULT));
04851         }
04852     }
04853 
04854     TRACE("<--\n");
04855     return res;
04856 }
04857 
04858 /***********************************************************************
04859  *
04860  * Helper functions for the HttpSendRequest(Ex) functions
04861  *
04862  */
04863 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
04864 {
04865     struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
04866     http_request_t *request = (http_request_t*) workRequest->hdr;
04867 
04868     TRACE("%p\n", request);
04869 
04870     HTTP_HttpSendRequestW(request, req->lpszHeader,
04871             req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
04872             req->dwContentLength, req->bEndRequest);
04873 
04874     HeapFree(GetProcessHeap(), 0, req->lpszHeader);
04875 }
04876 
04877 
04878 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
04879 {
04880     INT responseLen;
04881     DWORD dwCode, dwCodeLength;
04882     DWORD dwBufferSize;
04883     DWORD res = ERROR_SUCCESS;
04884 
04885     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
04886                   INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
04887 
04888     responseLen = HTTP_GetResponseHeaders(request, TRUE);
04889     if (!responseLen)
04890         res = ERROR_HTTP_HEADER_NOT_FOUND;
04891 
04892     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
04893                   INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
04894 
04895     /* process cookies here. Is this right? */
04896     http_process_keep_alive(request);
04897     HTTP_ProcessCookies(request);
04898     HTTP_ProcessExpires(request);
04899     HTTP_ProcessLastModified(request);
04900 
04901     dwCodeLength = sizeof(dwCode);
04902     if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
04903                             &dwCode,&dwCodeLength,NULL) != ERROR_SUCCESS)
04904         dwCode = 0;
04905 
04906     if ((res = set_content_length( request, dwCode )) == ERROR_SUCCESS) {
04907         if(!request->contentLength)
04908             http_release_netconn(request, TRUE);
04909     }
04910 
04911     if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
04912     {
04913         if (dwCode == HTTP_STATUS_REDIRECT ||
04914                 dwCode == HTTP_STATUS_MOVED ||
04915                 dwCode == HTTP_STATUS_REDIRECT_METHOD ||
04916                 dwCode == HTTP_STATUS_REDIRECT_KEEP_VERB)
04917         {
04918             WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
04919             dwBufferSize=sizeof(szNewLocation);
04920             if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
04921             {
04922                 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD))
04923                 {
04924                     HeapFree(GetProcessHeap(), 0, request->verb);
04925                     request->verb = heap_strdupW(szGET);
04926                 }
04927                 drain_content(request);
04928                 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
04929                 {
04930                     INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
04931                                           new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
04932                     res = HTTP_HandleRedirect(request, new_url);
04933                     if (res == ERROR_SUCCESS)
04934                         res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
04935                     HeapFree( GetProcessHeap(), 0, new_url );
04936                 }
04937             }
04938         }
04939     }
04940 
04941     if (res == ERROR_SUCCESS && request->contentLength) {
04942         HTTP_ReceiveRequestData(request, TRUE);
04943     }else {
04944         INTERNET_ASYNC_RESULT iar = {0, res};
04945 
04946         INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
04947                               INTERNET_STATUS_REQUEST_COMPLETE, &iar,
04948                               sizeof(INTERNET_ASYNC_RESULT));
04949     }
04950 
04951     return res;
04952 }
04953 
04954 /***********************************************************************
04955  *           HttpEndRequestA (WININET.@)
04956  *
04957  * Ends an HTTP request that was started by HttpSendRequestEx
04958  *
04959  * RETURNS
04960  *    TRUE  if successful
04961  *    FALSE on failure
04962  *
04963  */
04964 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
04965         LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
04966 {
04967     TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
04968 
04969     if (lpBuffersOut)
04970     {
04971         SetLastError(ERROR_INVALID_PARAMETER);
04972         return FALSE;
04973     }
04974 
04975     return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
04976 }
04977 
04978 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
04979 {
04980     struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
04981     http_request_t *request = (http_request_t*)work->hdr;
04982 
04983     TRACE("%p\n", request);
04984 
04985     HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
04986 }
04987 
04988 /***********************************************************************
04989  *           HttpEndRequestW (WININET.@)
04990  *
04991  * Ends an HTTP request that was started by HttpSendRequestEx
04992  *
04993  * RETURNS
04994  *    TRUE  if successful
04995  *    FALSE on failure
04996  *
04997  */
04998 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
04999         LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
05000 {
05001     http_request_t *request;
05002     DWORD res;
05003 
05004     TRACE("-->\n");
05005 
05006     if (lpBuffersOut)
05007     {
05008         SetLastError(ERROR_INVALID_PARAMETER);
05009         return FALSE;
05010     }
05011 
05012     request = (http_request_t*) get_handle_object( hRequest );
05013 
05014     if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
05015     {
05016         SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
05017         if (request)
05018             WININET_Release( &request->hdr );
05019         return FALSE;
05020     }
05021     request->hdr.dwFlags |= dwFlags;
05022 
05023     if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
05024     {
05025         WORKREQUEST work;
05026         struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
05027 
05028         work.asyncproc = AsyncHttpEndRequestProc;
05029         work.hdr = WININET_AddRef( &request->hdr );
05030 
05031         work_endrequest = &work.u.HttpEndRequestW;
05032         work_endrequest->dwFlags = dwFlags;
05033         work_endrequest->dwContext = dwContext;
05034 
05035         INTERNET_AsyncCall(&work);
05036         res = ERROR_IO_PENDING;
05037     }
05038     else
05039         res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
05040 
05041     WININET_Release( &request->hdr );
05042     TRACE("%u <--\n", res);
05043     if(res != ERROR_SUCCESS)
05044         SetLastError(res);
05045     return res == ERROR_SUCCESS;
05046 }
05047 
05048 /***********************************************************************
05049  *           HttpSendRequestExA (WININET.@)
05050  *
05051  * Sends the specified request to the HTTP server and allows chunked
05052  * transfers.
05053  *
05054  * RETURNS
05055  *  Success: TRUE
05056  *  Failure: FALSE, call GetLastError() for more information.
05057  */
05058 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
05059                    LPINTERNET_BUFFERSA lpBuffersIn,
05060                    LPINTERNET_BUFFERSA lpBuffersOut,
05061                    DWORD dwFlags, DWORD_PTR dwContext)
05062 {
05063     INTERNET_BUFFERSW BuffersInW;
05064     BOOL rc = FALSE;
05065     DWORD headerlen;
05066     LPWSTR header = NULL;
05067 
05068     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
05069         lpBuffersOut, dwFlags, dwContext);
05070 
05071     if (lpBuffersIn)
05072     {
05073         BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
05074         if (lpBuffersIn->lpcszHeader)
05075         {
05076             headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
05077                     lpBuffersIn->dwHeadersLength,0,0);
05078             header = heap_alloc(headerlen*sizeof(WCHAR));
05079             if (!(BuffersInW.lpcszHeader = header))
05080             {
05081                 SetLastError(ERROR_OUTOFMEMORY);
05082                 return FALSE;
05083             }
05084             BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
05085                     lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
05086                     header, headerlen);
05087         }
05088         else
05089             BuffersInW.lpcszHeader = NULL;
05090         BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
05091         BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
05092         BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
05093         BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
05094         BuffersInW.Next = NULL;
05095     }
05096 
05097     rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
05098 
05099     HeapFree(GetProcessHeap(),0,header);
05100 
05101     return rc;
05102 }
05103 
05104 /***********************************************************************
05105  *           HttpSendRequestExW (WININET.@)
05106  *
05107  * Sends the specified request to the HTTP server and allows chunked
05108  * transfers
05109  *
05110  * RETURNS
05111  *  Success: TRUE
05112  *  Failure: FALSE, call GetLastError() for more information.
05113  */
05114 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
05115                    LPINTERNET_BUFFERSW lpBuffersIn,
05116                    LPINTERNET_BUFFERSW lpBuffersOut,
05117                    DWORD dwFlags, DWORD_PTR dwContext)
05118 {
05119     http_request_t *request;
05120     http_session_t *session;
05121     appinfo_t *hIC;
05122     DWORD res;
05123 
05124     TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
05125             lpBuffersOut, dwFlags, dwContext);
05126 
05127     request = (http_request_t*) get_handle_object( hRequest );
05128 
05129     if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
05130     {
05131         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
05132         goto lend;
05133     }
05134 
05135     session = request->session;
05136     assert(session->hdr.htype == WH_HHTTPSESSION);
05137     hIC = session->appInfo;
05138     assert(hIC->hdr.htype == WH_HINIT);
05139 
05140     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
05141     {
05142         WORKREQUEST workRequest;
05143         struct WORKREQ_HTTPSENDREQUESTW *req;
05144 
05145         workRequest.asyncproc = AsyncHttpSendRequestProc;
05146         workRequest.hdr = WININET_AddRef( &request->hdr );
05147         req = &workRequest.u.HttpSendRequestW;
05148         if (lpBuffersIn)
05149         {
05150             DWORD size = 0;
05151 
05152             if (lpBuffersIn->lpcszHeader)
05153             {
05154                 if (lpBuffersIn->dwHeadersLength == ~0u)
05155                     size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
05156                 else
05157                     size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
05158 
05159                 req->lpszHeader = heap_alloc(size);
05160                 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
05161             }
05162             else req->lpszHeader = NULL;
05163 
05164             req->dwHeaderLength = size / sizeof(WCHAR);
05165             req->lpOptional = lpBuffersIn->lpvBuffer;
05166             req->dwOptionalLength = lpBuffersIn->dwBufferLength;
05167             req->dwContentLength = lpBuffersIn->dwBufferTotal;
05168         }
05169         else
05170         {
05171             req->lpszHeader = NULL;
05172             req->dwHeaderLength = 0;
05173             req->lpOptional = NULL;
05174             req->dwOptionalLength = 0;
05175             req->dwContentLength = 0;
05176         }
05177 
05178         req->bEndRequest = FALSE;
05179 
05180         INTERNET_AsyncCall(&workRequest);
05181         /*
05182          * This is from windows.
05183          */
05184         res = ERROR_IO_PENDING;
05185     }
05186     else
05187     {
05188         if (lpBuffersIn)
05189             res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
05190                                         lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
05191                                         lpBuffersIn->dwBufferTotal, FALSE);
05192         else
05193             res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
05194     }
05195 
05196 lend:
05197     if ( request )
05198         WININET_Release( &request->hdr );
05199 
05200     TRACE("<---\n");
05201     SetLastError(res);
05202     return res == ERROR_SUCCESS;
05203 }
05204 
05205 /***********************************************************************
05206  *           HttpSendRequestW (WININET.@)
05207  *
05208  * Sends the specified request to the HTTP server
05209  *
05210  * RETURNS
05211  *    TRUE  on success
05212  *    FALSE on failure
05213  *
05214  */
05215 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
05216     DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
05217 {
05218     http_request_t *request;
05219     http_session_t *session = NULL;
05220     appinfo_t *hIC = NULL;
05221     DWORD res = ERROR_SUCCESS;
05222 
05223     TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
05224             debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
05225 
05226     request = (http_request_t*) get_handle_object( hHttpRequest );
05227     if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
05228     {
05229         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
05230         goto lend;
05231     }
05232 
05233     session = request->session;
05234     if (NULL == session ||  session->hdr.htype != WH_HHTTPSESSION)
05235     {
05236         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
05237         goto lend;
05238     }
05239 
05240     hIC = session->appInfo;
05241     if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT)
05242     {
05243         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
05244         goto lend;
05245     }
05246 
05247     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
05248     {
05249         WORKREQUEST workRequest;
05250         struct WORKREQ_HTTPSENDREQUESTW *req;
05251 
05252         workRequest.asyncproc = AsyncHttpSendRequestProc;
05253         workRequest.hdr = WININET_AddRef( &request->hdr );
05254         req = &workRequest.u.HttpSendRequestW;
05255         if (lpszHeaders)
05256         {
05257             DWORD size;
05258 
05259             if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
05260             else size = dwHeaderLength * sizeof(WCHAR);
05261 
05262             req->lpszHeader = heap_alloc(size);
05263             memcpy(req->lpszHeader, lpszHeaders, size);
05264         }
05265         else
05266             req->lpszHeader = 0;
05267         req->dwHeaderLength = dwHeaderLength;
05268         req->lpOptional = lpOptional;
05269         req->dwOptionalLength = dwOptionalLength;
05270         req->dwContentLength = dwOptionalLength;
05271         req->bEndRequest = TRUE;
05272 
05273         INTERNET_AsyncCall(&workRequest);
05274         /*
05275          * This is from windows.
05276          */
05277         res = ERROR_IO_PENDING;
05278     }
05279     else
05280     {
05281     res = HTTP_HttpSendRequestW(request, lpszHeaders,
05282         dwHeaderLength, lpOptional, dwOptionalLength,
05283         dwOptionalLength, TRUE);
05284     }
05285 lend:
05286     if( request )
05287         WININET_Release( &request->hdr );
05288 
05289     SetLastError(res);
05290     return res == ERROR_SUCCESS;
05291 }
05292 
05293 /***********************************************************************
05294  *           HttpSendRequestA (WININET.@)
05295  *
05296  * Sends the specified request to the HTTP server
05297  *
05298  * RETURNS
05299  *    TRUE  on success
05300  *    FALSE on failure
05301  *
05302  */
05303 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
05304     DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
05305 {
05306     BOOL result;
05307     LPWSTR szHeaders=NULL;
05308     DWORD nLen=dwHeaderLength;
05309     if(lpszHeaders!=NULL)
05310     {
05311         nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
05312         szHeaders = heap_alloc(nLen*sizeof(WCHAR));
05313         MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
05314     }
05315     result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
05316     HeapFree(GetProcessHeap(),0,szHeaders);
05317     return result;
05318 }
05319 
05320 /***********************************************************************
05321  *           HTTPSESSION_Destroy (internal)
05322  *
05323  * Deallocate session handle
05324  *
05325  */
05326 static void HTTPSESSION_Destroy(object_header_t *hdr)
05327 {
05328     http_session_t *session = (http_session_t*) hdr;
05329 
05330     TRACE("%p\n", session);
05331 
05332     WININET_Release(&session->appInfo->hdr);
05333 
05334     HeapFree(GetProcessHeap(), 0, session->hostName);
05335     HeapFree(GetProcessHeap(), 0, session->serverName);
05336     HeapFree(GetProcessHeap(), 0, session->password);
05337     HeapFree(GetProcessHeap(), 0, session->userName);
05338 }
05339 
05340 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
05341 {
05342     switch(option) {
05343     case INTERNET_OPTION_HANDLE_TYPE:
05344         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
05345 
05346         if (*size < sizeof(ULONG))
05347             return ERROR_INSUFFICIENT_BUFFER;
05348 
05349         *size = sizeof(DWORD);
05350         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
05351         return ERROR_SUCCESS;
05352     }
05353 
05354     return INET_QueryOption(hdr, option, buffer, size, unicode);
05355 }
05356 
05357 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
05358 {
05359     http_session_t *ses = (http_session_t*)hdr;
05360 
05361     switch(option) {
05362     case INTERNET_OPTION_USERNAME:
05363     {
05364         HeapFree(GetProcessHeap(), 0, ses->userName);
05365         if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
05366         return ERROR_SUCCESS;
05367     }
05368     case INTERNET_OPTION_PASSWORD:
05369     {
05370         HeapFree(GetProcessHeap(), 0, ses->password);
05371         if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
05372         return ERROR_SUCCESS;
05373     }
05374     default: break;
05375     }
05376 
05377     return ERROR_INTERNET_INVALID_OPTION;
05378 }
05379 
05380 static const object_vtbl_t HTTPSESSIONVtbl = {
05381     HTTPSESSION_Destroy,
05382     NULL,
05383     HTTPSESSION_QueryOption,
05384     HTTPSESSION_SetOption,
05385     NULL,
05386     NULL,
05387     NULL,
05388     NULL,
05389     NULL
05390 };
05391 
05392 
05393 /***********************************************************************
05394  *           HTTP_Connect  (internal)
05395  *
05396  * Create http session handle
05397  *
05398  * RETURNS
05399  *   HINTERNET a session handle on success
05400  *   NULL on failure
05401  *
05402  */
05403 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
05404         INTERNET_PORT serverPort, LPCWSTR lpszUserName,
05405         LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
05406         DWORD dwInternalFlags, HINTERNET *ret)
05407 {
05408     http_session_t *session = NULL;
05409 
05410     TRACE("-->\n");
05411 
05412     if (!lpszServerName || !lpszServerName[0])
05413         return ERROR_INVALID_PARAMETER;
05414 
05415     assert( hIC->hdr.htype == WH_HINIT );
05416 
05417     session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
05418     if (!session)
05419         return ERROR_OUTOFMEMORY;
05420 
05421    /*
05422     * According to my tests. The name is not resolved until a request is sent
05423     */
05424 
05425     session->hdr.htype = WH_HHTTPSESSION;
05426     session->hdr.dwFlags = dwFlags;
05427     session->hdr.dwContext = dwContext;
05428     session->hdr.dwInternalFlags |= dwInternalFlags;
05429 
05430     WININET_AddRef( &hIC->hdr );
05431     session->appInfo = hIC;
05432     list_add_head( &hIC->hdr.children, &session->hdr.entry );
05433 
05434     if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
05435         if(hIC->proxyBypass)
05436             FIXME("Proxy bypass is ignored.\n");
05437     }
05438     session->serverName = heap_strdupW(lpszServerName);
05439     session->hostName = heap_strdupW(lpszServerName);
05440     if (lpszUserName && lpszUserName[0])
05441         session->userName = heap_strdupW(lpszUserName);
05442     if (lpszPassword && lpszPassword[0])
05443         session->password = heap_strdupW(lpszPassword);
05444     session->serverPort = serverPort;
05445     session->hostPort = serverPort;
05446 
05447     /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
05448     if (!(session->hdr.dwInternalFlags & INET_OPENURL))
05449     {
05450         INTERNET_SendCallback(&hIC->hdr, dwContext,
05451                               INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
05452                               sizeof(HINTERNET));
05453     }
05454 
05455 /*
05456  * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
05457  * windows
05458  */
05459 
05460     TRACE("%p --> %p\n", hIC, session);
05461 
05462     *ret = session->hdr.hInternet;
05463     return ERROR_SUCCESS;
05464 }
05465 
05466 /***********************************************************************
05467  *           HTTP_clear_response_headers (internal)
05468  *
05469  * clear out any old response headers
05470  */
05471 static void HTTP_clear_response_headers( http_request_t *request )
05472 {
05473     DWORD i;
05474 
05475     for( i=0; i<request->nCustHeaders; i++)
05476     {
05477         if( !request->custHeaders[i].lpszField )
05478             continue;
05479         if( !request->custHeaders[i].lpszValue )
05480             continue;
05481         if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
05482             continue;
05483         HTTP_DeleteCustomHeader( request, i );
05484         i--;
05485     }
05486 }
05487 
05488 /***********************************************************************
05489  *           HTTP_GetResponseHeaders (internal)
05490  *
05491  * Read server response
05492  *
05493  * RETURNS
05494  *
05495  *   TRUE  on success
05496  *   FALSE on error
05497  */
05498 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
05499 {
05500     INT cbreaks = 0;
05501     WCHAR buffer[MAX_REPLY_LEN];
05502     DWORD buflen = MAX_REPLY_LEN;
05503     BOOL bSuccess = FALSE;
05504     INT  rc = 0;
05505     char bufferA[MAX_REPLY_LEN];
05506     LPWSTR status_code = NULL, status_text = NULL;
05507     DWORD cchMaxRawHeaders = 1024;
05508     LPWSTR lpszRawHeaders = NULL;
05509     LPWSTR temp;
05510     DWORD cchRawHeaders = 0;
05511     BOOL codeHundred = FALSE;
05512 
05513     TRACE("-->\n");
05514 
05515     if(!request->netconn)
05516         goto lend;
05517 
05518     do {
05519         static const WCHAR szHundred[] = {'1','0','0',0};
05520         /*
05521          * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
05522          */
05523         buflen = MAX_REPLY_LEN;
05524         if (!read_line(request, bufferA, &buflen))
05525             goto lend;
05526 
05527         /* clear old response headers (eg. from a redirect response) */
05528         if (clear) {
05529             HTTP_clear_response_headers( request );
05530             clear = FALSE;
05531         }
05532 
05533         rc += buflen;
05534         MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
05535         /* check is this a status code line? */
05536         if (!strncmpW(buffer, g_szHttp1_0, 4))
05537         {
05538             /* split the version from the status code */
05539             status_code = strchrW( buffer, ' ' );
05540             if( !status_code )
05541                 goto lend;
05542             *status_code++=0;
05543 
05544             /* split the status code from the status text */
05545             status_text = strchrW( status_code, ' ' );
05546             if( !status_text )
05547                 goto lend;
05548             *status_text++=0;
05549 
05550             TRACE("version [%s] status code [%s] status text [%s]\n",
05551                debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
05552 
05553             codeHundred = (!strcmpW(status_code, szHundred));
05554         }
05555         else if (!codeHundred)
05556         {
05557             WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
05558 
05559             HeapFree(GetProcessHeap(), 0, request->version);
05560             HeapFree(GetProcessHeap(), 0, request->statusText);
05561 
05562             request->version = heap_strdupW(g_szHttp1_0);
05563             request->statusText = heap_strdupW(szOK);
05564 
05565             HeapFree(GetProcessHeap(), 0, request->rawHeaders);
05566             request->rawHeaders = heap_strdupW(szDefaultHeader);
05567 
05568             bSuccess = TRUE;
05569             goto lend;
05570         }
05571     } while (codeHundred);
05572 
05573     /* Add status code */
05574     HTTP_ProcessHeader(request, szStatus, status_code,
05575             HTTP_ADDHDR_FLAG_REPLACE);
05576 
05577     HeapFree(GetProcessHeap(),0,request->version);
05578     HeapFree(GetProcessHeap(),0,request->statusText);
05579 
05580     request->version = heap_strdupW(buffer);
05581     request->statusText = heap_strdupW(status_text);
05582 
05583     /* Restore the spaces */
05584     *(status_code-1) = ' ';
05585     *(status_text-1) = ' ';
05586 
05587     /* regenerate raw headers */
05588     lpszRawHeaders = heap_alloc((cchMaxRawHeaders + 1) * sizeof(WCHAR));
05589     if (!lpszRawHeaders) goto lend;
05590 
05591     while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
05592         cchMaxRawHeaders *= 2;
05593     temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
05594     if (temp == NULL) goto lend;
05595     lpszRawHeaders = temp;
05596     memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
05597     cchRawHeaders += (buflen-1);
05598     memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
05599     cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
05600     lpszRawHeaders[cchRawHeaders] = '\0';
05601 
05602     /* Parse each response line */
05603     do
05604     {
05605     buflen = MAX_REPLY_LEN;
05606         if (read_line(request, bufferA, &buflen))
05607         {
05608             LPWSTR * pFieldAndValue;
05609 
05610             TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
05611 
05612             if (!bufferA[0]) break;
05613             MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
05614 
05615             pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
05616             if (pFieldAndValue)
05617             {
05618                 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
05619                     cchMaxRawHeaders *= 2;
05620                 temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
05621                 if (temp == NULL) goto lend;
05622                 lpszRawHeaders = temp;
05623                 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
05624                 cchRawHeaders += (buflen-1);
05625                 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
05626                 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
05627                 lpszRawHeaders[cchRawHeaders] = '\0';
05628 
05629                 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
05630                                    HTTP_ADDREQ_FLAG_ADD );
05631 
05632                 HTTP_FreeTokens(pFieldAndValue);
05633             }
05634         }
05635     else
05636     {
05637         cbreaks++;
05638         if (cbreaks >= 2)
05639            break;
05640     }
05641     }while(1);
05642 
05643     /* make sure the response header is terminated with an empty line.  Some apps really
05644        truly care about that empty line being there for some reason.  Just add it to the
05645        header. */
05646     if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
05647     {
05648         cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
05649         temp = heap_realloc(lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
05650         if (temp == NULL) goto lend;
05651         lpszRawHeaders = temp;
05652     }
05653 
05654     memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
05655 
05656     HeapFree(GetProcessHeap(), 0, request->rawHeaders);
05657     request->rawHeaders = lpszRawHeaders;
05658     TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
05659     bSuccess = TRUE;
05660 
05661 lend:
05662 
05663     TRACE("<--\n");
05664     if (bSuccess)
05665         return rc;
05666     else
05667     {
05668         HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
05669         return 0;
05670     }
05671 }
05672 
05673 /***********************************************************************
05674  *           HTTP_InterpretHttpHeader (internal)
05675  *
05676  * Parse server response
05677  *
05678  * RETURNS
05679  *
05680  *   Pointer to array of field, value, NULL on success.
05681  *   NULL on error.
05682  */
05683 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
05684 {
05685     LPWSTR * pTokenPair;
05686     LPWSTR pszColon;
05687     INT len;
05688 
05689     pTokenPair = heap_alloc_zero(sizeof(*pTokenPair)*3);
05690 
05691     pszColon = strchrW(buffer, ':');
05692     /* must have two tokens */
05693     if (!pszColon)
05694     {
05695         HTTP_FreeTokens(pTokenPair);
05696         if (buffer[0])
05697             TRACE("No ':' in line: %s\n", debugstr_w(buffer));
05698         return NULL;
05699     }
05700 
05701     pTokenPair[0] = heap_alloc((pszColon - buffer + 1) * sizeof(WCHAR));
05702     if (!pTokenPair[0])
05703     {
05704         HTTP_FreeTokens(pTokenPair);
05705         return NULL;
05706     }
05707     memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
05708     pTokenPair[0][pszColon - buffer] = '\0';
05709 
05710     /* skip colon */
05711     pszColon++;
05712     len = strlenW(pszColon);
05713     pTokenPair[1] = heap_alloc((len + 1) * sizeof(WCHAR));
05714     if (!pTokenPair[1])
05715     {
05716         HTTP_FreeTokens(pTokenPair);
05717         return NULL;
05718     }
05719     memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
05720 
05721     strip_spaces(pTokenPair[0]);
05722     strip_spaces(pTokenPair[1]);
05723 
05724     TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
05725     return pTokenPair;
05726 }
05727 
05728 /***********************************************************************
05729  *           HTTP_ProcessHeader (internal)
05730  *
05731  * Stuff header into header tables according to <dwModifier>
05732  *
05733  */
05734 
05735 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
05736 
05737 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
05738 {
05739     LPHTTPHEADERW lphttpHdr = NULL;
05740     INT index = -1;
05741     BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
05742     DWORD res = ERROR_HTTP_INVALID_HEADER;
05743 
05744     TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
05745 
05746     /* REPLACE wins out over ADD */
05747     if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
05748         dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
05749     
05750     if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
05751         index = -1;
05752     else
05753         index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
05754 
05755     if (index >= 0)
05756     {
05757         if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
05758             return ERROR_HTTP_INVALID_HEADER;
05759         lphttpHdr = &request->custHeaders[index];
05760     }
05761     else if (value)
05762     {
05763         HTTPHEADERW hdr;
05764 
05765         hdr.lpszField = (LPWSTR)field;
05766         hdr.lpszValue = (LPWSTR)value;
05767         hdr.wFlags = hdr.wCount = 0;
05768 
05769         if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
05770             hdr.wFlags |= HDR_ISREQUEST;
05771 
05772         return HTTP_InsertCustomHeader(request, &hdr);
05773     }
05774     /* no value to delete */
05775     else return ERROR_SUCCESS;
05776 
05777     if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
05778         lphttpHdr->wFlags |= HDR_ISREQUEST;
05779     else
05780         lphttpHdr->wFlags &= ~HDR_ISREQUEST;
05781 
05782     if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
05783     {
05784         HTTP_DeleteCustomHeader( request, index );
05785 
05786         if (value)
05787         {
05788             HTTPHEADERW hdr;
05789 
05790             hdr.lpszField = (LPWSTR)field;
05791             hdr.lpszValue = (LPWSTR)value;
05792             hdr.wFlags = hdr.wCount = 0;
05793 
05794             if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
05795                 hdr.wFlags |= HDR_ISREQUEST;
05796 
05797             return HTTP_InsertCustomHeader(request, &hdr);
05798         }
05799 
05800         return ERROR_SUCCESS;
05801     }
05802     else if (dwModifier & COALESCEFLAGS)
05803     {
05804         LPWSTR lpsztmp;
05805         WCHAR ch = 0;
05806         INT len = 0;
05807         INT origlen = strlenW(lphttpHdr->lpszValue);
05808         INT valuelen = strlenW(value);
05809 
05810         if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
05811         {
05812             ch = ',';
05813             lphttpHdr->wFlags |= HDR_COMMADELIMITED;
05814         }
05815         else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
05816         {
05817             ch = ';';
05818             lphttpHdr->wFlags |= HDR_COMMADELIMITED;
05819         }
05820 
05821         len = origlen + valuelen + ((ch > 0) ? 2 : 0);
05822 
05823         lpsztmp = heap_realloc(lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
05824         if (lpsztmp)
05825         {
05826             lphttpHdr->lpszValue = lpsztmp;
05827     /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
05828             if (ch > 0)
05829             {
05830                 lphttpHdr->lpszValue[origlen] = ch;
05831                 origlen++;
05832                 lphttpHdr->lpszValue[origlen] = ' ';
05833                 origlen++;
05834             }
05835 
05836             memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
05837             lphttpHdr->lpszValue[len] = '\0';
05838             res = ERROR_SUCCESS;
05839         }
05840         else
05841         {
05842             WARN("heap_realloc (%d bytes) failed\n",len+1);
05843             res = ERROR_OUTOFMEMORY;
05844         }
05845     }
05846     TRACE("<-- %d\n", res);
05847     return res;
05848 }
05849 
05850 /***********************************************************************
05851  *           HTTP_GetCustomHeaderIndex (internal)
05852  *
05853  * Return index of custom header from header array
05854  *
05855  */
05856 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
05857                                      int requested_index, BOOL request_only)
05858 {
05859     DWORD index;
05860 
05861     TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
05862 
05863     for (index = 0; index < request->nCustHeaders; index++)
05864     {
05865         if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
05866             continue;
05867 
05868         if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
05869             continue;
05870 
05871         if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
05872             continue;
05873 
05874         if (requested_index == 0)
05875             break;
05876         requested_index --;
05877     }
05878 
05879     if (index >= request->nCustHeaders)
05880     index = -1;
05881 
05882     TRACE("Return: %d\n", index);
05883     return index;
05884 }
05885 
05886 
05887 /***********************************************************************
05888  *           HTTP_InsertCustomHeader (internal)
05889  *
05890  * Insert header into array
05891  *
05892  */
05893 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
05894 {
05895     INT count;
05896     LPHTTPHEADERW lph = NULL;
05897 
05898     TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
05899     count = request->nCustHeaders + 1;
05900     if (count > 1)
05901     lph = heap_realloc_zero(request->custHeaders, sizeof(HTTPHEADERW) * count);
05902     else
05903     lph = heap_alloc_zero(sizeof(HTTPHEADERW) * count);
05904 
05905     if (!lph)
05906         return ERROR_OUTOFMEMORY;
05907 
05908     request->custHeaders = lph;
05909     request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
05910     request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
05911     request->custHeaders[count-1].wFlags = lpHdr->wFlags;
05912     request->custHeaders[count-1].wCount= lpHdr->wCount;
05913     request->nCustHeaders++;
05914 
05915     return ERROR_SUCCESS;
05916 }
05917 
05918 
05919 /***********************************************************************
05920  *           HTTP_DeleteCustomHeader (internal)
05921  *
05922  * Delete header from array
05923  *  If this function is called, the indexs may change.
05924  */
05925 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
05926 {
05927     if( request->nCustHeaders <= 0 )
05928         return FALSE;
05929     if( index >= request->nCustHeaders )
05930         return FALSE;
05931     request->nCustHeaders--;
05932 
05933     HeapFree(GetProcessHeap(), 0, request->custHeaders[index].lpszField);
05934     HeapFree(GetProcessHeap(), 0, request->custHeaders[index].lpszValue);
05935 
05936     memmove( &request->custHeaders[index], &request->custHeaders[index+1],
05937              (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
05938     memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
05939 
05940     return TRUE;
05941 }
05942 
05943 
05944 /***********************************************************************
05945  *           HTTP_VerifyValidHeader (internal)
05946  *
05947  * Verify the given header is not invalid for the given http request
05948  *
05949  */
05950 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
05951 {
05952     /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
05953     if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
05954         return ERROR_HTTP_INVALID_HEADER;
05955 
05956     return ERROR_SUCCESS;
05957 }
05958 
05959 /***********************************************************************
05960  *          IsHostInProxyBypassList (@)
05961  *
05962  * Undocumented
05963  *
05964  */
05965 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
05966 {
05967    FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
05968    return FALSE;
05969 }
05970 
05971 /***********************************************************************
05972  *           InternetShowSecurityInfoByURLA (@)
05973  */
05974 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
05975 {
05976    FIXME("stub: %s %p\n", url, window);
05977    return FALSE;
05978 }
05979 
05980 /***********************************************************************
05981  *           InternetShowSecurityInfoByURLW (@)
05982  */
05983 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
05984 {
05985    FIXME("stub: %s %p\n", debugstr_w(url), window);
05986    return FALSE;
05987 }

Generated on Mon May 28 2012 04:26:17 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.