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

request.c
Go to the documentation of this file.
00001 /*
00002  * Copyright 2004 Mike McCormack for CodeWeavers
00003  * Copyright 2006 Rob Shearman for CodeWeavers
00004  * Copyright 2008 Hans Leidekker for CodeWeavers
00005  * Copyright 2009 Juan Lang
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2.1 of the License, or (at your option) any later version.
00011  *
00012  * This library is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this library; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00020  */
00021 
00022 #include "config.h"
00023 #include "wine/port.h"
00024 #include "wine/debug.h"
00025 
00026 #include <stdarg.h>
00027 #ifdef HAVE_ARPA_INET_H
00028 # include <arpa/inet.h>
00029 #endif
00030 
00031 #include "windef.h"
00032 #include "winbase.h"
00033 #include "winhttp.h"
00034 
00035 #include "winhttp_private.h"
00036 
00037 #include "inet_ntop.c"
00038 
00039 WINE_DEFAULT_DEBUG_CHANNEL(winhttp);
00040 
00041 static const WCHAR attr_accept[] = {'A','c','c','e','p','t',0};
00042 static const WCHAR attr_accept_charset[] = {'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0};
00043 static const WCHAR attr_accept_encoding[] = {'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0};
00044 static const WCHAR attr_accept_language[] = {'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0};
00045 static const WCHAR attr_accept_ranges[] = {'A','c','c','e','p','t','-','R','a','n','g','e','s',0};
00046 static const WCHAR attr_age[] = {'A','g','e',0};
00047 static const WCHAR attr_allow[] = {'A','l','l','o','w',0};
00048 static const WCHAR attr_authorization[] = {'A','u','t','h','o','r','i','z','a','t','i','o','n',0};
00049 static const WCHAR attr_cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',0};
00050 static const WCHAR attr_connection[] = {'C','o','n','n','e','c','t','i','o','n',0};
00051 static const WCHAR attr_content_base[] = {'C','o','n','t','e','n','t','-','B','a','s','e',0};
00052 static const WCHAR attr_content_encoding[] = {'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0};
00053 static const WCHAR attr_content_id[] = {'C','o','n','t','e','n','t','-','I','D',0};
00054 static const WCHAR attr_content_language[] = {'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0};
00055 static const WCHAR attr_content_length[] = {'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0};
00056 static const WCHAR attr_content_location[] = {'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0};
00057 static const WCHAR attr_content_md5[] = {'C','o','n','t','e','n','t','-','M','D','5',0};
00058 static const WCHAR attr_content_range[] = {'C','o','n','t','e','n','t','-','R','a','n','g','e',0};
00059 static const WCHAR attr_content_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};
00060 static const WCHAR attr_content_type[] = {'C','o','n','t','e','n','t','-','T','y','p','e',0};
00061 static const WCHAR attr_cookie[] = {'C','o','o','k','i','e',0};
00062 static const WCHAR attr_date[] = {'D','a','t','e',0};
00063 static const WCHAR attr_from[] = {'F','r','o','m',0};
00064 static const WCHAR attr_etag[] = {'E','T','a','g',0};
00065 static const WCHAR attr_expect[] = {'E','x','p','e','c','t',0};
00066 static const WCHAR attr_expires[] = {'E','x','p','i','r','e','s',0};
00067 static const WCHAR attr_host[] = {'H','o','s','t',0};
00068 static const WCHAR attr_if_match[] = {'I','f','-','M','a','t','c','h',0};
00069 static const WCHAR attr_if_modified_since[] = {'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0};
00070 static const WCHAR attr_if_none_match[] = {'I','f','-','N','o','n','e','-','M','a','t','c','h',0};
00071 static const WCHAR attr_if_range[] = {'I','f','-','R','a','n','g','e',0};
00072 static const WCHAR attr_if_unmodified_since[] = {'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0};
00073 static const WCHAR attr_last_modified[] = {'L','a','s','t','-','M','o','d','i','f','i','e','d',0};
00074 static const WCHAR attr_location[] = {'L','o','c','a','t','i','o','n',0};
00075 static const WCHAR attr_max_forwards[] = {'M','a','x','-','F','o','r','w','a','r','d','s',0};
00076 static const WCHAR attr_mime_version[] = {'M','i','m','e','-','V','e','r','s','i','o','n',0};
00077 static const WCHAR attr_pragma[] = {'P','r','a','g','m','a',0};
00078 static const WCHAR attr_proxy_authenticate[] = {'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0};
00079 static const WCHAR attr_proxy_authorization[] = {'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0};
00080 static const WCHAR attr_proxy_connection[] = {'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0};
00081 static const WCHAR attr_public[] = {'P','u','b','l','i','c',0};
00082 static const WCHAR attr_range[] = {'R','a','n','g','e',0};
00083 static const WCHAR attr_referer[] = {'R','e','f','e','r','e','r',0};
00084 static const WCHAR attr_retry_after[] = {'R','e','t','r','y','-','A','f','t','e','r',0};
00085 static const WCHAR attr_server[] = {'S','e','r','v','e','r',0};
00086 static const WCHAR attr_set_cookie[] = {'S','e','t','-','C','o','o','k','i','e',0};
00087 static const WCHAR attr_status[] = {'S','t','a','t','u','s',0};
00088 static const WCHAR attr_transfer_encoding[] = {'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0};
00089 static const WCHAR attr_unless_modified_since[] = {'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0};
00090 static const WCHAR attr_upgrade[] = {'U','p','g','r','a','d','e',0};
00091 static const WCHAR attr_uri[] = {'U','R','I',0};
00092 static const WCHAR attr_user_agent[] = {'U','s','e','r','-','A','g','e','n','t',0};
00093 static const WCHAR attr_vary[] = {'V','a','r','y',0};
00094 static const WCHAR attr_via[] = {'V','i','a',0};
00095 static const WCHAR attr_warning[] = {'W','a','r','n','i','n','g',0};
00096 static const WCHAR attr_www_authenticate[] = {'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0};
00097 
00098 static const WCHAR *attribute_table[] =
00099 {
00100     attr_mime_version,              /* WINHTTP_QUERY_MIME_VERSION               = 0  */
00101     attr_content_type,              /* WINHTTP_QUERY_CONTENT_TYPE               = 1  */
00102     attr_content_transfer_encoding, /* WINHTTP_QUERY_CONTENT_TRANSFER_ENCODING  = 2  */
00103     attr_content_id,                /* WINHTTP_QUERY_CONTENT_ID                 = 3  */
00104     NULL,                           /* WINHTTP_QUERY_CONTENT_DESCRIPTION        = 4  */
00105     attr_content_length,            /* WINHTTP_QUERY_CONTENT_LENGTH             = 5  */
00106     attr_content_language,          /* WINHTTP_QUERY_CONTENT_LANGUAGE           = 6  */
00107     attr_allow,                     /* WINHTTP_QUERY_ALLOW                      = 7  */
00108     attr_public,                    /* WINHTTP_QUERY_PUBLIC                     = 8  */
00109     attr_date,                      /* WINHTTP_QUERY_DATE                       = 9  */
00110     attr_expires,                   /* WINHTTP_QUERY_EXPIRES                    = 10 */
00111     attr_last_modified,             /* WINHTTP_QUERY_LAST_MODIFIEDcw            = 11 */
00112     NULL,                           /* WINHTTP_QUERY_MESSAGE_ID                 = 12 */
00113     attr_uri,                       /* WINHTTP_QUERY_URI                        = 13 */
00114     attr_from,                      /* WINHTTP_QUERY_DERIVED_FROM               = 14 */
00115     NULL,                           /* WINHTTP_QUERY_COST                       = 15 */
00116     NULL,                           /* WINHTTP_QUERY_LINK                       = 16 */
00117     attr_pragma,                    /* WINHTTP_QUERY_PRAGMA                     = 17 */
00118     NULL,                           /* WINHTTP_QUERY_VERSION                    = 18 */
00119     attr_status,                    /* WINHTTP_QUERY_STATUS_CODE                = 19 */
00120     NULL,                           /* WINHTTP_QUERY_STATUS_TEXT                = 20 */
00121     NULL,                           /* WINHTTP_QUERY_RAW_HEADERS                = 21 */
00122     NULL,                           /* WINHTTP_QUERY_RAW_HEADERS_CRLF           = 22 */
00123     attr_connection,                /* WINHTTP_QUERY_CONNECTION                 = 23 */
00124     attr_accept,                    /* WINHTTP_QUERY_ACCEPT                     = 24 */
00125     attr_accept_charset,            /* WINHTTP_QUERY_ACCEPT_CHARSET             = 25 */
00126     attr_accept_encoding,           /* WINHTTP_QUERY_ACCEPT_ENCODING            = 26 */
00127     attr_accept_language,           /* WINHTTP_QUERY_ACCEPT_LANGUAGE            = 27 */
00128     attr_authorization,             /* WINHTTP_QUERY_AUTHORIZATION              = 28 */
00129     attr_content_encoding,          /* WINHTTP_QUERY_CONTENT_ENCODING           = 29 */
00130     NULL,                           /* WINHTTP_QUERY_FORWARDED                  = 30 */
00131     NULL,                           /* WINHTTP_QUERY_FROM                       = 31 */
00132     attr_if_modified_since,         /* WINHTTP_QUERY_IF_MODIFIED_SINCE          = 32 */
00133     attr_location,                  /* WINHTTP_QUERY_LOCATION                   = 33 */
00134     NULL,                           /* WINHTTP_QUERY_ORIG_URI                   = 34 */
00135     attr_referer,                   /* WINHTTP_QUERY_REFERER                    = 35 */
00136     attr_retry_after,               /* WINHTTP_QUERY_RETRY_AFTER                = 36 */
00137     attr_server,                    /* WINHTTP_QUERY_SERVER                     = 37 */
00138     NULL,                           /* WINHTTP_TITLE                            = 38 */
00139     attr_user_agent,                /* WINHTTP_QUERY_USER_AGENT                 = 39 */
00140     attr_www_authenticate,          /* WINHTTP_QUERY_WWW_AUTHENTICATE           = 40 */
00141     attr_proxy_authenticate,        /* WINHTTP_QUERY_PROXY_AUTHENTICATE         = 41 */
00142     attr_accept_ranges,             /* WINHTTP_QUERY_ACCEPT_RANGES              = 42 */
00143     attr_set_cookie,                /* WINHTTP_QUERY_SET_COOKIE                 = 43 */
00144     attr_cookie,                    /* WINHTTP_QUERY_COOKIE                     = 44 */
00145     NULL,                           /* WINHTTP_QUERY_REQUEST_METHOD             = 45 */
00146     NULL,                           /* WINHTTP_QUERY_REFRESH                    = 46 */
00147     NULL,                           /* WINHTTP_QUERY_CONTENT_DISPOSITION        = 47 */
00148     attr_age,                       /* WINHTTP_QUERY_AGE                        = 48 */
00149     attr_cache_control,             /* WINHTTP_QUERY_CACHE_CONTROL              = 49 */
00150     attr_content_base,              /* WINHTTP_QUERY_CONTENT_BASE               = 50 */
00151     attr_content_location,          /* WINHTTP_QUERY_CONTENT_LOCATION           = 51 */
00152     attr_content_md5,               /* WINHTTP_QUERY_CONTENT_MD5                = 52 */
00153     attr_content_range,             /* WINHTTP_QUERY_CONTENT_RANGE              = 53 */
00154     attr_etag,                      /* WINHTTP_QUERY_ETAG                       = 54 */
00155     attr_host,                      /* WINHTTP_QUERY_HOST                       = 55 */
00156     attr_if_match,                  /* WINHTTP_QUERY_IF_MATCH                   = 56 */
00157     attr_if_none_match,             /* WINHTTP_QUERY_IF_NONE_MATCH              = 57 */
00158     attr_if_range,                  /* WINHTTP_QUERY_IF_RANGE                   = 58 */
00159     attr_if_unmodified_since,       /* WINHTTP_QUERY_IF_UNMODIFIED_SINCE        = 59 */
00160     attr_max_forwards,              /* WINHTTP_QUERY_MAX_FORWARDS               = 60 */
00161     attr_proxy_authorization,       /* WINHTTP_QUERY_PROXY_AUTHORIZATION        = 61 */
00162     attr_range,                     /* WINHTTP_QUERY_RANGE                      = 62 */
00163     attr_transfer_encoding,         /* WINHTTP_QUERY_TRANSFER_ENCODING          = 63 */
00164     attr_upgrade,                   /* WINHTTP_QUERY_UPGRADE                    = 64 */
00165     attr_vary,                      /* WINHTTP_QUERY_VARY                       = 65 */
00166     attr_via,                       /* WINHTTP_QUERY_VIA                        = 66 */
00167     attr_warning,                   /* WINHTTP_QUERY_WARNING                    = 67 */
00168     attr_expect,                    /* WINHTTP_QUERY_EXPECT                     = 68 */
00169     attr_proxy_connection,          /* WINHTTP_QUERY_PROXY_CONNECTION           = 69 */
00170     attr_unless_modified_since,     /* WINHTTP_QUERY_UNLESS_MODIFIED_SINCE      = 70 */
00171     NULL,                           /* WINHTTP_QUERY_PROXY_SUPPORT              = 75 */
00172     NULL,                           /* WINHTTP_QUERY_AUTHENTICATION_INFO        = 76 */
00173     NULL,                           /* WINHTTP_QUERY_PASSPORT_URLS              = 77 */
00174     NULL                            /* WINHTTP_QUERY_PASSPORT_CONFIG            = 78 */
00175 };
00176 
00177 static DWORD CALLBACK task_thread( LPVOID param )
00178 {
00179     task_header_t *task = param;
00180 
00181     task->proc( task );
00182 
00183     release_object( &task->request->hdr );
00184     heap_free( task );
00185     return ERROR_SUCCESS;
00186 }
00187 
00188 static BOOL queue_task( task_header_t *task )
00189 {
00190     return QueueUserWorkItem( task_thread, task, WT_EXECUTELONGFUNCTION );
00191 }
00192 
00193 static void free_header( header_t *header )
00194 {
00195     heap_free( header->field );
00196     heap_free( header->value );
00197     heap_free( header );
00198 }
00199 
00200 static BOOL valid_token_char( WCHAR c )
00201 {
00202     if (c < 32 || c == 127) return FALSE;
00203     switch (c)
00204     {
00205     case '(': case ')':
00206     case '<': case '>':
00207     case '@': case ',':
00208     case ';': case ':':
00209     case '\\': case '\"':
00210     case '/': case '[':
00211     case ']': case '?':
00212     case '=': case '{':
00213     case '}': case ' ':
00214     case '\t':
00215         return FALSE;
00216     default:
00217         return TRUE;
00218     }
00219 }
00220 
00221 static header_t *parse_header( LPCWSTR string )
00222 {
00223     const WCHAR *p, *q;
00224     header_t *header;
00225     int len;
00226 
00227     p = string;
00228     if (!(q = strchrW( p, ':' )))
00229     {
00230         WARN("no ':' in line %s\n", debugstr_w(string));
00231         return NULL;
00232     }
00233     if (q == string)
00234     {
00235         WARN("empty field name in line %s\n", debugstr_w(string));
00236         return NULL;
00237     }
00238     while (*p != ':')
00239     {
00240         if (!valid_token_char( *p ))
00241         {
00242             WARN("invalid character in field name %s\n", debugstr_w(string));
00243             return NULL;
00244         }
00245         p++;
00246     }
00247     len = q - string;
00248     if (!(header = heap_alloc_zero( sizeof(header_t) ))) return NULL;
00249     if (!(header->field = heap_alloc( (len + 1) * sizeof(WCHAR) )))
00250     {
00251         heap_free( header );
00252         return NULL;
00253     }
00254     memcpy( header->field, string, len * sizeof(WCHAR) );
00255     header->field[len] = 0;
00256 
00257     q++; /* skip past colon */
00258     while (*q == ' ') q++;
00259     if (!*q)
00260     {
00261         WARN("no value in line %s\n", debugstr_w(string));
00262         return header;
00263     }
00264     len = strlenW( q );
00265     if (!(header->value = heap_alloc( (len + 1) * sizeof(WCHAR) )))
00266     {
00267         free_header( header );
00268         return NULL;
00269     }
00270     memcpy( header->value, q, len * sizeof(WCHAR) );
00271     header->value[len] = 0;
00272 
00273     return header;
00274 }
00275 
00276 static int get_header_index( request_t *request, LPCWSTR field, int requested_index, BOOL request_only )
00277 {
00278     int index;
00279 
00280     TRACE("%s\n", debugstr_w(field));
00281 
00282     for (index = 0; index < request->num_headers; index++)
00283     {
00284         if (strcmpiW( request->headers[index].field, field )) continue;
00285         if (request_only && !request->headers[index].is_request) continue;
00286         if (!request_only && request->headers[index].is_request) continue;
00287 
00288         if (!requested_index) break;
00289         requested_index--;
00290     }
00291     if (index >= request->num_headers) index = -1;
00292     TRACE("returning %d\n", index);
00293     return index;
00294 }
00295 
00296 static BOOL insert_header( request_t *request, header_t *header )
00297 {
00298     DWORD count;
00299     header_t *hdrs;
00300 
00301     count = request->num_headers + 1;
00302     if (count > 1)
00303         hdrs = heap_realloc_zero( request->headers, sizeof(header_t) * count );
00304     else
00305         hdrs = heap_alloc_zero( sizeof(header_t) * count );
00306 
00307     if (hdrs)
00308     {
00309         request->headers = hdrs;
00310         request->headers[count - 1].field = strdupW( header->field );
00311         request->headers[count - 1].value = strdupW( header->value );
00312         request->headers[count - 1].is_request = header->is_request;
00313         request->num_headers++;
00314         return TRUE;
00315     }
00316     return FALSE;
00317 }
00318 
00319 static BOOL delete_header( request_t *request, DWORD index )
00320 {
00321     if (!request->num_headers) return FALSE;
00322     if (index >= request->num_headers) return FALSE;
00323     request->num_headers--;
00324 
00325     heap_free( request->headers[index].field );
00326     heap_free( request->headers[index].value );
00327 
00328     memmove( &request->headers[index], &request->headers[index + 1], (request->num_headers - index) * sizeof(header_t) );
00329     memset( &request->headers[request->num_headers], 0, sizeof(header_t) );
00330     return TRUE;
00331 }
00332 
00333 static BOOL process_header( request_t *request, LPCWSTR field, LPCWSTR value, DWORD flags, BOOL request_only )
00334 {
00335     int index;
00336     header_t *header;
00337 
00338     TRACE("%s: %s 0x%08x\n", debugstr_w(field), debugstr_w(value), flags);
00339 
00340     /* replace wins out over add */
00341     if (flags & WINHTTP_ADDREQ_FLAG_REPLACE) flags &= ~WINHTTP_ADDREQ_FLAG_ADD;
00342 
00343     if (flags & WINHTTP_ADDREQ_FLAG_ADD) index = -1;
00344     else
00345         index = get_header_index( request, field, 0, request_only );
00346 
00347     if (index >= 0)
00348     {
00349         if (flags & WINHTTP_ADDREQ_FLAG_ADD_IF_NEW) return FALSE;
00350         header = &request->headers[index];
00351     }
00352     else if (value)
00353     {
00354         header_t hdr;
00355 
00356         hdr.field = (LPWSTR)field;
00357         hdr.value = (LPWSTR)value;
00358         hdr.is_request = request_only;
00359 
00360         return insert_header( request, &hdr );
00361     }
00362     /* no value to delete */
00363     else return TRUE;
00364 
00365     if (flags & WINHTTP_ADDREQ_FLAG_REPLACE)
00366     {
00367         delete_header( request, index );
00368         if (value)
00369         {
00370             header_t hdr;
00371 
00372             hdr.field = (LPWSTR)field;
00373             hdr.value = (LPWSTR)value;
00374             hdr.is_request = request_only;
00375 
00376             return insert_header( request, &hdr );
00377         }
00378         return TRUE;
00379     }
00380     else if (flags & (WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA | WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON))
00381     {
00382         WCHAR sep, *tmp;
00383         int len, orig_len, value_len;
00384 
00385         orig_len = strlenW( header->value );
00386         value_len = strlenW( value );
00387 
00388         if (flags & WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA) sep = ',';
00389         else sep = ';';
00390 
00391         len = orig_len + value_len + 2;
00392         if ((tmp = heap_realloc( header->value, (len + 1) * sizeof(WCHAR) )))
00393         {
00394             header->value = tmp;
00395 
00396             header->value[orig_len] = sep;
00397             orig_len++;
00398             header->value[orig_len] = ' ';
00399             orig_len++;
00400 
00401             memcpy( &header->value[orig_len], value, value_len * sizeof(WCHAR) );
00402             header->value[len] = 0;
00403             return TRUE;
00404         }
00405     }
00406     return TRUE;
00407 }
00408 
00409 BOOL add_request_headers( request_t *request, LPCWSTR headers, DWORD len, DWORD flags )
00410 {
00411     BOOL ret = FALSE;
00412     WCHAR *buffer, *p, *q;
00413     header_t *header;
00414 
00415     if (len == ~0u) len = strlenW( headers );
00416     if (!len) return TRUE;
00417     if (!(buffer = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return FALSE;
00418     strcpyW( buffer, headers );
00419 
00420     p = buffer;
00421     do
00422     {
00423         q = p;
00424         while (*q)
00425         {
00426             if (q[0] == '\r' && q[1] == '\n') break;
00427             q++;
00428         }
00429         if (!*p) break;
00430         if (*q == '\r')
00431         {
00432             *q = 0;
00433             q += 2; /* jump over \r\n */
00434         }
00435         if ((header = parse_header( p )))
00436         {
00437             ret = process_header( request, header->field, header->value, flags, TRUE );
00438             free_header( header );
00439         }
00440         p = q;
00441     } while (ret);
00442 
00443     heap_free( buffer );
00444     return ret;
00445 }
00446 
00447 /***********************************************************************
00448  *          WinHttpAddRequestHeaders (winhttp.@)
00449  */
00450 BOOL WINAPI WinHttpAddRequestHeaders( HINTERNET hrequest, LPCWSTR headers, DWORD len, DWORD flags )
00451 {
00452     BOOL ret;
00453     request_t *request;
00454 
00455     TRACE("%p, %s, 0x%x, 0x%08x\n", hrequest, debugstr_w(headers), len, flags);
00456 
00457     if (!headers)
00458     {
00459         set_last_error( ERROR_INVALID_PARAMETER );
00460         return FALSE;
00461     }
00462     if (!(request = (request_t *)grab_object( hrequest )))
00463     {
00464         set_last_error( ERROR_INVALID_HANDLE );
00465         return FALSE;
00466     }
00467     if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
00468     {
00469         release_object( &request->hdr );
00470         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
00471         return FALSE;
00472     }
00473 
00474     ret = add_request_headers( request, headers, len, flags );
00475 
00476     release_object( &request->hdr );
00477     return ret;
00478 }
00479 
00480 static WCHAR *build_request_path( request_t *request )
00481 {
00482     WCHAR *ret;
00483 
00484     if (strcmpiW( request->connect->hostname, request->connect->servername ))
00485     {
00486         static const WCHAR http[] = { 'h','t','t','p',0 };
00487         static const WCHAR https[] = { 'h','t','t','p','s',0 };
00488         static const WCHAR fmt[] = { '%','s',':','/','/','%','s',0 };
00489         LPCWSTR scheme = request->netconn.secure ? https : http;
00490         int len;
00491 
00492         len = strlenW( scheme ) + strlenW( request->connect->hostname );
00493         /* 3 characters for '://', 1 for NUL. */
00494         len += 4;
00495         if (request->connect->hostport)
00496         {
00497             /* 1 for ':' between host and port, up to 5 for port */
00498             len += 6;
00499         }
00500         if (request->path)
00501             len += strlenW( request->path );
00502         if ((ret = heap_alloc( len * sizeof(WCHAR) )))
00503         {
00504             sprintfW( ret, fmt, scheme, request->connect->hostname );
00505             if (request->connect->hostport)
00506             {
00507                 static const WCHAR colonFmt[] = { ':','%','u',0 };
00508 
00509                 sprintfW( ret + strlenW( ret ), colonFmt,
00510                     request->connect->hostport );
00511             }
00512             if (request->path)
00513                 strcatW( ret, request->path );
00514         }
00515     }
00516     else
00517         ret = request->path;
00518     return ret;
00519 }
00520 
00521 static WCHAR *build_request_string( request_t *request )
00522 {
00523     static const WCHAR space[]   = {' ',0};
00524     static const WCHAR crlf[]    = {'\r','\n',0};
00525     static const WCHAR colon[]   = {':',' ',0};
00526     static const WCHAR twocrlf[] = {'\r','\n','\r','\n',0};
00527 
00528     WCHAR *path, *ret;
00529     const WCHAR **headers, **p;
00530     unsigned int len, i = 0, j;
00531 
00532     /* allocate space for an array of all the string pointers to be added */
00533     len = request->num_headers * 4 + 7;
00534     if (!(headers = heap_alloc( len * sizeof(LPCWSTR) ))) return NULL;
00535 
00536     path = build_request_path( request );
00537     headers[i++] = request->verb;
00538     headers[i++] = space;
00539     headers[i++] = path;
00540     headers[i++] = space;
00541     headers[i++] = request->version;
00542 
00543     for (j = 0; j < request->num_headers; j++)
00544     {
00545         if (request->headers[j].is_request)
00546         {
00547             headers[i++] = crlf;
00548             headers[i++] = request->headers[j].field;
00549             headers[i++] = colon;
00550             headers[i++] = request->headers[j].value;
00551 
00552             TRACE("adding header %s (%s)\n", debugstr_w(request->headers[j].field),
00553                   debugstr_w(request->headers[j].value));
00554         }
00555     }
00556     headers[i++] = twocrlf;
00557     headers[i] = NULL;
00558 
00559     len = 0;
00560     for (p = headers; *p; p++) len += strlenW( *p );
00561     len++;
00562 
00563     if (!(ret = heap_alloc( len * sizeof(WCHAR) )))
00564         goto out;
00565     *ret = 0;
00566     for (p = headers; *p; p++) strcatW( ret, *p );
00567 
00568 out:
00569     if (path != request->path)
00570         heap_free( path );
00571     heap_free( headers );
00572     return ret;
00573 }
00574 
00575 #define QUERY_MODIFIER_MASK (WINHTTP_QUERY_FLAG_REQUEST_HEADERS | WINHTTP_QUERY_FLAG_SYSTEMTIME | WINHTTP_QUERY_FLAG_NUMBER)
00576 
00577 static BOOL query_headers( request_t *request, DWORD level, LPCWSTR name, LPVOID buffer, LPDWORD buflen, LPDWORD index )
00578 {
00579     header_t *header = NULL;
00580     BOOL request_only, ret = FALSE;
00581     int requested_index, header_index = -1;
00582     DWORD attr, len;
00583 
00584     request_only = level & WINHTTP_QUERY_FLAG_REQUEST_HEADERS;
00585     requested_index = index ? *index : 0;
00586 
00587     attr = level & ~QUERY_MODIFIER_MASK;
00588     switch (attr)
00589     {
00590     case WINHTTP_QUERY_CUSTOM:
00591     {
00592         header_index = get_header_index( request, name, requested_index, request_only );
00593         break;
00594     }
00595     case WINHTTP_QUERY_RAW_HEADERS:
00596     {
00597         WCHAR *headers, *p, *q;
00598 
00599         if (request_only)
00600             headers = build_request_string( request );
00601         else
00602             headers = request->raw_headers;
00603 
00604         if (!(p = headers)) return FALSE;
00605         for (len = 0; *p; p++) if (*p != '\r') len++;
00606 
00607         if (!buffer || (len + 1) * sizeof(WCHAR) > *buflen)
00608         {
00609             len++;
00610             set_last_error( ERROR_INSUFFICIENT_BUFFER );
00611         }
00612         else
00613         {
00614             for (p = headers, q = buffer; *p; p++, q++)
00615             {
00616                 if (*p != '\r') *q = *p;
00617                 else
00618                 {
00619                     *q = 0;
00620                     p++; /* skip '\n' */
00621                 }
00622             }
00623             *q = 0;
00624             TRACE("returning data: %s\n", debugstr_wn(buffer, len));
00625             ret = TRUE;
00626         }
00627         *buflen = len * sizeof(WCHAR);
00628         if (request_only) heap_free( headers );
00629         return ret;
00630     }
00631     case WINHTTP_QUERY_RAW_HEADERS_CRLF:
00632     {
00633         WCHAR *headers;
00634 
00635         if (request_only)
00636             headers = build_request_string( request );
00637         else
00638             headers = request->raw_headers;
00639 
00640         if (!headers) return FALSE;
00641         len = strlenW( headers ) * sizeof(WCHAR);
00642         if (!buffer || len + sizeof(WCHAR) > *buflen)
00643         {
00644             len += sizeof(WCHAR);
00645             set_last_error( ERROR_INSUFFICIENT_BUFFER );
00646         }
00647         else
00648         {
00649             memcpy( buffer, headers, len + sizeof(WCHAR) );
00650             TRACE("returning data: %s\n", debugstr_wn(buffer, len / sizeof(WCHAR)));
00651             ret = TRUE;
00652         }
00653         *buflen = len;
00654         if (request_only) heap_free( headers );
00655         return ret;
00656     }
00657     case WINHTTP_QUERY_VERSION:
00658         len = strlenW( request->version ) * sizeof(WCHAR);
00659         if (!buffer || len + sizeof(WCHAR) > *buflen)
00660         {
00661             len += sizeof(WCHAR);
00662             set_last_error( ERROR_INSUFFICIENT_BUFFER );
00663         }
00664         else
00665         {
00666             strcpyW( buffer, request->version );
00667             TRACE("returning string: %s\n", debugstr_w(buffer));
00668             ret = TRUE;
00669         }
00670         *buflen = len;
00671         return ret;
00672 
00673     case WINHTTP_QUERY_STATUS_TEXT:
00674         len = strlenW( request->status_text ) * sizeof(WCHAR);
00675         if (!buffer || len + sizeof(WCHAR) > *buflen)
00676         {
00677             len += sizeof(WCHAR);
00678             set_last_error( ERROR_INSUFFICIENT_BUFFER );
00679         }
00680         else
00681         {
00682             strcpyW( buffer, request->status_text );
00683             TRACE("returning string: %s\n", debugstr_w(buffer));
00684             ret = TRUE;
00685         }
00686         *buflen = len;
00687         return ret;
00688 
00689     default:
00690         if (attr >= sizeof(attribute_table)/sizeof(attribute_table[0]) || !attribute_table[attr])
00691         {
00692             FIXME("attribute %u not implemented\n", attr);
00693             return FALSE;
00694         }
00695         TRACE("attribute %s\n", debugstr_w(attribute_table[attr]));
00696         header_index = get_header_index( request, attribute_table[attr], requested_index, request_only );
00697         break;
00698     }
00699 
00700     if (header_index >= 0)
00701     {
00702         header = &request->headers[header_index];
00703     }
00704     if (!header || (request_only && !header->is_request))
00705     {
00706         set_last_error( ERROR_WINHTTP_HEADER_NOT_FOUND );
00707         return FALSE;
00708     }
00709     if (index) *index += 1;
00710     if (level & WINHTTP_QUERY_FLAG_NUMBER)
00711     {
00712         if (!buffer || sizeof(int) > *buflen)
00713         {
00714             set_last_error( ERROR_INSUFFICIENT_BUFFER );
00715         }
00716         else
00717         {
00718             int *number = buffer;
00719             *number = atoiW( header->value );
00720             TRACE("returning number: %d\n", *number);
00721             ret = TRUE;
00722         }
00723         *buflen = sizeof(int);
00724     }
00725     else if (level & WINHTTP_QUERY_FLAG_SYSTEMTIME)
00726     {
00727         SYSTEMTIME *st = buffer;
00728         if (!buffer || sizeof(SYSTEMTIME) > *buflen)
00729         {
00730             set_last_error( ERROR_INSUFFICIENT_BUFFER );
00731         }
00732         else if ((ret = WinHttpTimeToSystemTime( header->value, st )))
00733         {
00734             TRACE("returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
00735                   st->wYear, st->wMonth, st->wDay, st->wDayOfWeek,
00736                   st->wHour, st->wMinute, st->wSecond, st->wMilliseconds);
00737         }
00738         *buflen = sizeof(SYSTEMTIME);
00739     }
00740     else if (header->value)
00741     {
00742         len = strlenW( header->value ) * sizeof(WCHAR);
00743         if (!buffer || len + sizeof(WCHAR) > *buflen)
00744         {
00745             len += sizeof(WCHAR);
00746             set_last_error( ERROR_INSUFFICIENT_BUFFER );
00747         }
00748         else
00749         {
00750             strcpyW( buffer, header->value );
00751             TRACE("returning string: %s\n", debugstr_w(buffer));
00752             ret = TRUE;
00753         }
00754         *buflen = len;
00755     }
00756     return ret;
00757 }
00758 
00759 /***********************************************************************
00760  *          WinHttpQueryHeaders (winhttp.@)
00761  */
00762 BOOL WINAPI WinHttpQueryHeaders( HINTERNET hrequest, DWORD level, LPCWSTR name, LPVOID buffer, LPDWORD buflen, LPDWORD index )
00763 {
00764     BOOL ret;
00765     request_t *request;
00766 
00767     TRACE("%p, 0x%08x, %s, %p, %p, %p\n", hrequest, level, debugstr_w(name), buffer, buflen, index);
00768 
00769     if (!(request = (request_t *)grab_object( hrequest )))
00770     {
00771         set_last_error( ERROR_INVALID_HANDLE );
00772         return FALSE;
00773     }
00774     if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
00775     {
00776         release_object( &request->hdr );
00777         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
00778         return FALSE;
00779     }
00780 
00781     ret = query_headers( request, level, name, buffer, buflen, index );
00782 
00783     release_object( &request->hdr );
00784     return ret;
00785 }
00786 
00787 static LPWSTR concatenate_string_list( LPCWSTR *list, int len )
00788 {
00789     LPCWSTR *t;
00790     LPWSTR str;
00791 
00792     for( t = list; *t ; t++  )
00793         len += strlenW( *t );
00794     len++;
00795 
00796     str = heap_alloc( len * sizeof(WCHAR) );
00797     if (!str) return NULL;
00798     *str = 0;
00799 
00800     for( t = list; *t ; t++ )
00801         strcatW( str, *t );
00802 
00803     return str;
00804 }
00805 
00806 static LPWSTR build_header_request_string( request_t *request, LPCWSTR verb,
00807     LPCWSTR path, LPCWSTR version )
00808 {
00809     static const WCHAR crlf[] = {'\r','\n',0};
00810     static const WCHAR space[] = { ' ',0 };
00811     static const WCHAR colon[] = { ':',' ',0 };
00812     static const WCHAR twocrlf[] = {'\r','\n','\r','\n', 0};
00813     LPWSTR requestString;
00814     DWORD len, n;
00815     LPCWSTR *req;
00816     UINT i;
00817     LPWSTR p;
00818 
00819     /* allocate space for an array of all the string pointers to be added */
00820     len = (request->num_headers) * 4 + 10;
00821     req = heap_alloc( len * sizeof(LPCWSTR) );
00822     if (!req) return NULL;
00823 
00824     /* add the verb, path and HTTP version string */
00825     n = 0;
00826     req[n++] = verb;
00827     req[n++] = space;
00828     req[n++] = path;
00829     req[n++] = space;
00830     req[n++] = version;
00831 
00832     /* Append custom request headers */
00833     for (i = 0; i < request->num_headers; i++)
00834     {
00835         if (request->headers[i].is_request)
00836         {
00837             req[n++] = crlf;
00838             req[n++] = request->headers[i].field;
00839             req[n++] = colon;
00840             req[n++] = request->headers[i].value;
00841 
00842             TRACE("Adding custom header %s (%s)\n",
00843                    debugstr_w(request->headers[i].field),
00844                    debugstr_w(request->headers[i].value));
00845         }
00846     }
00847 
00848     if( n >= len )
00849         ERR("oops. buffer overrun\n");
00850 
00851     req[n] = NULL;
00852     requestString = concatenate_string_list( req, 4 );
00853     heap_free( req );
00854     if (!requestString) return NULL;
00855 
00856     /*
00857      * Set (header) termination string for request
00858      * Make sure there's exactly two new lines at the end of the request
00859      */
00860     p = &requestString[strlenW(requestString)-1];
00861     while ( (*p == '\n') || (*p == '\r') )
00862        p--;
00863     strcpyW( p+1, twocrlf );
00864 
00865     return requestString;
00866 }
00867 
00868 static BOOL read_reply( request_t *request );
00869 
00870 static BOOL secure_proxy_connect( request_t *request )
00871 {
00872     static const WCHAR verbConnect[] = {'C','O','N','N','E','C','T',0};
00873     static const WCHAR fmt[] = {'%','s',':','%','u',0};
00874     BOOL ret = FALSE;
00875     LPWSTR path;
00876     connect_t *connect = request->connect;
00877 
00878     path = heap_alloc( (strlenW( connect->hostname ) + 13) * sizeof(WCHAR) );
00879     if (path)
00880     {
00881         LPWSTR requestString;
00882 
00883         sprintfW( path, fmt, connect->hostname, connect->hostport );
00884         requestString = build_header_request_string( request, verbConnect,
00885             path, http1_1 );
00886         heap_free( path );
00887         if (requestString)
00888         {
00889             LPSTR req_ascii = strdupWA( requestString );
00890 
00891             heap_free( requestString );
00892             if (req_ascii)
00893             {
00894                 int len = strlen( req_ascii ), bytes_sent;
00895 
00896                 ret = netconn_send( &request->netconn, req_ascii, len, 0, &bytes_sent );
00897                 heap_free( req_ascii );
00898                 if (ret)
00899                     ret = read_reply( request );
00900             }
00901         }
00902     }
00903     return ret;
00904 }
00905 
00906 #ifndef INET6_ADDRSTRLEN
00907 #define INET6_ADDRSTRLEN 46
00908 #endif
00909 
00910 static BOOL open_connection( request_t *request )
00911 {
00912     connect_t *connect;
00913     const void *addr;
00914     char address[INET6_ADDRSTRLEN];
00915     WCHAR *addressW;
00916     INTERNET_PORT port;
00917     socklen_t slen;
00918 
00919     if (netconn_connected( &request->netconn )) return TRUE;
00920 
00921     connect = request->connect;
00922     port = connect->serverport ? connect->serverport : (request->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80);
00923 
00924     send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, connect->servername, strlenW(connect->servername) + 1 );
00925 
00926     slen = sizeof(connect->sockaddr);
00927     if (!netconn_resolve( connect->servername, port, (struct sockaddr *)&connect->sockaddr, &slen, request->resolve_timeout )) return FALSE;
00928     switch (connect->sockaddr.ss_family)
00929     {
00930     case AF_INET:
00931         addr = &((struct sockaddr_in *)&connect->sockaddr)->sin_addr;
00932         break;
00933     case AF_INET6:
00934         addr = &((struct sockaddr_in6 *)&connect->sockaddr)->sin6_addr;
00935         break;
00936     default:
00937         WARN("unsupported address family %d\n", connect->sockaddr.ss_family);
00938         return FALSE;
00939     }
00940     inet_ntop( connect->sockaddr.ss_family, addr, address, sizeof(address) );
00941     addressW = strdupAW( address );
00942 
00943     send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, addressW, strlenW(addressW) + 1 );
00944 
00945     TRACE("connecting to %s:%u\n", address, port);
00946 
00947     send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, addressW, 0 );
00948 
00949     if (!netconn_create( &request->netconn, connect->sockaddr.ss_family, SOCK_STREAM, 0 ))
00950     {
00951         heap_free( addressW );
00952         return FALSE;
00953     }
00954     netconn_set_timeout( &request->netconn, TRUE, request->send_timeout );
00955     netconn_set_timeout( &request->netconn, FALSE, request->recv_timeout );
00956     if (!netconn_connect( &request->netconn, (struct sockaddr *)&connect->sockaddr, slen, request->connect_timeout ))
00957     {
00958         netconn_close( &request->netconn );
00959         heap_free( addressW );
00960         return FALSE;
00961     }
00962     if (request->hdr.flags & WINHTTP_FLAG_SECURE)
00963     {
00964         if (connect->session->proxy_server &&
00965             strcmpiW( connect->hostname, connect->servername ))
00966         {
00967             if (!secure_proxy_connect( request ))
00968             {
00969                 heap_free( addressW );
00970                 return FALSE;
00971             }
00972         }
00973         if (!netconn_secure_connect( &request->netconn, connect->servername ))
00974         {
00975             netconn_close( &request->netconn );
00976             heap_free( addressW );
00977             return FALSE;
00978         }
00979     }
00980 
00981     send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, addressW, strlenW(addressW) + 1 );
00982 
00983     heap_free( addressW );
00984     return TRUE;
00985 }
00986 
00987 void close_connection( request_t *request )
00988 {
00989     if (!netconn_connected( &request->netconn )) return;
00990 
00991     send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, 0, 0 );
00992     netconn_close( &request->netconn );
00993     send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, 0, 0 );
00994 }
00995 
00996 static BOOL add_host_header( request_t *request, DWORD modifier )
00997 {
00998     BOOL ret;
00999     DWORD len;
01000     WCHAR *host;
01001     static const WCHAR fmt[] = {'%','s',':','%','u',0};
01002     connect_t *connect = request->connect;
01003     INTERNET_PORT port;
01004 
01005     port = connect->hostport ? connect->hostport : (request->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80);
01006 
01007     if (port == INTERNET_DEFAULT_HTTP_PORT || port == INTERNET_DEFAULT_HTTPS_PORT)
01008     {
01009         return process_header( request, attr_host, connect->hostname, modifier, TRUE );
01010     }
01011     len = strlenW( connect->hostname ) + 7; /* sizeof(":65335") */
01012     if (!(host = heap_alloc( len * sizeof(WCHAR) ))) return FALSE;
01013     sprintfW( host, fmt, connect->hostname, port );
01014     ret = process_header( request, attr_host, host, modifier, TRUE );
01015     heap_free( host );
01016     return ret;
01017 }
01018 
01019 static BOOL send_request( request_t *request, LPCWSTR headers, DWORD headers_len, LPVOID optional,
01020                           DWORD optional_len, DWORD total_len, DWORD_PTR context, BOOL async )
01021 {
01022     static const WCHAR keep_alive[] = {'K','e','e','p','-','A','l','i','v','e',0};
01023     static const WCHAR no_cache[]   = {'n','o','-','c','a','c','h','e',0};
01024     static const WCHAR length_fmt[] = {'%','l','d',0};
01025 
01026     BOOL ret = FALSE;
01027     connect_t *connect = request->connect;
01028     session_t *session = connect->session;
01029     WCHAR *req = NULL;
01030     char *req_ascii;
01031     int bytes_sent;
01032     DWORD len;
01033 
01034     if (session->agent)
01035         process_header( request, attr_user_agent, session->agent, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
01036 
01037     if (connect->hostname)
01038         add_host_header( request, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW );
01039 
01040     if (total_len || (request->verb && !strcmpW( request->verb, postW )))
01041     {
01042         WCHAR length[21]; /* decimal long int + null */
01043         sprintfW( length, length_fmt, total_len );
01044         process_header( request, attr_content_length, length, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
01045     }
01046     if (!(request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE))
01047     {
01048         process_header( request, attr_connection, keep_alive, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
01049     }
01050     if (request->hdr.flags & WINHTTP_FLAG_REFRESH)
01051     {
01052         process_header( request, attr_pragma, no_cache, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
01053         process_header( request, attr_cache_control, no_cache, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
01054     }
01055     if (headers && !add_request_headers( request, headers, headers_len, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE ))
01056     {
01057         TRACE("failed to add request headers\n");
01058         return FALSE;
01059     }
01060     if (!(request->hdr.disable_flags & WINHTTP_DISABLE_COOKIES) && !add_cookie_headers( request ))
01061     {
01062         WARN("failed to add cookie headers\n");
01063         return FALSE;
01064     }
01065 
01066     if (!(ret = open_connection( request ))) goto end;
01067     if (!(req = build_request_string( request ))) goto end;
01068 
01069     if (!(req_ascii = strdupWA( req ))) goto end;
01070     TRACE("full request: %s\n", debugstr_a(req_ascii));
01071     len = strlen(req_ascii);
01072 
01073     if (context) request->hdr.context = context;
01074     send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST, NULL, 0 );
01075 
01076     ret = netconn_send( &request->netconn, req_ascii, len, 0, &bytes_sent );
01077     heap_free( req_ascii );
01078     if (!ret) goto end;
01079 
01080     if (optional_len && !netconn_send( &request->netconn, optional, optional_len, 0, &bytes_sent )) goto end;
01081     len += optional_len;
01082 
01083     send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_SENT, &len, sizeof(DWORD) );
01084 
01085 end:
01086     if (async)
01087     {
01088         if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, NULL, 0 );
01089         else
01090         {
01091             WINHTTP_ASYNC_RESULT result;
01092             result.dwResult = API_SEND_REQUEST;
01093             result.dwError  = get_last_error();
01094             send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
01095         }
01096     }
01097     heap_free( req );
01098     return ret;
01099 }
01100 
01101 static void task_send_request( task_header_t *task )
01102 {
01103     send_request_t *s = (send_request_t *)task;
01104     send_request( s->hdr.request, s->headers, s->headers_len, s->optional, s->optional_len, s->total_len, s->context, TRUE );
01105     heap_free( s->headers );
01106 }
01107 
01108 /***********************************************************************
01109  *          WinHttpSendRequest (winhttp.@)
01110  */
01111 BOOL WINAPI WinHttpSendRequest( HINTERNET hrequest, LPCWSTR headers, DWORD headers_len,
01112                                 LPVOID optional, DWORD optional_len, DWORD total_len, DWORD_PTR context )
01113 {
01114     BOOL ret;
01115     request_t *request;
01116 
01117     TRACE("%p, %s, 0x%x, %u, %u, %lx\n",
01118           hrequest, debugstr_w(headers), headers_len, optional_len, total_len, context);
01119 
01120     if (!(request = (request_t *)grab_object( hrequest )))
01121     {
01122         set_last_error( ERROR_INVALID_HANDLE );
01123         return FALSE;
01124     }
01125     if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
01126     {
01127         release_object( &request->hdr );
01128         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
01129         return FALSE;
01130     }
01131 
01132     if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
01133     {
01134         send_request_t *s;
01135 
01136         if (!(s = heap_alloc( sizeof(send_request_t) ))) return FALSE;
01137         s->hdr.request  = request;
01138         s->hdr.proc     = task_send_request;
01139         s->headers      = strdupW( headers );
01140         s->headers_len  = headers_len;
01141         s->optional     = optional;
01142         s->optional_len = optional_len;
01143         s->total_len    = total_len;
01144         s->context      = context;
01145 
01146         addref_object( &request->hdr );
01147         ret = queue_task( (task_header_t *)s );
01148     }
01149     else
01150         ret = send_request( request, headers, headers_len, optional, optional_len, total_len, context, FALSE );
01151 
01152     release_object( &request->hdr );
01153     return ret;
01154 }
01155 
01156 #define ARRAYSIZE(array) (sizeof(array) / sizeof((array)[0]))
01157 
01158 static DWORD auth_scheme_from_header( WCHAR *header )
01159 {
01160     static const WCHAR basic[]     = {'B','a','s','i','c'};
01161     static const WCHAR ntlm[]      = {'N','T','L','M'};
01162     static const WCHAR passport[]  = {'P','a','s','s','p','o','r','t'};
01163     static const WCHAR digest[]    = {'D','i','g','e','s','t'};
01164     static const WCHAR negotiate[] = {'N','e','g','o','t','i','a','t','e'};
01165 
01166     if (!strncmpiW( header, basic, ARRAYSIZE(basic) ) &&
01167         (header[ARRAYSIZE(basic)] == ' ' || !header[ARRAYSIZE(basic)])) return WINHTTP_AUTH_SCHEME_BASIC;
01168 
01169     if (!strncmpiW( header, ntlm, ARRAYSIZE(ntlm) ) &&
01170         (header[ARRAYSIZE(ntlm)] == ' ' || !header[ARRAYSIZE(ntlm)])) return WINHTTP_AUTH_SCHEME_NTLM;
01171 
01172     if (!strncmpiW( header, passport, ARRAYSIZE(passport) ) &&
01173         (header[ARRAYSIZE(passport)] == ' ' || !header[ARRAYSIZE(passport)])) return WINHTTP_AUTH_SCHEME_PASSPORT;
01174 
01175     if (!strncmpiW( header, digest, ARRAYSIZE(digest) ) &&
01176         (header[ARRAYSIZE(digest)] == ' ' || !header[ARRAYSIZE(digest)])) return WINHTTP_AUTH_SCHEME_DIGEST;
01177 
01178     if (!strncmpiW( header, negotiate, ARRAYSIZE(negotiate) ) &&
01179         (header[ARRAYSIZE(negotiate)] == ' ' || !header[ARRAYSIZE(negotiate)])) return WINHTTP_AUTH_SCHEME_NEGOTIATE;
01180 
01181     return 0;
01182 }
01183 
01184 static BOOL query_auth_schemes( request_t *request, DWORD level, LPDWORD supported, LPDWORD first )
01185 {
01186     DWORD index = 0;
01187     BOOL ret = FALSE;
01188 
01189     for (;;)
01190     {
01191         WCHAR *buffer;
01192         DWORD size, scheme;
01193 
01194         size = 0;
01195         query_headers( request, level, NULL, NULL, &size, &index );
01196         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) break;
01197 
01198         index--;
01199         if (!(buffer = heap_alloc( size ))) return FALSE;
01200         if (!query_headers( request, level, NULL, buffer, &size, &index ))
01201         {
01202             heap_free( buffer );
01203             return FALSE;
01204         }
01205         scheme = auth_scheme_from_header( buffer );
01206         if (first && index == 1) *first = scheme;
01207         *supported |= scheme;
01208 
01209         heap_free( buffer );
01210         ret = TRUE;
01211     }
01212     return ret;
01213 }
01214 
01215 /***********************************************************************
01216  *          WinHttpQueryAuthSchemes (winhttp.@)
01217  */
01218 BOOL WINAPI WinHttpQueryAuthSchemes( HINTERNET hrequest, LPDWORD supported, LPDWORD first, LPDWORD target )
01219 {
01220     BOOL ret = FALSE;
01221     request_t *request;
01222 
01223     TRACE("%p, %p, %p, %p\n", hrequest, supported, first, target);
01224 
01225     if (!(request = (request_t *)grab_object( hrequest )))
01226     {
01227         set_last_error( ERROR_INVALID_HANDLE );
01228         return FALSE;
01229     }
01230     if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
01231     {
01232         release_object( &request->hdr );
01233         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
01234         return FALSE;
01235     }
01236 
01237     if (query_auth_schemes( request, WINHTTP_QUERY_WWW_AUTHENTICATE, supported, first ))
01238     {
01239         *target = WINHTTP_AUTH_TARGET_SERVER;
01240         ret = TRUE;
01241     }
01242     else if (query_auth_schemes( request, WINHTTP_QUERY_PROXY_AUTHENTICATE, supported, first ))
01243     {
01244         *target = WINHTTP_AUTH_TARGET_PROXY;
01245         ret = TRUE;
01246     }
01247 
01248     release_object( &request->hdr );
01249     return ret;
01250 }
01251 
01252 static UINT encode_base64( const char *bin, unsigned int len, WCHAR *base64 )
01253 {
01254     UINT n = 0, x;
01255     static const char base64enc[] =
01256         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
01257 
01258     while (len > 0)
01259     {
01260         /* first 6 bits, all from bin[0] */
01261         base64[n++] = base64enc[(bin[0] & 0xfc) >> 2];
01262         x = (bin[0] & 3) << 4;
01263 
01264         /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
01265         if (len == 1)
01266         {
01267             base64[n++] = base64enc[x];
01268             base64[n++] = '=';
01269             base64[n++] = '=';
01270             break;
01271         }
01272         base64[n++] = base64enc[x | ((bin[1] & 0xf0) >> 4)];
01273         x = (bin[1] & 0x0f) << 2;
01274 
01275         /* next 6 bits 4 from bin[1] and 2 from bin[2] */
01276         if (len == 2)
01277         {
01278             base64[n++] = base64enc[x];
01279             base64[n++] = '=';
01280             break;
01281         }
01282         base64[n++] = base64enc[x | ((bin[2] & 0xc0) >> 6)];
01283 
01284         /* last 6 bits, all from bin [2] */
01285         base64[n++] = base64enc[bin[2] & 0x3f];
01286         bin += 3;
01287         len -= 3;
01288     }
01289     base64[n] = 0;
01290     return n;
01291 }
01292 
01293 static BOOL set_credentials( request_t *request, DWORD target, DWORD scheme, LPCWSTR username, LPCWSTR password )
01294 {
01295     static const WCHAR basic[] = {'B','a','s','i','c',' ',0};
01296     const WCHAR *auth_scheme, *auth_target;
01297     WCHAR *auth_header;
01298     DWORD len, auth_data_len;
01299     char *auth_data;
01300     BOOL ret;
01301 
01302     if (!username || !password)
01303     {
01304         set_last_error( ERROR_INVALID_PARAMETER );
01305         return FALSE;
01306     }
01307 
01308     switch (target)
01309     {
01310     case WINHTTP_AUTH_TARGET_SERVER: auth_target = attr_authorization; break;
01311     case WINHTTP_AUTH_TARGET_PROXY:  auth_target = attr_proxy_authorization; break;
01312     default:
01313         WARN("unknown target %x\n", target);
01314         return FALSE;
01315     }
01316     switch (scheme)
01317     {
01318     case WINHTTP_AUTH_SCHEME_BASIC:
01319     {
01320         int userlen = WideCharToMultiByte( CP_UTF8, 0, username, strlenW( username ), NULL, 0, NULL, NULL );
01321         int passlen = WideCharToMultiByte( CP_UTF8, 0, password, strlenW( password ), NULL, 0, NULL, NULL );
01322 
01323         TRACE("basic authentication\n");
01324 
01325         auth_scheme = basic;
01326         auth_data_len = userlen + 1 + passlen;
01327         if (!(auth_data = heap_alloc( auth_data_len ))) return FALSE;
01328 
01329         WideCharToMultiByte( CP_UTF8, 0, username, -1, auth_data, userlen, NULL, NULL );
01330         auth_data[userlen] = ':';
01331         WideCharToMultiByte( CP_UTF8, 0, password, -1, auth_data + userlen + 1, passlen, NULL, NULL );
01332         break;
01333     }
01334     case WINHTTP_AUTH_SCHEME_NTLM:
01335     case WINHTTP_AUTH_SCHEME_PASSPORT:
01336     case WINHTTP_AUTH_SCHEME_DIGEST:
01337     case WINHTTP_AUTH_SCHEME_NEGOTIATE:
01338         FIXME("unimplemented authentication scheme %x\n", scheme);
01339         return FALSE;
01340     default:
01341         WARN("unknown authentication scheme %x\n", scheme);
01342         return FALSE;
01343     }
01344 
01345     len = strlenW( auth_scheme ) + ((auth_data_len + 2) * 4) / 3;
01346     if (!(auth_header = heap_alloc( (len + 1) * sizeof(WCHAR) )))
01347     {
01348         heap_free( auth_data );
01349         return FALSE;
01350     }
01351     strcpyW( auth_header, auth_scheme );
01352     encode_base64( auth_data, auth_data_len, auth_header + strlenW( auth_header ) );
01353 
01354     ret = process_header( request, auth_target, auth_header, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE, TRUE );
01355 
01356     heap_free( auth_data );
01357     heap_free( auth_header );
01358     return ret;
01359 }
01360 
01361 /***********************************************************************
01362  *          WinHttpSetCredentials (winhttp.@)
01363  */
01364 BOOL WINAPI WinHttpSetCredentials( HINTERNET hrequest, DWORD target, DWORD scheme, LPCWSTR username,
01365                                    LPCWSTR password, LPVOID params )
01366 {
01367     BOOL ret;
01368     request_t *request;
01369 
01370     TRACE("%p, %x, 0x%08x, %s, %p, %p\n", hrequest, target, scheme, debugstr_w(username), password, params);
01371 
01372     if (!(request = (request_t *)grab_object( hrequest )))
01373     {
01374         set_last_error( ERROR_INVALID_HANDLE );
01375         return FALSE;
01376     }
01377     if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
01378     {
01379         release_object( &request->hdr );
01380         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
01381         return FALSE;
01382     }
01383 
01384     ret = set_credentials( request, target, scheme, username, password );
01385 
01386     release_object( &request->hdr );
01387     return ret;
01388 }
01389 
01390 static BOOL handle_authorization( request_t *request, DWORD status )
01391 {
01392     DWORD schemes, level, target;
01393     const WCHAR *username, *password;
01394 
01395     switch (status)
01396     {
01397     case 401:
01398         target = WINHTTP_AUTH_TARGET_SERVER;
01399         level  = WINHTTP_QUERY_WWW_AUTHENTICATE;
01400         break;
01401 
01402     case 407:
01403         target = WINHTTP_AUTH_TARGET_PROXY;
01404         level  = WINHTTP_QUERY_PROXY_AUTHENTICATE;
01405         break;
01406 
01407     default:
01408         WARN("unhandled status %u\n", status);
01409         return FALSE;
01410     }
01411 
01412     if (!query_auth_schemes( request, level, &schemes, NULL )) return FALSE;
01413 
01414     if (target == WINHTTP_AUTH_TARGET_SERVER)
01415     {
01416         username = request->connect->username;
01417         password = request->connect->password;
01418     }
01419     else
01420     {
01421         username = request->connect->session->proxy_username;
01422         password = request->connect->session->proxy_password;
01423     }
01424 
01425     if (schemes & WINHTTP_AUTH_SCHEME_BASIC)
01426         return set_credentials( request, target, WINHTTP_AUTH_SCHEME_BASIC, username, password );
01427 
01428     FIXME("unsupported authentication scheme\n");
01429     return FALSE;
01430 }
01431 
01432 static void clear_response_headers( request_t *request )
01433 {
01434     unsigned int i;
01435 
01436     for (i = 0; i < request->num_headers; i++)
01437     {
01438         if (!request->headers[i].field) continue;
01439         if (!request->headers[i].value) continue;
01440         if (request->headers[i].is_request) continue;
01441         delete_header( request, i );
01442         i--;
01443     }
01444 }
01445 
01446 #define MAX_REPLY_LEN   1460
01447 #define INITIAL_HEADER_BUFFER_LEN  512
01448 
01449 static BOOL read_reply( request_t *request )
01450 {
01451     static const WCHAR crlf[] = {'\r','\n',0};
01452 
01453     char buffer[MAX_REPLY_LEN];
01454     DWORD buflen, len, offset, received_len, crlf_len = 2; /* strlenW(crlf) */
01455     char *status_code, *status_text;
01456     WCHAR *versionW, *status_textW, *raw_headers;
01457     WCHAR status_codeW[4]; /* sizeof("nnn") */
01458 
01459     if (!netconn_connected( &request->netconn )) return FALSE;
01460 
01461     send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NULL, 0 );
01462 
01463     received_len = 0;
01464     do
01465     {
01466         buflen = MAX_REPLY_LEN;
01467         if (!netconn_get_next_line( &request->netconn, buffer, &buflen )) return FALSE;
01468         received_len += buflen;
01469 
01470         /* first line should look like 'HTTP/1.x nnn OK' where nnn is the status code */
01471         if (!(status_code = strchr( buffer, ' ' ))) return FALSE;
01472         status_code++;
01473         if (!(status_text = strchr( status_code, ' ' ))) return FALSE;
01474         if ((len = status_text - status_code) != sizeof("nnn") - 1) return FALSE;
01475         status_text++;
01476 
01477         TRACE("version [%s] status code [%s] status text [%s]\n",
01478               debugstr_an(buffer, status_code - buffer - 1),
01479               debugstr_an(status_code, len),
01480               debugstr_a(status_text));
01481 
01482     } while (!memcmp( status_code, "100", len )); /* ignore "100 Continue" responses */
01483 
01484     /*  we rely on the fact that the protocol is ascii */
01485     MultiByteToWideChar( CP_ACP, 0, status_code, len, status_codeW, len );
01486     status_codeW[len] = 0;
01487     if (!(process_header( request, attr_status, status_codeW, WINHTTP_ADDREQ_FLAG_REPLACE, FALSE ))) return FALSE;
01488 
01489     len = status_code - buffer;
01490     if (!(versionW = heap_alloc( len * sizeof(WCHAR) ))) return FALSE;
01491     MultiByteToWideChar( CP_ACP, 0, buffer, len - 1, versionW, len -1 );
01492     versionW[len - 1] = 0;
01493 
01494     heap_free( request->version );
01495     request->version = versionW;
01496 
01497     len = buflen - (status_text - buffer);
01498     if (!(status_textW = heap_alloc( len * sizeof(WCHAR) ))) return FALSE;
01499     MultiByteToWideChar( CP_ACP, 0, status_text, len, status_textW, len );
01500 
01501     heap_free( request->status_text );
01502     request->status_text = status_textW;
01503 
01504     len = max( buflen + crlf_len, INITIAL_HEADER_BUFFER_LEN );
01505     if (!(raw_headers = heap_alloc( len * sizeof(WCHAR) ))) return FALSE;
01506     MultiByteToWideChar( CP_ACP, 0, buffer, buflen, raw_headers, buflen );
01507     memcpy( raw_headers + buflen - 1, crlf, sizeof(crlf) );
01508 
01509     heap_free( request->raw_headers );
01510     request->raw_headers = raw_headers;
01511 
01512     offset = buflen + crlf_len - 1;
01513     for (;;)
01514     {
01515         header_t *header;
01516 
01517         buflen = MAX_REPLY_LEN;
01518         if (!netconn_get_next_line( &request->netconn, buffer, &buflen )) goto end;
01519         received_len += buflen;
01520         if (!*buffer) break;
01521 
01522         while (len - offset < buflen + crlf_len)
01523         {
01524             WCHAR *tmp;
01525             len *= 2;
01526             if (!(tmp = heap_realloc( raw_headers, len * sizeof(WCHAR) ))) return FALSE;
01527             request->raw_headers = raw_headers = tmp;
01528         }
01529         MultiByteToWideChar( CP_ACP, 0, buffer, buflen, raw_headers + offset, buflen );
01530 
01531         if (!(header = parse_header( raw_headers + offset ))) break;
01532         if (!(process_header( request, header->field, header->value, WINHTTP_ADDREQ_FLAG_ADD, FALSE )))
01533         {
01534             free_header( header );
01535             break;
01536         }
01537         free_header( header );
01538         memcpy( raw_headers + offset + buflen - 1, crlf, sizeof(crlf) );
01539         offset += buflen + crlf_len - 1;
01540     }
01541 
01542     TRACE("raw headers: %s\n", debugstr_w(raw_headers));
01543 
01544 end:
01545     send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, &received_len, sizeof(DWORD) );
01546     return TRUE;
01547 }
01548 
01549 static BOOL handle_redirect( request_t *request )
01550 {
01551     BOOL ret = FALSE;
01552     DWORD size, len;
01553     URL_COMPONENTS uc;
01554     connect_t *connect = request->connect;
01555     INTERNET_PORT port;
01556     WCHAR *hostname = NULL, *location = NULL;
01557     int index;
01558 
01559     size = 0;
01560     query_headers( request, WINHTTP_QUERY_LOCATION, NULL, NULL, &size, NULL );
01561     if (!(location = heap_alloc( size ))) return FALSE;
01562     if (!query_headers( request, WINHTTP_QUERY_LOCATION, NULL, location, &size, NULL )) goto end;
01563 
01564     send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REDIRECT, location, size / sizeof(WCHAR) + 1 );
01565 
01566     memset( &uc, 0, sizeof(uc) );
01567     uc.dwStructSize = sizeof(uc);
01568     uc.dwSchemeLength = uc.dwHostNameLength = uc.dwUrlPathLength = uc.dwExtraInfoLength = ~0u;
01569 
01570     if (!WinHttpCrackUrl( location, size / sizeof(WCHAR), 0, &uc )) /* assume relative redirect */
01571     {
01572         WCHAR *path, *p;
01573 
01574         len = strlenW( location ) + 1;
01575         if (location[0] != '/') len++;
01576         if (!(p = path = heap_alloc( len * sizeof(WCHAR) ))) goto end;
01577 
01578         if (location[0] != '/') *p++ = '/';
01579         strcpyW( p, location );
01580 
01581         heap_free( request->path );
01582         request->path = path;
01583     }
01584     else
01585     {
01586         if (uc.nScheme == INTERNET_SCHEME_HTTP && request->hdr.flags & WINHTTP_FLAG_SECURE)
01587         {
01588             TRACE("redirect from secure page to non-secure page\n");
01589             request->hdr.flags &= ~WINHTTP_FLAG_SECURE;
01590         }
01591         else if (uc.nScheme == INTERNET_SCHEME_HTTPS && !(request->hdr.flags & WINHTTP_FLAG_SECURE))
01592         {
01593             TRACE("redirect from non-secure page to secure page\n");
01594             request->hdr.flags |= WINHTTP_FLAG_SECURE;
01595         }
01596 
01597         len = uc.dwHostNameLength;
01598         if (!(hostname = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
01599         memcpy( hostname, uc.lpszHostName, len * sizeof(WCHAR) );
01600         hostname[len] = 0;
01601 
01602         port = uc.nPort ? uc.nPort : (uc.nScheme == INTERNET_SCHEME_HTTPS ? 443 : 80);
01603         if (strcmpiW( connect->hostname, hostname ) || connect->serverport != port)
01604         {
01605             heap_free( connect->hostname );
01606             connect->hostname = hostname;
01607             connect->hostport = port;
01608             if (!(ret = set_server_for_hostname( connect, hostname, port ))) goto end;
01609 
01610             netconn_close( &request->netconn );
01611             if (!(ret = netconn_init( &request->netconn, request->hdr.flags & WINHTTP_FLAG_SECURE ))) goto end;
01612         }
01613         if (!(ret = add_host_header( request, WINHTTP_ADDREQ_FLAG_REPLACE ))) goto end;
01614         if (!(ret = open_connection( request ))) goto end;
01615 
01616         heap_free( request->path );
01617         request->path = NULL;
01618         if (uc.dwUrlPathLength)
01619         {
01620             len = uc.dwUrlPathLength + uc.dwExtraInfoLength;
01621             if (!(request->path = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
01622             strcpyW( request->path, uc.lpszUrlPath );
01623         }
01624         else request->path = strdupW( slashW );
01625     }
01626 
01627     /* remove content-type/length headers */
01628     if ((index = get_header_index( request, attr_content_type, 0, TRUE )) >= 0) delete_header( request, index );
01629     if ((index = get_header_index( request, attr_content_length, 0, TRUE )) >= 0 ) delete_header( request, index );
01630 
01631     /* redirects are always GET requests */
01632     heap_free( request->verb );
01633     request->verb = strdupW( getW );
01634     ret = TRUE;
01635 
01636 end:
01637     if (!ret) heap_free( hostname );
01638     heap_free( location );
01639     return ret;
01640 }
01641 
01642 static BOOL receive_data( request_t *request, void *buffer, DWORD size, DWORD *read, BOOL async )
01643 {
01644     DWORD to_read;
01645     int bytes_read;
01646 
01647     to_read = min( size, request->content_length - request->content_read );
01648     if (!netconn_recv( &request->netconn, buffer, to_read, async ? 0 : MSG_WAITALL, &bytes_read ))
01649     {
01650         if (bytes_read != to_read)
01651         {
01652             ERR("not all data received %d/%d\n", bytes_read, to_read);
01653         }
01654         /* always return success, even if the network layer returns an error */
01655         *read = 0;
01656         return TRUE;
01657     }
01658     request->content_read += bytes_read;
01659     *read = bytes_read;
01660     return TRUE;
01661 }
01662 
01663 static DWORD get_chunk_size( const char *buffer )
01664 {
01665     const char *p;
01666     DWORD size = 0;
01667 
01668     for (p = buffer; *p; p++)
01669     {
01670         if (*p >= '0' && *p <= '9') size = size * 16 + *p - '0';
01671         else if (*p >= 'a' && *p <= 'f') size = size * 16 + *p - 'a' + 10;
01672         else if (*p >= 'A' && *p <= 'F') size = size * 16 + *p - 'A' + 10;
01673         else if (*p == ';') break;
01674     }
01675     return size;
01676 }
01677 
01678 static BOOL receive_data_chunked( request_t *request, void *buffer, DWORD size, DWORD *read, BOOL async )
01679 {
01680     char reply[MAX_REPLY_LEN], *p = buffer;
01681     DWORD buflen, to_read, to_write = size;
01682     int bytes_read;
01683 
01684     *read = 0;
01685     for (;;)
01686     {
01687         if (*read == size) break;
01688 
01689         if (request->content_length == ~0u) /* new chunk */
01690         {
01691             buflen = sizeof(reply);
01692             if (!netconn_get_next_line( &request->netconn, reply, &buflen )) break;
01693 
01694             if (!(request->content_length = get_chunk_size( reply )))
01695             {
01696                 /* zero sized chunk marks end of transfer; read any trailing headers and return */
01697                 read_reply( request );
01698                 break;
01699             }
01700         }
01701         to_read = min( to_write, request->content_length - request->content_read );
01702 
01703         if (!netconn_recv( &request->netconn, p, to_read, async ? 0 : MSG_WAITALL, &bytes_read ))
01704         {
01705             if (bytes_read != to_read)
01706             {
01707                 ERR("Not all data received %d/%d\n", bytes_read, to_read);
01708             }
01709             /* always return success, even if the network layer returns an error */
01710             *read = 0;
01711             break;
01712         }
01713         if (!bytes_read) break;
01714 
01715         request->content_read += bytes_read;
01716         to_write -= bytes_read;
01717         *read += bytes_read;
01718         p += bytes_read;
01719 
01720         if (request->content_read == request->content_length) /* chunk complete */
01721         {
01722             request->content_read = 0;
01723             request->content_length = ~0u;
01724 
01725             buflen = sizeof(reply);
01726             if (!netconn_get_next_line( &request->netconn, reply, &buflen ))
01727             {
01728                 ERR("Malformed chunk\n");
01729                 *read = 0;
01730                 break;
01731             }
01732         }
01733     }
01734     return TRUE;
01735 }
01736 
01737 static void finished_reading( request_t *request )
01738 {
01739     static const WCHAR closeW[] = {'c','l','o','s','e',0};
01740 
01741     BOOL close = FALSE;
01742     WCHAR connection[20];
01743     DWORD size = sizeof(connection);
01744 
01745     if (request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE) close = TRUE;
01746     else if (query_headers( request, WINHTTP_QUERY_CONNECTION, NULL, connection, &size, NULL ) ||
01747              query_headers( request, WINHTTP_QUERY_PROXY_CONNECTION, NULL, connection, &size, NULL ))
01748     {
01749         if (!strcmpiW( connection, closeW )) close = TRUE;
01750     }
01751     else if (!strcmpW( request->version, http1_0 )) close = TRUE;
01752 
01753     if (close) close_connection( request );
01754     request->content_length = ~0u;
01755     request->content_read = 0;
01756 }
01757 
01758 static BOOL read_data( request_t *request, void *buffer, DWORD to_read, DWORD *read, BOOL async )
01759 {
01760     static const WCHAR chunked[] = {'c','h','u','n','k','e','d',0};
01761 
01762     BOOL ret;
01763     WCHAR encoding[20];
01764     DWORD num_bytes, buflen = sizeof(encoding);
01765 
01766     if (query_headers( request, WINHTTP_QUERY_TRANSFER_ENCODING, NULL, encoding, &buflen, NULL ) &&
01767         !strcmpiW( encoding, chunked ))
01768     {
01769         ret = receive_data_chunked( request, buffer, to_read, &num_bytes, async );
01770     }
01771     else
01772         ret = receive_data( request, buffer, to_read, &num_bytes, async );
01773 
01774     if (async)
01775     {
01776         if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, buffer, num_bytes );
01777         else
01778         {
01779             WINHTTP_ASYNC_RESULT result;
01780             result.dwResult = API_READ_DATA;
01781             result.dwError  = get_last_error();
01782             send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
01783         }
01784     }
01785     if (ret)
01786     {
01787         if (read) *read = num_bytes;
01788         if (!num_bytes) finished_reading( request );
01789     }
01790     return ret;
01791 }
01792 
01793 /* read any content returned by the server so that the connection can be reused */
01794 static void drain_content( request_t *request )
01795 {
01796     DWORD bytes_read;
01797     char buffer[2048];
01798 
01799     if (!request->content_length) return;
01800     for (;;)
01801     {
01802         if (!read_data( request, buffer, sizeof(buffer), &bytes_read, FALSE ) || !bytes_read) return;
01803     }
01804 }
01805 
01806 static void record_cookies( request_t *request )
01807 {
01808     unsigned int i;
01809 
01810     for (i = 0; i < request->num_headers; i++)
01811     {
01812         header_t *set_cookie = &request->headers[i];
01813         if (!strcmpiW( set_cookie->field, attr_set_cookie ) && !set_cookie->is_request)
01814         {
01815             set_cookies( request, set_cookie->value );
01816         }
01817     }
01818 }
01819 
01820 static BOOL receive_response( request_t *request, BOOL async )
01821 {
01822     BOOL ret;
01823     DWORD size, query, status;
01824 
01825     for (;;)
01826     {
01827         if (!(ret = read_reply( request ))) break;
01828 
01829         size = sizeof(DWORD);
01830         query = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER;
01831         if (!(ret = query_headers( request, query, NULL, &status, &size, NULL ))) break;
01832 
01833         size = sizeof(DWORD);
01834         query = WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER;
01835         if (!query_headers( request, query, NULL, &request->content_length, &size, NULL ))
01836             request->content_length = ~0u;
01837 
01838         if (!(request->hdr.disable_flags & WINHTTP_DISABLE_COOKIES)) record_cookies( request );
01839 
01840         if (status == HTTP_STATUS_MOVED || status == HTTP_STATUS_REDIRECT || status == HTTP_STATUS_REDIRECT_KEEP_VERB)
01841         {
01842             if (request->hdr.disable_flags & WINHTTP_DISABLE_REDIRECTS) break;
01843 
01844             drain_content( request );
01845             if (!(ret = handle_redirect( request ))) break;
01846 
01847             clear_response_headers( request );
01848             ret = send_request( request, NULL, 0, NULL, 0, 0, 0, FALSE ); /* recurse synchronously */
01849             continue;
01850         }
01851         else if (status == 401 || status == 407)
01852         {
01853             if (request->hdr.disable_flags & WINHTTP_DISABLE_AUTHENTICATION) break;
01854 
01855             drain_content( request );
01856             if (!handle_authorization( request, status ))
01857             {
01858                 ret = TRUE;
01859                 break;
01860             }
01861             clear_response_headers( request );
01862             ret = send_request( request, NULL, 0, NULL, 0, 0, 0, FALSE );
01863             continue;
01864         }
01865         break;
01866     }
01867 
01868     if (async)
01869     {
01870         if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, NULL, 0 );
01871         else
01872         {
01873             WINHTTP_ASYNC_RESULT result;
01874             result.dwResult = API_RECEIVE_RESPONSE;
01875             result.dwError  = get_last_error();
01876             send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
01877         }
01878     }
01879     return ret;
01880 }
01881 
01882 static void task_receive_response( task_header_t *task )
01883 {
01884     receive_response_t *r = (receive_response_t *)task;
01885     receive_response( r->hdr.request, TRUE );
01886 }
01887 
01888 /***********************************************************************
01889  *          WinHttpReceiveResponse (winhttp.@)
01890  */
01891 BOOL WINAPI WinHttpReceiveResponse( HINTERNET hrequest, LPVOID reserved )
01892 {
01893     BOOL ret;
01894     request_t *request;
01895 
01896     TRACE("%p, %p\n", hrequest, reserved);
01897 
01898     if (!(request = (request_t *)grab_object( hrequest )))
01899     {
01900         set_last_error( ERROR_INVALID_HANDLE );
01901         return FALSE;
01902     }
01903     if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
01904     {
01905         release_object( &request->hdr );
01906         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
01907         return FALSE;
01908     }
01909 
01910     if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
01911     {
01912         receive_response_t *r;
01913 
01914         if (!(r = heap_alloc( sizeof(receive_response_t) ))) return FALSE;
01915         r->hdr.request = request;
01916         r->hdr.proc    = task_receive_response;
01917 
01918         addref_object( &request->hdr );
01919         ret = queue_task( (task_header_t *)r );
01920     }
01921     else
01922         ret = receive_response( request, FALSE );
01923 
01924     release_object( &request->hdr );
01925     return ret;
01926 }
01927 
01928 static BOOL query_data( request_t *request, LPDWORD available, BOOL async )
01929 {
01930     BOOL ret;
01931     DWORD num_bytes;
01932 
01933     if ((ret = netconn_query_data_available( &request->netconn, &num_bytes )))
01934     {
01935         if (request->content_read < request->content_length)
01936         {
01937             if (!num_bytes)
01938             {
01939                 char buffer[4096];
01940                 size_t to_read = min( sizeof(buffer), request->content_length - request->content_read );
01941 
01942                 ret = netconn_recv( &request->netconn, buffer, to_read, MSG_PEEK, (int *)&num_bytes );
01943                 if (ret && !num_bytes) WARN("expected more data to be available\n");
01944             }
01945         }
01946         else if (num_bytes)
01947         {
01948             WARN("extra data available %u\n", num_bytes);
01949             ret = FALSE;
01950         }
01951     }
01952     TRACE("%u bytes available\n", num_bytes);
01953 
01954     if (async)
01955     {
01956         if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, &num_bytes, sizeof(DWORD) );
01957         else
01958         {
01959             WINHTTP_ASYNC_RESULT result;
01960             result.dwResult = API_QUERY_DATA_AVAILABLE;
01961             result.dwError  = get_last_error();
01962             send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
01963         }
01964     }
01965     if (ret && available) *available = num_bytes;
01966     return ret;
01967 }
01968 
01969 static void task_query_data( task_header_t *task )
01970 {
01971     query_data_t *q = (query_data_t *)task;
01972     query_data( q->hdr.request, q->available, TRUE );
01973 }
01974 
01975 /***********************************************************************
01976  *          WinHttpQueryDataAvailable (winhttp.@)
01977  */
01978 BOOL WINAPI WinHttpQueryDataAvailable( HINTERNET hrequest, LPDWORD available )
01979 {
01980     BOOL ret;
01981     request_t *request;
01982 
01983     TRACE("%p, %p\n", hrequest, available);
01984 
01985     if (!(request = (request_t *)grab_object( hrequest )))
01986     {
01987         set_last_error( ERROR_INVALID_HANDLE );
01988         return FALSE;
01989     }
01990     if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
01991     {
01992         release_object( &request->hdr );
01993         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
01994         return FALSE;
01995     }
01996 
01997     if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
01998     {
01999         query_data_t *q;
02000 
02001         if (!(q = heap_alloc( sizeof(query_data_t) ))) return FALSE;
02002         q->hdr.request = request;
02003         q->hdr.proc    = task_query_data;
02004         q->available   = available;
02005 
02006         addref_object( &request->hdr );
02007         ret = queue_task( (task_header_t *)q );
02008     }
02009     else
02010         ret = query_data( request, available, FALSE );
02011 
02012     release_object( &request->hdr );
02013     return ret;
02014 }
02015 
02016 static void task_read_data( task_header_t *task )
02017 {
02018     read_data_t *r = (read_data_t *)task;
02019     read_data( r->hdr.request, r->buffer, r->to_read, r->read, TRUE );
02020 }
02021 
02022 /***********************************************************************
02023  *          WinHttpReadData (winhttp.@)
02024  */
02025 BOOL WINAPI WinHttpReadData( HINTERNET hrequest, LPVOID buffer, DWORD to_read, LPDWORD read )
02026 {
02027     BOOL ret;
02028     request_t *request;
02029 
02030     TRACE("%p, %p, %d, %p\n", hrequest, buffer, to_read, read);
02031 
02032     if (!(request = (request_t *)grab_object( hrequest )))
02033     {
02034         set_last_error( ERROR_INVALID_HANDLE );
02035         return FALSE;
02036     }
02037     if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
02038     {
02039         release_object( &request->hdr );
02040         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
02041         return FALSE;
02042     }
02043 
02044     if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
02045     {
02046         read_data_t *r;
02047 
02048         if (!(r = heap_alloc( sizeof(read_data_t) ))) return FALSE;
02049         r->hdr.request = request;
02050         r->hdr.proc    = task_read_data;
02051         r->buffer      = buffer;
02052         r->to_read     = to_read;
02053         r->read        = read;
02054 
02055         addref_object( &request->hdr );
02056         ret = queue_task( (task_header_t *)r );
02057     }
02058     else
02059         ret = read_data( request, buffer, to_read, read, FALSE );
02060 
02061     release_object( &request->hdr );
02062     return ret;
02063 }
02064 
02065 static BOOL write_data( request_t *request, LPCVOID buffer, DWORD to_write, LPDWORD written, BOOL async )
02066 {
02067     BOOL ret;
02068     int num_bytes;
02069 
02070     ret = netconn_send( &request->netconn, buffer, to_write, 0, &num_bytes );
02071 
02072     if (async)
02073     {
02074         if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, &num_bytes, sizeof(DWORD) );
02075         else
02076         {
02077             WINHTTP_ASYNC_RESULT result;
02078             result.dwResult = API_WRITE_DATA;
02079             result.dwError  = get_last_error();
02080             send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
02081         }
02082     }
02083     if (ret && written) *written = num_bytes;
02084     return ret;
02085 }
02086 
02087 static void task_write_data( task_header_t *task )
02088 {
02089     write_data_t *w = (write_data_t *)task;
02090     write_data( w->hdr.request, w->buffer, w->to_write, w->written, TRUE );
02091 }
02092 
02093 /***********************************************************************
02094  *          WinHttpWriteData (winhttp.@)
02095  */
02096 BOOL WINAPI WinHttpWriteData( HINTERNET hrequest, LPCVOID buffer, DWORD to_write, LPDWORD written )
02097 {
02098     BOOL ret;
02099     request_t *request;
02100 
02101     TRACE("%p, %p, %d, %p\n", hrequest, buffer, to_write, written);
02102 
02103     if (!(request = (request_t *)grab_object( hrequest )))
02104     {
02105         set_last_error( ERROR_INVALID_HANDLE );
02106         return FALSE;
02107     }
02108     if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
02109     {
02110         release_object( &request->hdr );
02111         set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
02112         return FALSE;
02113     }
02114 
02115     if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
02116     {
02117         write_data_t *w;
02118 
02119         if (!(w = heap_alloc( sizeof(write_data_t) ))) return FALSE;
02120         w->hdr.request = request;
02121         w->hdr.proc    = task_write_data;
02122         w->buffer      = buffer;
02123         w->to_write    = to_write;
02124         w->written     = written;
02125 
02126         addref_object( &request->hdr );
02127         ret = queue_task( (task_header_t *)w );
02128     }
02129     else
02130         ret = write_data( request, buffer, to_write, written, FALSE );
02131 
02132     release_object( &request->hdr );
02133     return ret;
02134 }

Generated on Sun May 27 2012 04:26:56 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.