2 * Wininet - Http Implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2002 CodeWeavers Inc.
6 * Copyright 2002 TransGaming Technologies Inc.
7 * Copyright 2004 Mike McCormack for CodeWeavers
8 * Copyright 2005 Aric Stewart for CodeWeavers
9 * Copyright 2006 Robert Shearman for CodeWeavers
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "wine/port.h"
32 #if defined(__MINGW32__) || defined (_MSC_VER)
36 #include <sys/types.h>
37 #ifdef HAVE_SYS_SOCKET_H
38 # include <sys/socket.h>
40 #ifdef HAVE_ARPA_INET_H
41 # include <arpa/inet.h>
59 #define NO_SHLWAPI_STREAM
60 #define NO_SHLWAPI_REG
61 #define NO_SHLWAPI_STRFCNS
62 #define NO_SHLWAPI_GDI
68 #include "wine/debug.h"
69 #include "wine/exception.h"
70 #include "wine/unicode.h"
72 WINE_DEFAULT_DEBUG_CHANNEL(wininet
);
74 static const WCHAR g_szHttp1_0
[] = {'H','T','T','P','/','1','.','0',0};
75 static const WCHAR g_szHttp1_1
[] = {'H','T','T','P','/','1','.','1',0};
76 static const WCHAR szOK
[] = {'O','K',0};
77 static const WCHAR szDefaultHeader
[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
78 static const WCHAR hostW
[] = { 'H','o','s','t',0 };
79 static const WCHAR szAuthorization
[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
80 static const WCHAR szProxy_Authorization
[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
81 static const WCHAR szStatus
[] = { 'S','t','a','t','u','s',0 };
82 static const WCHAR szKeepAlive
[] = {'K','e','e','p','-','A','l','i','v','e',0};
83 static const WCHAR szGET
[] = { 'G','E','T', 0 };
84 static const WCHAR szHEAD
[] = { 'H','E','A','D', 0 };
85 static const WCHAR szCrLf
[] = {'\r','\n', 0};
87 static const WCHAR szAccept
[] = { 'A','c','c','e','p','t',0 };
88 static const WCHAR szAccept_Charset
[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
89 static const WCHAR szAccept_Encoding
[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
90 static const WCHAR szAccept_Language
[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
91 static const WCHAR szAccept_Ranges
[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
92 static const WCHAR szAge
[] = { 'A','g','e',0 };
93 static const WCHAR szAllow
[] = { 'A','l','l','o','w',0 };
94 static const WCHAR szCache_Control
[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
95 static const WCHAR szConnection
[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
96 static const WCHAR szContent_Base
[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
97 static const WCHAR szContent_Encoding
[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
98 static const WCHAR szContent_ID
[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
99 static const WCHAR szContent_Language
[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
100 static const WCHAR szContent_Length
[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
101 static const WCHAR szContent_Location
[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
102 static const WCHAR szContent_MD5
[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
103 static const WCHAR szContent_Range
[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
104 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 };
105 static const WCHAR szContent_Type
[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
106 static const WCHAR szCookie
[] = { 'C','o','o','k','i','e',0 };
107 static const WCHAR szDate
[] = { 'D','a','t','e',0 };
108 static const WCHAR szFrom
[] = { 'F','r','o','m',0 };
109 static const WCHAR szETag
[] = { 'E','T','a','g',0 };
110 static const WCHAR szExpect
[] = { 'E','x','p','e','c','t',0 };
111 static const WCHAR szExpires
[] = { 'E','x','p','i','r','e','s',0 };
112 static const WCHAR szIf_Match
[] = { 'I','f','-','M','a','t','c','h',0 };
113 static const WCHAR szIf_Modified_Since
[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
114 static const WCHAR szIf_None_Match
[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
115 static const WCHAR szIf_Range
[] = { 'I','f','-','R','a','n','g','e',0 };
116 static const WCHAR szIf_Unmodified_Since
[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
117 static const WCHAR szLast_Modified
[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
118 static const WCHAR szLocation
[] = { 'L','o','c','a','t','i','o','n',0 };
119 static const WCHAR szMax_Forwards
[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
120 static const WCHAR szMime_Version
[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
121 static const WCHAR szPragma
[] = { 'P','r','a','g','m','a',0 };
122 static const WCHAR szProxy_Authenticate
[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
123 static const WCHAR szProxy_Connection
[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
124 static const WCHAR szPublic
[] = { 'P','u','b','l','i','c',0 };
125 static const WCHAR szRange
[] = { 'R','a','n','g','e',0 };
126 static const WCHAR szReferer
[] = { 'R','e','f','e','r','e','r',0 };
127 static const WCHAR szRetry_After
[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
128 static const WCHAR szServer
[] = { 'S','e','r','v','e','r',0 };
129 static const WCHAR szSet_Cookie
[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
130 static const WCHAR szTransfer_Encoding
[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
131 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 };
132 static const WCHAR szUpgrade
[] = { 'U','p','g','r','a','d','e',0 };
133 static const WCHAR szURI
[] = { 'U','R','I',0 };
134 static const WCHAR szUser_Agent
[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
135 static const WCHAR szVary
[] = { 'V','a','r','y',0 };
136 static const WCHAR szVia
[] = { 'V','i','a',0 };
137 static const WCHAR szWarning
[] = { 'W','a','r','n','i','n','g',0 };
138 static const WCHAR szWWW_Authenticate
[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
140 #define MAXHOSTNAME 100
141 #define MAX_FIELD_VALUE_LEN 256
142 #define MAX_FIELD_LEN 256
144 #define HTTP_REFERER szReferer
145 #define HTTP_ACCEPT szAccept
146 #define HTTP_USERAGENT szUser_Agent
148 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
149 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
150 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
151 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
152 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
153 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
154 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
156 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
167 unsigned int auth_data_len
;
168 BOOL finished
; /* finished authenticating */
172 struct gzip_stream_t
{
182 typedef struct _basicAuthorizationData
188 LPSTR lpszAuthorization
;
189 UINT AuthorizationLen
;
190 } basicAuthorizationData
;
192 typedef struct _authorizationData
206 static struct list basicAuthorizationCache
= LIST_INIT(basicAuthorizationCache
);
207 static struct list authorizationCache
= LIST_INIT(authorizationCache
);
209 static CRITICAL_SECTION authcache_cs
;
210 static CRITICAL_SECTION_DEBUG critsect_debug
=
213 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
214 0, 0, { (DWORD_PTR
)(__FILE__
": authcache_cs") }
216 static CRITICAL_SECTION authcache_cs
= { &critsect_debug
, -1, 0, 0, 0, 0 };
218 static DWORD
HTTP_OpenConnection(http_request_t
*req
);
219 static BOOL
HTTP_GetResponseHeaders(http_request_t
*req
, BOOL clear
);
220 static DWORD
HTTP_ProcessHeader(http_request_t
*req
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
);
221 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
);
222 static DWORD
HTTP_InsertCustomHeader(http_request_t
*req
, LPHTTPHEADERW lpHdr
);
223 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*req
, LPCWSTR lpszField
, INT index
, BOOL Request
);
224 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*req
, DWORD index
);
225 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
);
226 static DWORD
HTTP_HttpQueryInfoW(http_request_t
*, DWORD
, LPVOID
, LPDWORD
, LPDWORD
);
227 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*req
, LPCWSTR lpszUrl
);
228 static UINT
HTTP_DecodeBase64(LPCWSTR base64
, LPSTR bin
);
229 static BOOL
HTTP_VerifyValidHeader(http_request_t
*req
, LPCWSTR field
);
230 static void HTTP_DrainContent(http_request_t
*req
);
231 static BOOL
HTTP_FinishedReading(http_request_t
*req
);
233 static LPHTTPHEADERW
HTTP_GetHeader(http_request_t
*req
, LPCWSTR head
)
236 HeaderIndex
= HTTP_GetCustomHeaderIndex(req
, head
, 0, TRUE
);
237 if (HeaderIndex
== -1)
240 return &req
->pCustHeaders
[HeaderIndex
];
245 static voidpf
wininet_zalloc(voidpf opaque
, uInt items
, uInt size
)
247 return HeapAlloc(GetProcessHeap(), 0, items
*size
);
250 static void wininet_zfree(voidpf opaque
, voidpf address
)
252 HeapFree(GetProcessHeap(), 0, address
);
255 static void init_gzip_stream(http_request_t
*req
)
257 gzip_stream_t
*gzip_stream
;
260 gzip_stream
= HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t
));
261 gzip_stream
->zstream
.zalloc
= wininet_zalloc
;
262 gzip_stream
->zstream
.zfree
= wininet_zfree
;
263 gzip_stream
->zstream
.opaque
= NULL
;
264 gzip_stream
->zstream
.next_in
= NULL
;
265 gzip_stream
->zstream
.avail_in
= 0;
266 gzip_stream
->zstream
.next_out
= NULL
;
267 gzip_stream
->zstream
.avail_out
= 0;
268 gzip_stream
->buf_pos
= 0;
269 gzip_stream
->buf_size
= 0;
270 gzip_stream
->end_of_data
= FALSE
;
272 zres
= inflateInit2(&gzip_stream
->zstream
, 0x1f);
274 ERR("inflateInit failed: %d\n", zres
);
275 HeapFree(GetProcessHeap(), 0, gzip_stream
);
279 req
->gzip_stream
= gzip_stream
;
281 index
= HTTP_GetCustomHeaderIndex(req
, szContent_Length
, 0, FALSE
);
283 HTTP_DeleteCustomHeader(req
, index
);
288 static void init_gzip_stream(http_request_t
*req
)
290 ERR("gzip stream not supported, missing zlib.\n");
295 /* set the request content length based on the headers */
296 static DWORD
set_content_length( http_request_t
*lpwhr
)
298 static const WCHAR szChunked
[] = {'c','h','u','n','k','e','d',0};
302 size
= sizeof(lpwhr
->dwContentLength
);
303 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_CONTENT_LENGTH
,
304 &lpwhr
->dwContentLength
, &size
, NULL
) != ERROR_SUCCESS
)
305 lpwhr
->dwContentLength
= ~0u;
307 size
= sizeof(encoding
);
308 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_TRANSFER_ENCODING
, encoding
, &size
, NULL
) == ERROR_SUCCESS
&&
309 !strcmpiW(encoding
, szChunked
))
311 lpwhr
->dwContentLength
= ~0u;
312 lpwhr
->read_chunked
= TRUE
;
315 if(lpwhr
->decoding
) {
318 static const WCHAR gzipW
[] = {'g','z','i','p',0};
320 encoding_idx
= HTTP_GetCustomHeaderIndex(lpwhr
, szContent_Encoding
, 0, FALSE
);
321 if(encoding_idx
!= -1 && !strcmpiW(lpwhr
->pCustHeaders
[encoding_idx
].lpszValue
, gzipW
))
322 init_gzip_stream(lpwhr
);
325 return lpwhr
->dwContentLength
;
328 /***********************************************************************
329 * HTTP_Tokenize (internal)
331 * Tokenize a string, allocating memory for the tokens.
333 static LPWSTR
* HTTP_Tokenize(LPCWSTR string
, LPCWSTR token_string
)
335 LPWSTR
* token_array
;
342 /* empty string has no tokens */
346 for (i
= 0; string
[i
]; i
++)
348 if (!strncmpW(string
+i
, token_string
, strlenW(token_string
)))
352 /* we want to skip over separators, but not the null terminator */
353 for (j
= 0; j
< strlenW(token_string
) - 1; j
++)
361 /* add 1 for terminating NULL */
362 token_array
= HeapAlloc(GetProcessHeap(), 0, (tokens
+1) * sizeof(*token_array
));
363 token_array
[tokens
] = NULL
;
366 for (i
= 0; i
< tokens
; i
++)
369 next_token
= strstrW(string
, token_string
);
370 if (!next_token
) next_token
= string
+strlenW(string
);
371 len
= next_token
- string
;
372 token_array
[i
] = HeapAlloc(GetProcessHeap(), 0, (len
+1)*sizeof(WCHAR
));
373 memcpy(token_array
[i
], string
, len
*sizeof(WCHAR
));
374 token_array
[i
][len
] = '\0';
375 string
= next_token
+strlenW(token_string
);
380 /***********************************************************************
381 * HTTP_FreeTokens (internal)
383 * Frees memory returned from HTTP_Tokenize.
385 static void HTTP_FreeTokens(LPWSTR
* token_array
)
388 for (i
= 0; token_array
[i
]; i
++)
389 HeapFree(GetProcessHeap(), 0, token_array
[i
]);
390 HeapFree(GetProcessHeap(), 0, token_array
);
393 static void HTTP_FixURL(http_request_t
*lpwhr
)
395 static const WCHAR szSlash
[] = { '/',0 };
396 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/', 0 };
398 /* If we don't have a path we set it to root */
399 if (NULL
== lpwhr
->lpszPath
)
400 lpwhr
->lpszPath
= heap_strdupW(szSlash
);
401 else /* remove \r and \n*/
403 int nLen
= strlenW(lpwhr
->lpszPath
);
404 while ((nLen
>0 ) && ((lpwhr
->lpszPath
[nLen
-1] == '\r')||(lpwhr
->lpszPath
[nLen
-1] == '\n')))
407 lpwhr
->lpszPath
[nLen
]='\0';
409 /* Replace '\' with '/' */
412 if (lpwhr
->lpszPath
[nLen
] == '\\') lpwhr
->lpszPath
[nLen
]='/';
416 if(CSTR_EQUAL
!= CompareStringW( LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
417 lpwhr
->lpszPath
, strlenW(lpwhr
->lpszPath
), szHttp
, strlenW(szHttp
) )
418 && lpwhr
->lpszPath
[0] != '/') /* not an absolute path ?? --> fix it !! */
420 WCHAR
*fixurl
= HeapAlloc(GetProcessHeap(), 0,
421 (strlenW(lpwhr
->lpszPath
) + 2)*sizeof(WCHAR
));
423 strcpyW(fixurl
+ 1, lpwhr
->lpszPath
);
424 HeapFree( GetProcessHeap(), 0, lpwhr
->lpszPath
);
425 lpwhr
->lpszPath
= fixurl
;
429 static LPWSTR
HTTP_BuildHeaderRequestString( http_request_t
*lpwhr
, LPCWSTR verb
, LPCWSTR path
, LPCWSTR version
)
431 LPWSTR requestString
;
437 static const WCHAR szSpace
[] = { ' ',0 };
438 static const WCHAR szColon
[] = { ':',' ',0 };
439 static const WCHAR sztwocrlf
[] = {'\r','\n','\r','\n', 0};
441 /* allocate space for an array of all the string pointers to be added */
442 len
= (lpwhr
->nCustHeaders
)*4 + 10;
443 req
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(LPCWSTR
) );
445 /* add the verb, path and HTTP version string */
453 /* Append custom request headers */
454 for (i
= 0; i
< lpwhr
->nCustHeaders
; i
++)
456 if (lpwhr
->pCustHeaders
[i
].wFlags
& HDR_ISREQUEST
)
459 req
[n
++] = lpwhr
->pCustHeaders
[i
].lpszField
;
461 req
[n
++] = lpwhr
->pCustHeaders
[i
].lpszValue
;
463 TRACE("Adding custom header %s (%s)\n",
464 debugstr_w(lpwhr
->pCustHeaders
[i
].lpszField
),
465 debugstr_w(lpwhr
->pCustHeaders
[i
].lpszValue
));
470 ERR("oops. buffer overrun\n");
473 requestString
= HTTP_build_req( req
, 4 );
474 HeapFree( GetProcessHeap(), 0, req
);
477 * Set (header) termination string for request
478 * Make sure there's exactly two new lines at the end of the request
480 p
= &requestString
[strlenW(requestString
)-1];
481 while ( (*p
== '\n') || (*p
== '\r') )
483 strcpyW( p
+1, sztwocrlf
);
485 return requestString
;
488 static void HTTP_ProcessCookies( http_request_t
*lpwhr
)
492 LPHTTPHEADERW setCookieHeader
;
494 while((HeaderIndex
= HTTP_GetCustomHeaderIndex(lpwhr
, szSet_Cookie
, numCookies
, FALSE
)) != -1)
496 setCookieHeader
= &lpwhr
->pCustHeaders
[HeaderIndex
];
498 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
) && setCookieHeader
->lpszValue
)
501 static const WCHAR szFmt
[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
505 Host
= HTTP_GetHeader(lpwhr
, hostW
);
506 len
= lstrlenW(Host
->lpszValue
) + 9 + lstrlenW(lpwhr
->lpszPath
);
507 buf_url
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
508 sprintfW(buf_url
, szFmt
, Host
->lpszValue
, lpwhr
->lpszPath
);
509 InternetSetCookieW(buf_url
, NULL
, setCookieHeader
->lpszValue
);
511 HeapFree(GetProcessHeap(), 0, buf_url
);
517 static void strip_spaces(LPWSTR start
)
522 while (*str
== ' ' && *str
!= '\0')
526 memmove(start
, str
, sizeof(WCHAR
) * (strlenW(str
) + 1));
528 end
= start
+ strlenW(start
) - 1;
529 while (end
>= start
&& *end
== ' ')
536 static inline BOOL
is_basic_auth_value( LPCWSTR pszAuthValue
, LPWSTR
*pszRealm
)
538 static const WCHAR szBasic
[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
539 static const WCHAR szRealm
[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
541 is_basic
= !strncmpiW(pszAuthValue
, szBasic
, ARRAYSIZE(szBasic
)) &&
542 ((pszAuthValue
[ARRAYSIZE(szBasic
)] == ' ') || !pszAuthValue
[ARRAYSIZE(szBasic
)]);
543 if (is_basic
&& pszRealm
)
546 LPCWSTR ptr
= &pszAuthValue
[ARRAYSIZE(szBasic
)];
550 token
= strchrW(ptr
,'=');
554 while (*realm
== ' ' && *realm
!= '\0')
556 if(!strncmpiW(realm
, szRealm
, ARRAYSIZE(szRealm
)) &&
557 (realm
[ARRAYSIZE(szRealm
)] == ' ' || realm
[ARRAYSIZE(szRealm
)] == '='))
560 while (*token
== ' ' && *token
!= '\0')
564 *pszRealm
= heap_strdupW(token
);
565 strip_spaces(*pszRealm
);
572 static void destroy_authinfo( struct HttpAuthInfo
*authinfo
)
574 if (!authinfo
) return;
576 if (SecIsValidHandle(&authinfo
->ctx
))
577 DeleteSecurityContext(&authinfo
->ctx
);
578 if (SecIsValidHandle(&authinfo
->cred
))
579 FreeCredentialsHandle(&authinfo
->cred
);
581 HeapFree(GetProcessHeap(), 0, authinfo
->auth_data
);
582 HeapFree(GetProcessHeap(), 0, authinfo
->scheme
);
583 HeapFree(GetProcessHeap(), 0, authinfo
);
586 static UINT
retrieve_cached_basic_authorization(LPWSTR host
, LPWSTR realm
, LPSTR
*auth_data
)
588 basicAuthorizationData
*ad
;
591 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host
),debugstr_w(realm
));
593 EnterCriticalSection(&authcache_cs
);
594 LIST_FOR_EACH_ENTRY(ad
, &basicAuthorizationCache
, basicAuthorizationData
, entry
)
596 if (!strcmpiW(host
,ad
->lpszwHost
) && !strcmpW(realm
,ad
->lpszwRealm
))
598 TRACE("Authorization found in cache\n");
599 *auth_data
= HeapAlloc(GetProcessHeap(),0,ad
->AuthorizationLen
);
600 memcpy(*auth_data
,ad
->lpszAuthorization
,ad
->AuthorizationLen
);
601 rc
= ad
->AuthorizationLen
;
605 LeaveCriticalSection(&authcache_cs
);
609 static void cache_basic_authorization(LPWSTR host
, LPWSTR realm
, LPSTR auth_data
, UINT auth_data_len
)
612 basicAuthorizationData
* ad
= NULL
;
614 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host
),debugstr_w(realm
),debugstr_an(auth_data
,auth_data_len
));
616 EnterCriticalSection(&authcache_cs
);
617 LIST_FOR_EACH(cursor
, &basicAuthorizationCache
)
619 basicAuthorizationData
*check
= LIST_ENTRY(cursor
,basicAuthorizationData
,entry
);
620 if (!strcmpiW(host
,check
->lpszwHost
) && !strcmpW(realm
,check
->lpszwRealm
))
629 TRACE("Found match in cache, replacing\n");
630 HeapFree(GetProcessHeap(),0,ad
->lpszAuthorization
);
631 ad
->lpszAuthorization
= HeapAlloc(GetProcessHeap(),0,auth_data_len
);
632 memcpy(ad
->lpszAuthorization
, auth_data
, auth_data_len
);
633 ad
->AuthorizationLen
= auth_data_len
;
637 ad
= HeapAlloc(GetProcessHeap(),0,sizeof(basicAuthorizationData
));
638 ad
->lpszwHost
= heap_strdupW(host
);
639 ad
->lpszwRealm
= heap_strdupW(realm
);
640 ad
->lpszAuthorization
= HeapAlloc(GetProcessHeap(),0,auth_data_len
);
641 memcpy(ad
->lpszAuthorization
, auth_data
, auth_data_len
);
642 ad
->AuthorizationLen
= auth_data_len
;
643 list_add_head(&basicAuthorizationCache
,&ad
->entry
);
644 TRACE("authorization cached\n");
646 LeaveCriticalSection(&authcache_cs
);
649 static BOOL
retrieve_cached_authorization(LPWSTR host
, LPWSTR scheme
,
650 SEC_WINNT_AUTH_IDENTITY_W
*nt_auth_identity
)
652 authorizationData
*ad
;
654 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host
), debugstr_w(scheme
));
656 EnterCriticalSection(&authcache_cs
);
657 LIST_FOR_EACH_ENTRY(ad
, &authorizationCache
, authorizationData
, entry
) {
658 if(!strcmpiW(host
, ad
->host
) && !strcmpiW(scheme
, ad
->scheme
)) {
659 TRACE("Authorization found in cache\n");
661 nt_auth_identity
->User
= heap_strdupW(ad
->user
);
662 nt_auth_identity
->Password
= heap_strdupW(ad
->password
);
663 nt_auth_identity
->Domain
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*ad
->domain_len
);
664 if(!nt_auth_identity
->User
|| !nt_auth_identity
->Password
||
665 (!nt_auth_identity
->Domain
&& ad
->domain_len
)) {
666 HeapFree(GetProcessHeap(), 0, nt_auth_identity
->User
);
667 HeapFree(GetProcessHeap(), 0, nt_auth_identity
->Password
);
668 HeapFree(GetProcessHeap(), 0, nt_auth_identity
->Domain
);
672 nt_auth_identity
->Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
673 nt_auth_identity
->UserLength
= ad
->user_len
;
674 nt_auth_identity
->PasswordLength
= ad
->password_len
;
675 memcpy(nt_auth_identity
->Domain
, ad
->domain
, sizeof(WCHAR
)*ad
->domain_len
);
676 nt_auth_identity
->DomainLength
= ad
->domain_len
;
677 LeaveCriticalSection(&authcache_cs
);
681 LeaveCriticalSection(&authcache_cs
);
686 static void cache_authorization(LPWSTR host
, LPWSTR scheme
,
687 SEC_WINNT_AUTH_IDENTITY_W
*nt_auth_identity
)
689 authorizationData
*ad
;
692 TRACE("Caching authorization for %s:%s\n", debugstr_w(host
), debugstr_w(scheme
));
694 EnterCriticalSection(&authcache_cs
);
695 LIST_FOR_EACH_ENTRY(ad
, &authorizationCache
, authorizationData
, entry
)
696 if(!strcmpiW(host
, ad
->host
) && !strcmpiW(scheme
, ad
->scheme
)) {
702 HeapFree(GetProcessHeap(), 0, ad
->user
);
703 HeapFree(GetProcessHeap(), 0, ad
->password
);
704 HeapFree(GetProcessHeap(), 0, ad
->domain
);
706 ad
= HeapAlloc(GetProcessHeap(), 0, sizeof(authorizationData
));
708 LeaveCriticalSection(&authcache_cs
);
712 ad
->host
= heap_strdupW(host
);
713 ad
->scheme
= heap_strdupW(scheme
);
714 list_add_head(&authorizationCache
, &ad
->entry
);
717 ad
->user
= heap_strndupW(nt_auth_identity
->User
, nt_auth_identity
->UserLength
);
718 ad
->password
= heap_strndupW(nt_auth_identity
->Password
, nt_auth_identity
->PasswordLength
);
719 ad
->domain
= heap_strndupW(nt_auth_identity
->Domain
, nt_auth_identity
->DomainLength
);
720 ad
->user_len
= nt_auth_identity
->UserLength
;
721 ad
->password_len
= nt_auth_identity
->PasswordLength
;
722 ad
->domain_len
= nt_auth_identity
->DomainLength
;
724 if(!ad
->host
|| !ad
->scheme
|| !ad
->user
|| !ad
->password
725 || (nt_auth_identity
->Domain
&& !ad
->domain
)) {
726 HeapFree(GetProcessHeap(), 0, ad
->host
);
727 HeapFree(GetProcessHeap(), 0, ad
->scheme
);
728 HeapFree(GetProcessHeap(), 0, ad
->user
);
729 HeapFree(GetProcessHeap(), 0, ad
->password
);
730 HeapFree(GetProcessHeap(), 0, ad
->domain
);
731 list_remove(&ad
->entry
);
732 HeapFree(GetProcessHeap(), 0, ad
);
735 LeaveCriticalSection(&authcache_cs
);
738 static BOOL
HTTP_DoAuthorization( http_request_t
*lpwhr
, LPCWSTR pszAuthValue
,
739 struct HttpAuthInfo
**ppAuthInfo
,
740 LPWSTR domain_and_username
, LPWSTR password
,
743 SECURITY_STATUS sec_status
;
744 struct HttpAuthInfo
*pAuthInfo
= *ppAuthInfo
;
746 LPWSTR szRealm
= NULL
;
748 TRACE("%s\n", debugstr_w(pszAuthValue
));
755 pAuthInfo
= HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo
));
759 SecInvalidateHandle(&pAuthInfo
->cred
);
760 SecInvalidateHandle(&pAuthInfo
->ctx
);
761 memset(&pAuthInfo
->exp
, 0, sizeof(pAuthInfo
->exp
));
763 pAuthInfo
->auth_data
= NULL
;
764 pAuthInfo
->auth_data_len
= 0;
765 pAuthInfo
->finished
= FALSE
;
767 if (is_basic_auth_value(pszAuthValue
,NULL
))
769 static const WCHAR szBasic
[] = {'B','a','s','i','c',0};
770 pAuthInfo
->scheme
= heap_strdupW(szBasic
);
771 if (!pAuthInfo
->scheme
)
773 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
780 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity
;
782 pAuthInfo
->scheme
= heap_strdupW(pszAuthValue
);
783 if (!pAuthInfo
->scheme
)
785 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
789 if (domain_and_username
)
791 WCHAR
*user
= strchrW(domain_and_username
, '\\');
792 WCHAR
*domain
= domain_and_username
;
794 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
796 pAuthData
= &nt_auth_identity
;
801 user
= domain_and_username
;
805 nt_auth_identity
.Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
806 nt_auth_identity
.User
= user
;
807 nt_auth_identity
.UserLength
= strlenW(nt_auth_identity
.User
);
808 nt_auth_identity
.Domain
= domain
;
809 nt_auth_identity
.DomainLength
= domain
? user
- domain
- 1 : 0;
810 nt_auth_identity
.Password
= password
;
811 nt_auth_identity
.PasswordLength
= strlenW(nt_auth_identity
.Password
);
813 cache_authorization(host
, pAuthInfo
->scheme
, &nt_auth_identity
);
815 else if(retrieve_cached_authorization(host
, pAuthInfo
->scheme
, &nt_auth_identity
))
816 pAuthData
= &nt_auth_identity
;
818 /* use default credentials */
821 sec_status
= AcquireCredentialsHandleW(NULL
, pAuthInfo
->scheme
,
822 SECPKG_CRED_OUTBOUND
, NULL
,
824 NULL
, &pAuthInfo
->cred
,
827 if(pAuthData
&& !domain_and_username
) {
828 HeapFree(GetProcessHeap(), 0, nt_auth_identity
.User
);
829 HeapFree(GetProcessHeap(), 0, nt_auth_identity
.Domain
);
830 HeapFree(GetProcessHeap(), 0, nt_auth_identity
.Password
);
833 if (sec_status
== SEC_E_OK
)
835 PSecPkgInfoW sec_pkg_info
;
836 sec_status
= QuerySecurityPackageInfoW(pAuthInfo
->scheme
, &sec_pkg_info
);
837 if (sec_status
== SEC_E_OK
)
839 pAuthInfo
->max_token
= sec_pkg_info
->cbMaxToken
;
840 FreeContextBuffer(sec_pkg_info
);
843 if (sec_status
!= SEC_E_OK
)
845 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
846 debugstr_w(pAuthInfo
->scheme
), sec_status
);
847 HeapFree(GetProcessHeap(), 0, pAuthInfo
->scheme
);
848 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
852 *ppAuthInfo
= pAuthInfo
;
854 else if (pAuthInfo
->finished
)
857 if ((strlenW(pszAuthValue
) < strlenW(pAuthInfo
->scheme
)) ||
858 strncmpiW(pszAuthValue
, pAuthInfo
->scheme
, strlenW(pAuthInfo
->scheme
)))
860 ERR("authentication scheme changed from %s to %s\n",
861 debugstr_w(pAuthInfo
->scheme
), debugstr_w(pszAuthValue
));
865 if (is_basic_auth_value(pszAuthValue
,&szRealm
))
869 char *auth_data
= NULL
;
870 UINT auth_data_len
= 0;
872 TRACE("basic authentication realm %s\n",debugstr_w(szRealm
));
874 if (!domain_and_username
)
877 auth_data_len
= retrieve_cached_basic_authorization(host
, szRealm
,&auth_data
);
878 if (auth_data_len
== 0)
880 HeapFree(GetProcessHeap(),0,szRealm
);
886 userlen
= WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, lstrlenW(domain_and_username
), NULL
, 0, NULL
, NULL
);
887 passlen
= WideCharToMultiByte(CP_UTF8
, 0, password
, lstrlenW(password
), NULL
, 0, NULL
, NULL
);
889 /* length includes a nul terminator, which will be re-used for the ':' */
890 auth_data
= HeapAlloc(GetProcessHeap(), 0, userlen
+ 1 + passlen
);
893 HeapFree(GetProcessHeap(),0,szRealm
);
897 WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, -1, auth_data
, userlen
, NULL
, NULL
);
898 auth_data
[userlen
] = ':';
899 WideCharToMultiByte(CP_UTF8
, 0, password
, -1, &auth_data
[userlen
+1], passlen
, NULL
, NULL
);
900 auth_data_len
= userlen
+ 1 + passlen
;
902 cache_basic_authorization(host
, szRealm
, auth_data
, auth_data_len
);
905 pAuthInfo
->auth_data
= auth_data
;
906 pAuthInfo
->auth_data_len
= auth_data_len
;
907 pAuthInfo
->finished
= TRUE
;
908 HeapFree(GetProcessHeap(),0,szRealm
);
915 SecBufferDesc out_desc
, in_desc
;
917 unsigned char *buffer
;
918 ULONG context_req
= ISC_REQ_CONNECTION
| ISC_REQ_USE_DCE_STYLE
|
919 ISC_REQ_MUTUAL_AUTH
| ISC_REQ_DELEGATE
;
921 in
.BufferType
= SECBUFFER_TOKEN
;
925 in_desc
.ulVersion
= 0;
926 in_desc
.cBuffers
= 1;
927 in_desc
.pBuffers
= &in
;
929 pszAuthData
= pszAuthValue
+ strlenW(pAuthInfo
->scheme
);
930 if (*pszAuthData
== ' ')
933 in
.cbBuffer
= HTTP_DecodeBase64(pszAuthData
, NULL
);
934 in
.pvBuffer
= HeapAlloc(GetProcessHeap(), 0, in
.cbBuffer
);
935 HTTP_DecodeBase64(pszAuthData
, in
.pvBuffer
);
938 buffer
= HeapAlloc(GetProcessHeap(), 0, pAuthInfo
->max_token
);
940 out
.BufferType
= SECBUFFER_TOKEN
;
941 out
.cbBuffer
= pAuthInfo
->max_token
;
942 out
.pvBuffer
= buffer
;
944 out_desc
.ulVersion
= 0;
945 out_desc
.cBuffers
= 1;
946 out_desc
.pBuffers
= &out
;
948 sec_status
= InitializeSecurityContextW(first
? &pAuthInfo
->cred
: NULL
,
949 first
? NULL
: &pAuthInfo
->ctx
,
950 first
? lpwhr
->lpHttpSession
->lpszServerName
: NULL
,
951 context_req
, 0, SECURITY_NETWORK_DREP
,
952 in
.pvBuffer
? &in_desc
: NULL
,
953 0, &pAuthInfo
->ctx
, &out_desc
,
954 &pAuthInfo
->attr
, &pAuthInfo
->exp
);
955 if (sec_status
== SEC_E_OK
)
957 pAuthInfo
->finished
= TRUE
;
958 pAuthInfo
->auth_data
= out
.pvBuffer
;
959 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
960 TRACE("sending last auth packet\n");
962 else if (sec_status
== SEC_I_CONTINUE_NEEDED
)
964 pAuthInfo
->auth_data
= out
.pvBuffer
;
965 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
966 TRACE("sending next auth packet\n");
970 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status
);
971 HeapFree(GetProcessHeap(), 0, out
.pvBuffer
);
972 destroy_authinfo(pAuthInfo
);
981 /***********************************************************************
982 * HTTP_HttpAddRequestHeadersW (internal)
984 static DWORD
HTTP_HttpAddRequestHeadersW(http_request_t
*lpwhr
,
985 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
990 DWORD len
, res
= ERROR_HTTP_INVALID_HEADER
;
992 TRACE("copying header: %s\n", debugstr_wn(lpszHeader
, dwHeaderLength
));
994 if( dwHeaderLength
== ~0U )
995 len
= strlenW(lpszHeader
);
997 len
= dwHeaderLength
;
998 buffer
= HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR
)*(len
+1) );
999 lstrcpynW( buffer
, lpszHeader
, len
+ 1);
1005 LPWSTR
* pFieldAndValue
;
1007 lpszEnd
= lpszStart
;
1009 while (*lpszEnd
!= '\0')
1011 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
1016 if (*lpszStart
== '\0')
1019 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
1022 lpszEnd
++; /* Jump over newline */
1024 TRACE("interpreting header %s\n", debugstr_w(lpszStart
));
1025 if (*lpszStart
== '\0')
1027 /* Skip 0-length headers */
1028 lpszStart
= lpszEnd
;
1029 res
= ERROR_SUCCESS
;
1032 pFieldAndValue
= HTTP_InterpretHttpHeader(lpszStart
);
1035 res
= HTTP_VerifyValidHeader(lpwhr
, pFieldAndValue
[0]);
1036 if (res
== ERROR_SUCCESS
)
1037 res
= HTTP_ProcessHeader(lpwhr
, pFieldAndValue
[0],
1038 pFieldAndValue
[1], dwModifier
| HTTP_ADDHDR_FLAG_REQ
);
1039 HTTP_FreeTokens(pFieldAndValue
);
1042 lpszStart
= lpszEnd
;
1043 } while (res
== ERROR_SUCCESS
);
1045 HeapFree(GetProcessHeap(), 0, buffer
);
1050 /***********************************************************************
1051 * HttpAddRequestHeadersW (WININET.@)
1053 * Adds one or more HTTP header to the request handler
1056 * On Windows if dwHeaderLength includes the trailing '\0', then
1057 * HttpAddRequestHeadersW() adds it too. However this results in an
1058 * invalid Http header which is rejected by some servers so we probably
1059 * don't need to match Windows on that point.
1066 BOOL WINAPI
HttpAddRequestHeadersW(HINTERNET hHttpRequest
,
1067 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1069 http_request_t
*lpwhr
;
1070 DWORD res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
1072 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_wn(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
1077 lpwhr
= (http_request_t
*) WININET_GetObject( hHttpRequest
);
1078 if (lpwhr
&& lpwhr
->hdr
.htype
== WH_HHTTPREQ
)
1079 res
= HTTP_HttpAddRequestHeadersW( lpwhr
, lpszHeader
, dwHeaderLength
, dwModifier
);
1081 WININET_Release( &lpwhr
->hdr
);
1083 if(res
!= ERROR_SUCCESS
)
1085 return res
== ERROR_SUCCESS
;
1088 /***********************************************************************
1089 * HttpAddRequestHeadersA (WININET.@)
1091 * Adds one or more HTTP header to the request handler
1098 BOOL WINAPI
HttpAddRequestHeadersA(HINTERNET hHttpRequest
,
1099 LPCSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1105 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_an(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
1107 len
= MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, NULL
, 0 );
1108 hdr
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
1109 MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, hdr
, len
);
1110 if( dwHeaderLength
!= ~0U )
1111 dwHeaderLength
= len
;
1113 r
= HttpAddRequestHeadersW( hHttpRequest
, hdr
, dwHeaderLength
, dwModifier
);
1115 HeapFree( GetProcessHeap(), 0, hdr
);
1120 /***********************************************************************
1121 * HttpOpenRequestA (WININET.@)
1123 * Open a HTTP request handle
1126 * HINTERNET a HTTP request handle on success
1130 HINTERNET WINAPI
HttpOpenRequestA(HINTERNET hHttpSession
,
1131 LPCSTR lpszVerb
, LPCSTR lpszObjectName
, LPCSTR lpszVersion
,
1132 LPCSTR lpszReferrer
, LPCSTR
*lpszAcceptTypes
,
1133 DWORD dwFlags
, DWORD_PTR dwContext
)
1135 LPWSTR szVerb
= NULL
, szObjectName
= NULL
;
1136 LPWSTR szVersion
= NULL
, szReferrer
= NULL
, *szAcceptTypes
= NULL
;
1137 INT acceptTypesCount
;
1138 HINTERNET rc
= FALSE
;
1141 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
1142 debugstr_a(lpszVerb
), debugstr_a(lpszObjectName
),
1143 debugstr_a(lpszVersion
), debugstr_a(lpszReferrer
), lpszAcceptTypes
,
1144 dwFlags
, dwContext
);
1148 szVerb
= heap_strdupAtoW(lpszVerb
);
1155 szObjectName
= heap_strdupAtoW(lpszObjectName
);
1156 if ( !szObjectName
)
1162 szVersion
= heap_strdupAtoW(lpszVersion
);
1169 szReferrer
= heap_strdupAtoW(lpszReferrer
);
1174 if (lpszAcceptTypes
)
1176 acceptTypesCount
= 0;
1177 types
= lpszAcceptTypes
;
1182 /* find out how many there are */
1183 if (*types
&& **types
)
1185 TRACE("accept type: %s\n", debugstr_a(*types
));
1191 WARN("invalid accept type pointer\n");
1196 szAcceptTypes
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
*) * (acceptTypesCount
+1));
1197 if (!szAcceptTypes
) goto end
;
1199 acceptTypesCount
= 0;
1200 types
= lpszAcceptTypes
;
1205 if (*types
&& **types
)
1206 szAcceptTypes
[acceptTypesCount
++] = heap_strdupAtoW(*types
);
1210 /* ignore invalid pointer */
1215 szAcceptTypes
[acceptTypesCount
] = NULL
;
1218 rc
= HttpOpenRequestW(hHttpSession
, szVerb
, szObjectName
,
1219 szVersion
, szReferrer
,
1220 (LPCWSTR
*)szAcceptTypes
, dwFlags
, dwContext
);
1225 acceptTypesCount
= 0;
1226 while (szAcceptTypes
[acceptTypesCount
])
1228 HeapFree(GetProcessHeap(), 0, szAcceptTypes
[acceptTypesCount
]);
1231 HeapFree(GetProcessHeap(), 0, szAcceptTypes
);
1233 HeapFree(GetProcessHeap(), 0, szReferrer
);
1234 HeapFree(GetProcessHeap(), 0, szVersion
);
1235 HeapFree(GetProcessHeap(), 0, szObjectName
);
1236 HeapFree(GetProcessHeap(), 0, szVerb
);
1241 /***********************************************************************
1244 static UINT
HTTP_EncodeBase64( LPCSTR bin
, unsigned int len
, LPWSTR base64
)
1247 static const CHAR HTTP_Base64Enc
[] =
1248 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1252 /* first 6 bits, all from bin[0] */
1253 base64
[n
++] = HTTP_Base64Enc
[(bin
[0] & 0xfc) >> 2];
1254 x
= (bin
[0] & 3) << 4;
1256 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1259 base64
[n
++] = HTTP_Base64Enc
[x
];
1264 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[1]&0xf0) >> 4 ) ];
1265 x
= ( bin
[1] & 0x0f ) << 2;
1267 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1270 base64
[n
++] = HTTP_Base64Enc
[x
];
1274 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[2]&0xc0 ) >> 6 ) ];
1276 /* last 6 bits, all from bin [2] */
1277 base64
[n
++] = HTTP_Base64Enc
[ bin
[2] & 0x3f ];
1285 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1286 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1287 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1288 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1289 static const signed char HTTP_Base64Dec
[256] =
1291 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1292 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1293 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1294 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1295 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1296 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1297 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1298 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1299 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1300 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1301 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1302 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1303 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1304 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1305 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1306 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1307 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1308 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1309 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1310 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1311 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1312 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1313 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1314 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1315 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1316 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1320 /***********************************************************************
1323 static UINT
HTTP_DecodeBase64( LPCWSTR base64
, LPSTR bin
)
1331 if (base64
[0] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1332 ((in
[0] = HTTP_Base64Dec
[base64
[0]]) == -1) ||
1333 base64
[1] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1334 ((in
[1] = HTTP_Base64Dec
[base64
[1]]) == -1))
1336 WARN("invalid base64: %s\n", debugstr_w(base64
));
1340 bin
[n
] = (unsigned char) (in
[0] << 2 | in
[1] >> 4);
1343 if ((base64
[2] == '=') && (base64
[3] == '='))
1345 if (base64
[2] > ARRAYSIZE(HTTP_Base64Dec
) ||
1346 ((in
[2] = HTTP_Base64Dec
[base64
[2]]) == -1))
1348 WARN("invalid base64: %s\n", debugstr_w(&base64
[2]));
1352 bin
[n
] = (unsigned char) (in
[1] << 4 | in
[2] >> 2);
1355 if (base64
[3] == '=')
1357 if (base64
[3] > ARRAYSIZE(HTTP_Base64Dec
) ||
1358 ((in
[3] = HTTP_Base64Dec
[base64
[3]]) == -1))
1360 WARN("invalid base64: %s\n", debugstr_w(&base64
[3]));
1364 bin
[n
] = (unsigned char) (((in
[2] << 6) & 0xc0) | in
[3]);
1373 /***********************************************************************
1374 * HTTP_InsertAuthorization
1376 * Insert or delete the authorization field in the request header.
1378 static BOOL
HTTP_InsertAuthorization( http_request_t
*lpwhr
, struct HttpAuthInfo
*pAuthInfo
, LPCWSTR header
)
1382 static const WCHAR wszSpace
[] = {' ',0};
1383 static const WCHAR wszBasic
[] = {'B','a','s','i','c',0};
1385 WCHAR
*authorization
= NULL
;
1387 if (pAuthInfo
->auth_data_len
)
1389 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1390 len
= strlenW(pAuthInfo
->scheme
)+1+((pAuthInfo
->auth_data_len
+2)*4)/3;
1391 authorization
= HeapAlloc(GetProcessHeap(), 0, (len
+1)*sizeof(WCHAR
));
1395 strcpyW(authorization
, pAuthInfo
->scheme
);
1396 strcatW(authorization
, wszSpace
);
1397 HTTP_EncodeBase64(pAuthInfo
->auth_data
,
1398 pAuthInfo
->auth_data_len
,
1399 authorization
+strlenW(authorization
));
1401 /* clear the data as it isn't valid now that it has been sent to the
1402 * server, unless it's Basic authentication which doesn't do
1403 * connection tracking */
1404 if (strcmpiW(pAuthInfo
->scheme
, wszBasic
))
1406 HeapFree(GetProcessHeap(), 0, pAuthInfo
->auth_data
);
1407 pAuthInfo
->auth_data
= NULL
;
1408 pAuthInfo
->auth_data_len
= 0;
1412 TRACE("Inserting authorization: %s\n", debugstr_w(authorization
));
1414 HTTP_ProcessHeader(lpwhr
, header
, authorization
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
1416 HeapFree(GetProcessHeap(), 0, authorization
);
1421 static WCHAR
*HTTP_BuildProxyRequestUrl(http_request_t
*req
)
1423 WCHAR new_location
[INTERNET_MAX_URL_LENGTH
], *url
;
1426 size
= sizeof(new_location
);
1427 if (HTTP_HttpQueryInfoW(req
, HTTP_QUERY_LOCATION
, new_location
, &size
, NULL
) == ERROR_SUCCESS
)
1429 if (!(url
= HeapAlloc( GetProcessHeap(), 0, size
+ sizeof(WCHAR
) ))) return NULL
;
1430 strcpyW( url
, new_location
);
1434 static const WCHAR slash
[] = { '/',0 };
1435 static const WCHAR format
[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1436 static const WCHAR formatSSL
[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1437 http_session_t
*session
= req
->lpHttpSession
;
1439 size
= 16; /* "https://" + sizeof(port#) + ":/\0" */
1440 size
+= strlenW( session
->lpszHostName
) + strlenW( req
->lpszPath
);
1442 if (!(url
= HeapAlloc( GetProcessHeap(), 0, size
* sizeof(WCHAR
) ))) return NULL
;
1444 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1445 sprintfW( url
, formatSSL
, session
->lpszHostName
, session
->nHostPort
);
1447 sprintfW( url
, format
, session
->lpszHostName
, session
->nHostPort
);
1448 if (req
->lpszPath
[0] != '/') strcatW( url
, slash
);
1449 strcatW( url
, req
->lpszPath
);
1451 TRACE("url=%s\n", debugstr_w(url
));
1455 /***********************************************************************
1456 * HTTP_DealWithProxy
1458 static BOOL
HTTP_DealWithProxy(appinfo_t
*hIC
, http_session_t
*lpwhs
, http_request_t
*lpwhr
)
1460 WCHAR buf
[MAXHOSTNAME
];
1461 WCHAR protoProxy
[MAXHOSTNAME
+ 15];
1462 DWORD protoProxyLen
= sizeof(protoProxy
) / sizeof(protoProxy
[0]);
1463 WCHAR proxy
[MAXHOSTNAME
+ 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1464 static WCHAR szNul
[] = { 0 };
1465 URL_COMPONENTSW UrlComponents
;
1466 static const WCHAR protoHttp
[] = { 'h','t','t','p',0 };
1467 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/',0 };
1468 static const WCHAR szFormat
[] = { 'h','t','t','p',':','/','/','%','s',0 };
1470 memset( &UrlComponents
, 0, sizeof UrlComponents
);
1471 UrlComponents
.dwStructSize
= sizeof UrlComponents
;
1472 UrlComponents
.lpszHostName
= buf
;
1473 UrlComponents
.dwHostNameLength
= MAXHOSTNAME
;
1475 if (!INTERNET_FindProxyForProtocol(hIC
->lpszProxy
, protoHttp
, protoProxy
, &protoProxyLen
))
1477 if( CSTR_EQUAL
!= CompareStringW(LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
1478 protoProxy
,strlenW(szHttp
),szHttp
,strlenW(szHttp
)) )
1479 sprintfW(proxy
, szFormat
, protoProxy
);
1481 strcpyW(proxy
, protoProxy
);
1482 if( !InternetCrackUrlW(proxy
, 0, 0, &UrlComponents
) )
1484 if( UrlComponents
.dwHostNameLength
== 0 )
1487 if( !lpwhr
->lpszPath
)
1488 lpwhr
->lpszPath
= szNul
;
1490 if(UrlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
1491 UrlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
1493 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszServerName
);
1494 lpwhs
->lpszServerName
= heap_strdupW(UrlComponents
.lpszHostName
);
1495 lpwhs
->nServerPort
= UrlComponents
.nPort
;
1497 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs
->lpszServerName
), lpwhs
->nServerPort
);
1501 #ifndef INET6_ADDRSTRLEN
1502 #define INET6_ADDRSTRLEN 46
1505 static DWORD
HTTP_ResolveName(http_request_t
*lpwhr
)
1507 char szaddr
[INET6_ADDRSTRLEN
];
1508 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
1511 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1512 INTERNET_STATUS_RESOLVING_NAME
,
1513 lpwhs
->lpszServerName
,
1514 (strlenW(lpwhs
->lpszServerName
)+1) * sizeof(WCHAR
));
1516 lpwhs
->sa_len
= sizeof(lpwhs
->socketAddress
);
1517 if (!GetAddress(lpwhs
->lpszServerName
, lpwhs
->nServerPort
,
1518 (struct sockaddr
*)&lpwhs
->socketAddress
, &lpwhs
->sa_len
))
1519 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
1521 switch (lpwhs
->socketAddress
.ss_family
)
1524 addr
= &((struct sockaddr_in
*)&lpwhs
->socketAddress
)->sin_addr
;
1527 addr
= &((struct sockaddr_in6
*)&lpwhs
->socketAddress
)->sin6_addr
;
1530 WARN("unsupported family %d\n", lpwhs
->socketAddress
.ss_family
);
1531 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
1533 inet_ntop(lpwhs
->socketAddress
.ss_family
, addr
, szaddr
, sizeof(szaddr
));
1534 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1535 INTERNET_STATUS_NAME_RESOLVED
,
1536 szaddr
, strlen(szaddr
)+1);
1538 TRACE("resolved %s to %s\n", debugstr_w(lpwhs
->lpszServerName
), szaddr
);
1539 return ERROR_SUCCESS
;
1542 static BOOL
HTTP_GetRequestURL(http_request_t
*req
, LPWSTR buf
)
1544 LPHTTPHEADERW host_header
;
1546 static const WCHAR formatW
[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1548 host_header
= HTTP_GetHeader(req
, hostW
);
1552 sprintfW(buf
, formatW
, host_header
->lpszValue
, req
->lpszPath
); /* FIXME */
1557 /***********************************************************************
1558 * HTTPREQ_Destroy (internal)
1560 * Deallocate request handle
1563 static void HTTPREQ_Destroy(object_header_t
*hdr
)
1565 http_request_t
*lpwhr
= (http_request_t
*) hdr
;
1570 if(lpwhr
->hCacheFile
) {
1571 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1574 CloseHandle(lpwhr
->hCacheFile
);
1576 memset(&ft
, 0, sizeof(FILETIME
));
1577 if(HTTP_GetRequestURL(lpwhr
, url
)) {
1578 CommitUrlCacheEntryW(url
, lpwhr
->lpszCacheFile
, ft
, ft
,
1579 NORMAL_CACHE_ENTRY
, NULL
, 0, NULL
, 0);
1583 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszCacheFile
);
1585 DeleteCriticalSection( &lpwhr
->read_section
);
1586 WININET_Release(&lpwhr
->lpHttpSession
->hdr
);
1588 destroy_authinfo(lpwhr
->pAuthInfo
);
1589 destroy_authinfo(lpwhr
->pProxyAuthInfo
);
1591 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszPath
);
1592 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVerb
);
1593 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszRawHeaders
);
1594 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVersion
);
1595 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszStatusText
);
1597 for (i
= 0; i
< lpwhr
->nCustHeaders
; i
++)
1599 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[i
].lpszField
);
1600 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[i
].lpszValue
);
1604 if(lpwhr
->gzip_stream
) {
1605 if(!lpwhr
->gzip_stream
->end_of_data
)
1606 inflateEnd(&lpwhr
->gzip_stream
->zstream
);
1607 HeapFree(GetProcessHeap(), 0, lpwhr
->gzip_stream
);
1611 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
);
1612 HeapFree(GetProcessHeap(), 0, lpwhr
);
1615 static void HTTPREQ_CloseConnection(object_header_t
*hdr
)
1617 http_request_t
*lpwhr
= (http_request_t
*) hdr
;
1619 TRACE("%p\n",lpwhr
);
1621 if (!NETCON_connected(&lpwhr
->netConnection
))
1624 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1625 INTERNET_STATUS_CLOSING_CONNECTION
, 0, 0);
1627 NETCON_close(&lpwhr
->netConnection
);
1629 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1630 INTERNET_STATUS_CONNECTION_CLOSED
, 0, 0);
1633 static BOOL
HTTP_KeepAlive(http_request_t
*lpwhr
)
1635 WCHAR szVersion
[10];
1636 WCHAR szConnectionResponse
[20];
1637 DWORD dwBufferSize
= sizeof(szVersion
);
1638 BOOL keepalive
= FALSE
;
1640 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1641 * the connection is keep-alive by default */
1642 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_VERSION
, szVersion
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1643 && !strcmpiW(szVersion
, g_szHttp1_1
))
1648 dwBufferSize
= sizeof(szConnectionResponse
);
1649 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_PROXY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1650 || HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
)
1652 keepalive
= !strcmpiW(szConnectionResponse
, szKeepAlive
);
1658 static DWORD
HTTPREQ_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
1660 http_request_t
*req
= (http_request_t
*)hdr
;
1663 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO
:
1665 http_session_t
*lpwhs
= req
->lpHttpSession
;
1666 INTERNET_DIAGNOSTIC_SOCKET_INFO
*info
= buffer
;
1668 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1670 if (*size
< sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
))
1671 return ERROR_INSUFFICIENT_BUFFER
;
1672 *size
= sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
);
1673 /* FIXME: can't get a SOCKET from our connection since we don't use
1677 /* FIXME: get source port from req->netConnection */
1678 info
->SourcePort
= 0;
1679 info
->DestPort
= lpwhs
->nHostPort
;
1681 if (HTTP_KeepAlive(req
))
1682 info
->Flags
|= IDSI_FLAG_KEEP_ALIVE
;
1683 if (lpwhs
->lpAppInfo
->lpszProxy
&& lpwhs
->lpAppInfo
->lpszProxy
[0] != 0)
1684 info
->Flags
|= IDSI_FLAG_PROXY
;
1685 if (req
->netConnection
.useSSL
)
1686 info
->Flags
|= IDSI_FLAG_SECURE
;
1688 return ERROR_SUCCESS
;
1691 case INTERNET_OPTION_SECURITY_FLAGS
:
1696 if (*size
< sizeof(ULONG
))
1697 return ERROR_INSUFFICIENT_BUFFER
;
1699 *size
= sizeof(DWORD
);
1701 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1702 flags
|= SECURITY_FLAG_SECURE
;
1703 flags
|= req
->netConnection
.security_flags
;
1704 bits
= NETCON_GetCipherStrength(&req
->netConnection
);
1706 flags
|= SECURITY_FLAG_STRENGTH_STRONG
;
1707 else if (bits
>= 56)
1708 flags
|= SECURITY_FLAG_STRENGTH_MEDIUM
;
1710 flags
|= SECURITY_FLAG_STRENGTH_WEAK
;
1711 *(DWORD
*)buffer
= flags
;
1712 return ERROR_SUCCESS
;
1715 case INTERNET_OPTION_HANDLE_TYPE
:
1716 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1718 if (*size
< sizeof(ULONG
))
1719 return ERROR_INSUFFICIENT_BUFFER
;
1721 *size
= sizeof(DWORD
);
1722 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_HTTP_REQUEST
;
1723 return ERROR_SUCCESS
;
1725 case INTERNET_OPTION_URL
: {
1726 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1731 static const WCHAR httpW
[] = {'h','t','t','p',':','/','/',0};
1733 TRACE("INTERNET_OPTION_URL\n");
1735 host
= HTTP_GetHeader(req
, hostW
);
1736 strcpyW(url
, httpW
);
1737 strcatW(url
, host
->lpszValue
);
1738 if (NULL
!= (pch
= strchrW(url
+ strlenW(httpW
), ':')))
1740 strcatW(url
, req
->lpszPath
);
1742 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url
));
1745 len
= (strlenW(url
)+1) * sizeof(WCHAR
);
1747 return ERROR_INSUFFICIENT_BUFFER
;
1750 strcpyW(buffer
, url
);
1751 return ERROR_SUCCESS
;
1753 len
= WideCharToMultiByte(CP_ACP
, 0, url
, -1, buffer
, *size
, NULL
, NULL
);
1755 return ERROR_INSUFFICIENT_BUFFER
;
1758 return ERROR_SUCCESS
;
1762 case INTERNET_OPTION_CACHE_TIMESTAMPS
: {
1763 INTERNET_CACHE_ENTRY_INFOW
*info
;
1764 INTERNET_CACHE_TIMESTAMPS
*ts
= buffer
;
1765 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1766 DWORD nbytes
, error
;
1769 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1771 if (*size
< sizeof(*ts
))
1773 *size
= sizeof(*ts
);
1774 return ERROR_INSUFFICIENT_BUFFER
;
1777 HTTP_GetRequestURL(req
, url
);
1778 ret
= GetUrlCacheEntryInfoW(url
, NULL
, &nbytes
);
1779 error
= GetLastError();
1780 if (!ret
&& error
== ERROR_INSUFFICIENT_BUFFER
)
1782 if (!(info
= HeapAlloc(GetProcessHeap(), 0, nbytes
)))
1783 return ERROR_OUTOFMEMORY
;
1785 GetUrlCacheEntryInfoW(url
, info
, &nbytes
);
1787 ts
->ftExpires
= info
->ExpireTime
;
1788 ts
->ftLastModified
= info
->LastModifiedTime
;
1790 HeapFree(GetProcessHeap(), 0, info
);
1791 *size
= sizeof(*ts
);
1792 return ERROR_SUCCESS
;
1797 case INTERNET_OPTION_DATAFILE_NAME
: {
1800 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1802 if(!req
->lpszCacheFile
) {
1804 return ERROR_INTERNET_ITEM_NOT_FOUND
;
1808 req_size
= (lstrlenW(req
->lpszCacheFile
)+1) * sizeof(WCHAR
);
1809 if(*size
< req_size
)
1810 return ERROR_INSUFFICIENT_BUFFER
;
1813 memcpy(buffer
, req
->lpszCacheFile
, *size
);
1814 return ERROR_SUCCESS
;
1816 req_size
= WideCharToMultiByte(CP_ACP
, 0, req
->lpszCacheFile
, -1, NULL
, 0, NULL
, NULL
);
1817 if (req_size
> *size
)
1818 return ERROR_INSUFFICIENT_BUFFER
;
1820 *size
= WideCharToMultiByte(CP_ACP
, 0, req
->lpszCacheFile
,
1821 -1, buffer
, *size
, NULL
, NULL
);
1822 return ERROR_SUCCESS
;
1826 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT
: {
1827 PCCERT_CONTEXT context
;
1829 if(*size
< sizeof(INTERNET_CERTIFICATE_INFOA
)) {
1830 *size
= sizeof(INTERNET_CERTIFICATE_INFOA
);
1831 return ERROR_INSUFFICIENT_BUFFER
;
1834 context
= (PCCERT_CONTEXT
)NETCON_GetCert(&(req
->netConnection
));
1836 INTERNET_CERTIFICATE_INFOA
*info
= (INTERNET_CERTIFICATE_INFOA
*)buffer
;
1839 memset(info
, 0, sizeof(INTERNET_CERTIFICATE_INFOW
));
1840 info
->ftExpiry
= context
->pCertInfo
->NotAfter
;
1841 info
->ftStart
= context
->pCertInfo
->NotBefore
;
1842 len
= CertNameToStrA(context
->dwCertEncodingType
,
1843 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1844 info
->lpszSubjectInfo
= LocalAlloc(0, len
);
1845 if(info
->lpszSubjectInfo
)
1846 CertNameToStrA(context
->dwCertEncodingType
,
1847 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
,
1848 info
->lpszSubjectInfo
, len
);
1849 len
= CertNameToStrA(context
->dwCertEncodingType
,
1850 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1851 info
->lpszIssuerInfo
= LocalAlloc(0, len
);
1852 if(info
->lpszIssuerInfo
)
1853 CertNameToStrA(context
->dwCertEncodingType
,
1854 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
,
1855 info
->lpszIssuerInfo
, len
);
1856 info
->dwKeySize
= NETCON_GetCipherStrength(&req
->netConnection
);
1857 CertFreeCertificateContext(context
);
1858 return ERROR_SUCCESS
;
1863 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
1866 static DWORD
HTTPREQ_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
1868 http_request_t
*req
= (http_request_t
*)hdr
;
1871 case INTERNET_OPTION_SECURITY_FLAGS
:
1875 if (!buffer
|| size
!= sizeof(DWORD
))
1876 return ERROR_INVALID_PARAMETER
;
1877 flags
= *(DWORD
*)buffer
;
1878 TRACE("%08x\n", flags
);
1879 req
->netConnection
.security_flags
= flags
;
1880 return ERROR_SUCCESS
;
1882 case INTERNET_OPTION_SEND_TIMEOUT
:
1883 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
1884 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1886 if (size
!= sizeof(DWORD
))
1887 return ERROR_INVALID_PARAMETER
;
1889 return NETCON_set_timeout(&req
->netConnection
, option
== INTERNET_OPTION_SEND_TIMEOUT
,
1892 case INTERNET_OPTION_USERNAME
:
1893 HeapFree(GetProcessHeap(), 0, req
->lpHttpSession
->lpszUserName
);
1894 if (!(req
->lpHttpSession
->lpszUserName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
1895 return ERROR_SUCCESS
;
1897 case INTERNET_OPTION_PASSWORD
:
1898 HeapFree(GetProcessHeap(), 0, req
->lpHttpSession
->lpszPassword
);
1899 if (!(req
->lpHttpSession
->lpszPassword
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
1900 return ERROR_SUCCESS
;
1901 case INTERNET_OPTION_HTTP_DECODING
:
1902 if(size
!= sizeof(BOOL
))
1903 return ERROR_INVALID_PARAMETER
;
1904 req
->decoding
= *(BOOL
*)buffer
;
1905 return ERROR_SUCCESS
;
1908 return ERROR_INTERNET_INVALID_OPTION
;
1911 /* read some more data into the read buffer (the read section must be held) */
1912 static DWORD
read_more_data( http_request_t
*req
, int maxlen
)
1919 /* move existing data to the start of the buffer */
1921 memmove( req
->read_buf
, req
->read_buf
+ req
->read_pos
, req
->read_size
);
1925 if (maxlen
== -1) maxlen
= sizeof(req
->read_buf
);
1927 res
= NETCON_recv( &req
->netConnection
, req
->read_buf
+ req
->read_size
,
1928 maxlen
- req
->read_size
, 0, &len
);
1929 if(res
== ERROR_SUCCESS
)
1930 req
->read_size
+= len
;
1935 /* remove some amount of data from the read buffer (the read section must be held) */
1936 static void remove_data( http_request_t
*req
, int count
)
1938 if (!(req
->read_size
-= count
)) req
->read_pos
= 0;
1939 else req
->read_pos
+= count
;
1942 static BOOL
read_line( http_request_t
*req
, LPSTR buffer
, DWORD
*len
)
1944 int count
, bytes_read
, pos
= 0;
1947 EnterCriticalSection( &req
->read_section
);
1950 BYTE
*eol
= memchr( req
->read_buf
+ req
->read_pos
, '\n', req
->read_size
);
1954 count
= eol
- (req
->read_buf
+ req
->read_pos
);
1955 bytes_read
= count
+ 1;
1957 else count
= bytes_read
= req
->read_size
;
1959 count
= min( count
, *len
- pos
);
1960 memcpy( buffer
+ pos
, req
->read_buf
+ req
->read_pos
, count
);
1962 remove_data( req
, bytes_read
);
1965 if ((res
= read_more_data( req
, -1 )) != ERROR_SUCCESS
|| !req
->read_size
)
1968 TRACE( "returning empty string\n" );
1969 LeaveCriticalSection( &req
->read_section
);
1970 INTERNET_SetLastError(res
);
1974 LeaveCriticalSection( &req
->read_section
);
1978 if (pos
&& buffer
[pos
- 1] == '\r') pos
--;
1981 buffer
[*len
- 1] = 0;
1982 TRACE( "returning %s\n", debugstr_a(buffer
));
1986 /* discard data contents until we reach end of line (the read section must be held) */
1987 static DWORD
discard_eol( http_request_t
*req
)
1993 BYTE
*eol
= memchr( req
->read_buf
+ req
->read_pos
, '\n', req
->read_size
);
1996 remove_data( req
, (eol
+ 1) - (req
->read_buf
+ req
->read_pos
) );
1999 req
->read_pos
= req
->read_size
= 0; /* discard everything */
2000 if ((res
= read_more_data( req
, -1 )) != ERROR_SUCCESS
) return res
;
2001 } while (req
->read_size
);
2002 return ERROR_SUCCESS
;
2005 /* read the size of the next chunk (the read section must be held) */
2006 static DWORD
start_next_chunk( http_request_t
*req
)
2008 DWORD chunk_size
= 0, res
;
2010 if (!req
->dwContentLength
) return ERROR_SUCCESS
;
2011 if (req
->dwContentLength
== req
->dwContentRead
)
2013 /* read terminator for the previous chunk */
2014 if ((res
= discard_eol( req
)) != ERROR_SUCCESS
) return res
;
2015 req
->dwContentLength
= ~0u;
2016 req
->dwContentRead
= 0;
2020 while (req
->read_size
)
2022 char ch
= req
->read_buf
[req
->read_pos
];
2023 if (ch
>= '0' && ch
<= '9') chunk_size
= chunk_size
* 16 + ch
- '0';
2024 else if (ch
>= 'a' && ch
<= 'f') chunk_size
= chunk_size
* 16 + ch
- 'a' + 10;
2025 else if (ch
>= 'A' && ch
<= 'F') chunk_size
= chunk_size
* 16 + ch
- 'A' + 10;
2026 else if (ch
== ';' || ch
== '\r' || ch
== '\n')
2028 TRACE( "reading %u byte chunk\n", chunk_size
);
2029 req
->dwContentLength
= chunk_size
;
2030 req
->dwContentRead
= 0;
2031 return discard_eol( req
);
2033 remove_data( req
, 1 );
2035 if ((res
= read_more_data( req
, -1 )) != ERROR_SUCCESS
) return res
;
2036 if (!req
->read_size
)
2038 req
->dwContentLength
= req
->dwContentRead
= 0;
2039 return ERROR_SUCCESS
;
2044 /* check if we have reached the end of the data to read (the read section must be held) */
2045 static BOOL
end_of_read_data( http_request_t
*req
)
2047 if (req
->gzip_stream
) return req
->gzip_stream
->end_of_data
&& !req
->gzip_stream
->buf_size
;
2048 if (req
->read_chunked
) return (req
->dwContentLength
== 0);
2049 if (req
->dwContentLength
== ~0u) return FALSE
;
2050 return (req
->dwContentLength
== req
->dwContentRead
);
2053 /* fetch some more data into the read buffer (the read section must be held) */
2054 static DWORD
refill_buffer( http_request_t
*req
)
2056 int len
= sizeof(req
->read_buf
);
2059 if (req
->read_chunked
&& (req
->dwContentLength
== ~0u || req
->dwContentLength
== req
->dwContentRead
))
2061 if ((res
= start_next_chunk( req
)) != ERROR_SUCCESS
) return res
;
2064 if (req
->dwContentLength
!= ~0u) len
= min( len
, req
->dwContentLength
- req
->dwContentRead
);
2065 if (len
<= req
->read_size
) return ERROR_SUCCESS
;
2067 if ((res
= read_more_data( req
, len
)) != ERROR_SUCCESS
) return res
;
2068 if (!req
->read_size
) req
->dwContentLength
= req
->dwContentRead
= 0;
2069 return ERROR_SUCCESS
;
2072 static DWORD
read_gzip_data(http_request_t
*req
, BYTE
*buf
, int size
, BOOL sync
, int *read_ret
)
2074 DWORD ret
= ERROR_SUCCESS
;
2078 z_stream
*zstream
= &req
->gzip_stream
->zstream
;
2082 while(read
< size
&& !req
->gzip_stream
->end_of_data
) {
2083 if(!req
->read_size
) {
2084 if(!sync
|| refill_buffer(req
) != ERROR_SUCCESS
)
2088 if(req
->dwContentRead
== req
->dwContentLength
)
2091 buf_avail
= req
->dwContentLength
== ~0 ? req
->read_size
: min(req
->read_size
, req
->dwContentLength
-req
->dwContentRead
);
2093 zstream
->next_in
= req
->read_buf
+req
->read_pos
;
2094 zstream
->avail_in
= buf_avail
;
2095 zstream
->next_out
= buf
+read
;
2096 zstream
->avail_out
= size
-read
;
2097 zres
= inflate(zstream
, Z_FULL_FLUSH
);
2098 read
= size
- zstream
->avail_out
;
2099 req
->dwContentRead
+= buf_avail
-zstream
->avail_in
;
2100 remove_data(req
, buf_avail
-zstream
->avail_in
);
2101 if(zres
== Z_STREAM_END
) {
2102 TRACE("end of data\n");
2103 req
->gzip_stream
->end_of_data
= TRUE
;
2104 inflateEnd(&req
->gzip_stream
->zstream
);
2105 }else if(zres
!= Z_OK
) {
2106 WARN("inflate failed %d\n", zres
);
2108 ret
= ERROR_INTERNET_DECODING_FAILED
;
2118 static void refill_gzip_buffer(http_request_t
*req
)
2123 if(!req
->gzip_stream
|| !req
->read_size
|| req
->gzip_stream
->buf_size
== sizeof(req
->gzip_stream
->buf
))
2126 if(req
->gzip_stream
->buf_pos
) {
2127 if(req
->gzip_stream
->buf_size
)
2128 memmove(req
->gzip_stream
->buf
, req
->gzip_stream
->buf
+ req
->gzip_stream
->buf_pos
, req
->gzip_stream
->buf_size
);
2129 req
->gzip_stream
->buf_pos
= 0;
2132 res
= read_gzip_data(req
, req
->gzip_stream
->buf
+ req
->gzip_stream
->buf_size
,
2133 sizeof(req
->gzip_stream
->buf
) - req
->gzip_stream
->buf_size
, FALSE
, &len
);
2134 if(res
== ERROR_SUCCESS
)
2135 req
->gzip_stream
->buf_size
+= len
;
2138 /* return the size of data available to be read immediately (the read section must be held) */
2139 static DWORD
get_avail_data( http_request_t
*req
)
2141 if (req
->gzip_stream
) {
2142 refill_gzip_buffer(req
);
2143 return req
->gzip_stream
->buf_size
;
2145 if (req
->read_chunked
&& (req
->dwContentLength
== ~0u || req
->dwContentLength
== req
->dwContentRead
))
2147 return min( req
->read_size
, req
->dwContentLength
- req
->dwContentRead
);
2150 static void HTTP_ReceiveRequestData(http_request_t
*req
, BOOL first_notif
)
2152 INTERNET_ASYNC_RESULT iar
;
2157 EnterCriticalSection( &req
->read_section
);
2158 if ((res
= refill_buffer( req
)) == ERROR_SUCCESS
) {
2159 iar
.dwResult
= (DWORD_PTR
)req
->hdr
.hInternet
;
2160 iar
.dwError
= first_notif
? 0 : get_avail_data(req
);
2165 LeaveCriticalSection( &req
->read_section
);
2167 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2168 sizeof(INTERNET_ASYNC_RESULT
));
2171 /* read data from the http connection (the read section must be held) */
2172 static DWORD
HTTPREQ_Read(http_request_t
*req
, void *buffer
, DWORD size
, DWORD
*read
, BOOL sync
)
2174 BOOL finished_reading
= FALSE
;
2175 int len
, bytes_read
= 0;
2176 DWORD ret
= ERROR_SUCCESS
;
2178 EnterCriticalSection( &req
->read_section
);
2180 if (req
->read_chunked
&& (req
->dwContentLength
== ~0u || req
->dwContentLength
== req
->dwContentRead
))
2182 if (start_next_chunk( req
) != ERROR_SUCCESS
) goto done
;
2185 if(req
->gzip_stream
) {
2186 if(req
->gzip_stream
->buf_size
) {
2187 bytes_read
= min(req
->gzip_stream
->buf_size
, size
);
2188 memcpy(buffer
, req
->gzip_stream
->buf
+ req
->gzip_stream
->buf_pos
, bytes_read
);
2189 req
->gzip_stream
->buf_pos
+= bytes_read
;
2190 req
->gzip_stream
->buf_size
-= bytes_read
;
2191 }else if(!req
->read_size
&& !req
->gzip_stream
->end_of_data
) {
2195 if(size
> bytes_read
) {
2196 ret
= read_gzip_data(req
, (BYTE
*)buffer
+bytes_read
, size
-bytes_read
, sync
, &len
);
2197 if(ret
== ERROR_SUCCESS
)
2201 finished_reading
= req
->gzip_stream
->end_of_data
&& !req
->gzip_stream
->buf_size
;
2203 if (req
->dwContentLength
!= ~0u) size
= min( size
, req
->dwContentLength
- req
->dwContentRead
);
2205 if (req
->read_size
) {
2206 bytes_read
= min( req
->read_size
, size
);
2207 memcpy( buffer
, req
->read_buf
+ req
->read_pos
, bytes_read
);
2208 remove_data( req
, bytes_read
);
2211 if (size
> bytes_read
&& (!bytes_read
|| sync
)) {
2212 if (NETCON_recv( &req
->netConnection
, (char *)buffer
+ bytes_read
, size
- bytes_read
,
2213 sync
? MSG_WAITALL
: 0, &len
) == ERROR_SUCCESS
)
2215 /* always return success, even if the network layer returns an error */
2218 finished_reading
= !bytes_read
&& req
->dwContentRead
== req
->dwContentLength
;
2219 req
->dwContentRead
+= bytes_read
;
2224 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read
, req
->dwContentRead
, req
->dwContentLength
);
2225 LeaveCriticalSection( &req
->read_section
);
2227 if(ret
== ERROR_SUCCESS
&& req
->lpszCacheFile
) {
2229 DWORD dwBytesWritten
;
2231 res
= WriteFile(req
->hCacheFile
, buffer
, bytes_read
, &dwBytesWritten
, NULL
);
2233 WARN("WriteFile failed: %u\n", GetLastError());
2236 if(finished_reading
)
2237 HTTP_FinishedReading(req
);
2243 static DWORD
HTTPREQ_ReadFile(object_header_t
*hdr
, void *buffer
, DWORD size
, DWORD
*read
)
2245 http_request_t
*req
= (http_request_t
*)hdr
;
2248 EnterCriticalSection( &req
->read_section
);
2249 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2250 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2252 res
= HTTPREQ_Read(req
, buffer
, size
, read
, TRUE
);
2253 if(res
== ERROR_SUCCESS
)
2255 LeaveCriticalSection( &req
->read_section
);
2260 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST
*workRequest
)
2262 struct WORKREQ_INTERNETREADFILEEXA
const *data
= &workRequest
->u
.InternetReadFileExA
;
2263 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2264 INTERNET_ASYNC_RESULT iar
;
2267 TRACE("INTERNETREADFILEEXA %p\n", workRequest
->hdr
);
2269 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2270 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2272 iar
.dwResult
= res
== ERROR_SUCCESS
;
2275 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2276 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2277 sizeof(INTERNET_ASYNC_RESULT
));
2280 static DWORD
HTTPREQ_ReadFileExA(object_header_t
*hdr
, INTERNET_BUFFERSA
*buffers
,
2281 DWORD flags
, DWORD_PTR context
)
2283 http_request_t
*req
= (http_request_t
*)hdr
;
2284 DWORD res
, size
, read
, error
= ERROR_SUCCESS
;
2286 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2287 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2289 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2290 return ERROR_INVALID_PARAMETER
;
2292 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2294 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2296 WORKREQUEST workRequest
;
2298 if (TryEnterCriticalSection( &req
->read_section
))
2300 if (get_avail_data(req
))
2302 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2303 &buffers
->dwBufferLength
, FALSE
);
2304 size
= buffers
->dwBufferLength
;
2305 LeaveCriticalSection( &req
->read_section
);
2308 LeaveCriticalSection( &req
->read_section
);
2311 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExAProc
;
2312 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2313 workRequest
.u
.InternetReadFileExA
.lpBuffersOut
= buffers
;
2315 INTERNET_AsyncCall(&workRequest
);
2317 return ERROR_IO_PENDING
;
2321 size
= buffers
->dwBufferLength
;
2323 EnterCriticalSection( &req
->read_section
);
2324 if(hdr
->dwError
== ERROR_SUCCESS
)
2325 hdr
->dwError
= INTERNET_HANDLE_IN_USE
;
2326 else if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2327 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2330 res
= HTTPREQ_Read(req
, (char*)buffers
->lpvBuffer
+read
, size
-read
,
2331 &buffers
->dwBufferLength
, !(flags
& IRF_NO_WAIT
));
2332 if(res
== ERROR_SUCCESS
)
2333 read
+= buffers
->dwBufferLength
;
2337 if(!req
->read_chunked
|| read
==size
|| req
->dwContentLength
!=req
->dwContentRead
2338 || !req
->dwContentLength
|| (req
->gzip_stream
&& req
->gzip_stream
->end_of_data
))
2340 LeaveCriticalSection( &req
->read_section
);
2342 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2343 &buffers
->dwBufferLength
, sizeof(buffers
->dwBufferLength
));
2344 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2345 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2347 EnterCriticalSection( &req
->read_section
);
2350 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2351 hdr
->dwError
= ERROR_SUCCESS
;
2353 error
= hdr
->dwError
;
2355 LeaveCriticalSection( &req
->read_section
);
2356 size
= buffers
->dwBufferLength
;
2357 buffers
->dwBufferLength
= read
;
2360 if (res
== ERROR_SUCCESS
) {
2361 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2362 &size
, sizeof(size
));
2365 return res
==ERROR_SUCCESS
? error
: res
;
2368 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST
*workRequest
)
2370 struct WORKREQ_INTERNETREADFILEEXW
const *data
= &workRequest
->u
.InternetReadFileExW
;
2371 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2372 INTERNET_ASYNC_RESULT iar
;
2375 TRACE("INTERNETREADFILEEXW %p\n", workRequest
->hdr
);
2377 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2378 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2380 iar
.dwResult
= res
== ERROR_SUCCESS
;
2383 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2384 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2385 sizeof(INTERNET_ASYNC_RESULT
));
2388 static DWORD
HTTPREQ_ReadFileExW(object_header_t
*hdr
, INTERNET_BUFFERSW
*buffers
,
2389 DWORD flags
, DWORD_PTR context
)
2392 http_request_t
*req
= (http_request_t
*)hdr
;
2393 DWORD res
, size
, read
, error
= ERROR_SUCCESS
;
2395 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2396 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2398 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2399 return ERROR_INVALID_PARAMETER
;
2401 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2403 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2405 WORKREQUEST workRequest
;
2407 if (TryEnterCriticalSection( &req
->read_section
))
2409 if (get_avail_data(req
))
2411 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2412 &buffers
->dwBufferLength
, FALSE
);
2413 size
= buffers
->dwBufferLength
;
2414 LeaveCriticalSection( &req
->read_section
);
2417 LeaveCriticalSection( &req
->read_section
);
2420 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExWProc
;
2421 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2422 workRequest
.u
.InternetReadFileExW
.lpBuffersOut
= buffers
;
2424 INTERNET_AsyncCall(&workRequest
);
2426 return ERROR_IO_PENDING
;
2430 size
= buffers
->dwBufferLength
;
2432 EnterCriticalSection( &req
->read_section
);
2433 if(hdr
->dwError
== ERROR_SUCCESS
)
2434 hdr
->dwError
= INTERNET_HANDLE_IN_USE
;
2435 else if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2436 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2439 res
= HTTPREQ_Read(req
, (char*)buffers
->lpvBuffer
+read
, size
-read
,
2440 &buffers
->dwBufferLength
, !(flags
& IRF_NO_WAIT
));
2441 if(res
== ERROR_SUCCESS
)
2442 read
+= buffers
->dwBufferLength
;
2446 if(!req
->read_chunked
|| read
==size
|| req
->dwContentLength
!=req
->dwContentRead
2447 || !req
->dwContentLength
|| (req
->gzip_stream
&& req
->gzip_stream
->end_of_data
))
2449 LeaveCriticalSection( &req
->read_section
);
2451 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2452 &buffers
->dwBufferLength
, sizeof(buffers
->dwBufferLength
));
2453 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2454 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2456 EnterCriticalSection( &req
->read_section
);
2459 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2460 hdr
->dwError
= ERROR_SUCCESS
;
2462 error
= hdr
->dwError
;
2464 LeaveCriticalSection( &req
->read_section
);
2465 size
= buffers
->dwBufferLength
;
2466 buffers
->dwBufferLength
= read
;
2469 if (res
== ERROR_SUCCESS
) {
2470 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2471 &size
, sizeof(size
));
2474 return res
==ERROR_SUCCESS
? error
: res
;
2477 static DWORD
HTTPREQ_WriteFile(object_header_t
*hdr
, const void *buffer
, DWORD size
, DWORD
*written
)
2480 http_request_t
*lpwhr
= (http_request_t
*)hdr
;
2482 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
2485 res
= NETCON_send(&lpwhr
->netConnection
, buffer
, size
, 0, (LPINT
)written
);
2486 if (res
== ERROR_SUCCESS
)
2487 lpwhr
->dwBytesWritten
+= *written
;
2489 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_SENT
, written
, sizeof(DWORD
));
2493 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST
*workRequest
)
2495 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2497 HTTP_ReceiveRequestData(req
, FALSE
);
2500 static DWORD
HTTPREQ_QueryDataAvailable(object_header_t
*hdr
, DWORD
*available
, DWORD flags
, DWORD_PTR ctx
)
2502 http_request_t
*req
= (http_request_t
*)hdr
;
2504 TRACE("(%p %p %x %lx)\n", req
, available
, flags
, ctx
);
2506 if (req
->lpHttpSession
->lpAppInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
2508 WORKREQUEST workRequest
;
2510 /* never wait, if we can't enter the section we queue an async request right away */
2511 if (TryEnterCriticalSection( &req
->read_section
))
2513 if ((*available
= get_avail_data( req
))) goto done
;
2514 if (end_of_read_data( req
)) goto done
;
2515 LeaveCriticalSection( &req
->read_section
);
2518 workRequest
.asyncproc
= HTTPREQ_AsyncQueryDataAvailableProc
;
2519 workRequest
.hdr
= WININET_AddRef( &req
->hdr
);
2521 INTERNET_AsyncCall(&workRequest
);
2523 return ERROR_IO_PENDING
;
2526 EnterCriticalSection( &req
->read_section
);
2528 if (!(*available
= get_avail_data( req
)) && !end_of_read_data( req
))
2530 refill_buffer( req
);
2531 *available
= get_avail_data( req
);
2535 if (*available
== sizeof(req
->read_buf
) && !req
->gzip_stream
) /* check if we have even more pending in the socket */
2538 if (NETCON_query_data_available(&req
->netConnection
, &extra
))
2539 *available
= min( *available
+ extra
, req
->dwContentLength
- req
->dwContentRead
);
2541 LeaveCriticalSection( &req
->read_section
);
2543 TRACE( "returning %u\n", *available
);
2544 return ERROR_SUCCESS
;
2547 static const object_vtbl_t HTTPREQVtbl
= {
2549 HTTPREQ_CloseConnection
,
2550 HTTPREQ_QueryOption
,
2553 HTTPREQ_ReadFileExA
,
2554 HTTPREQ_ReadFileExW
,
2556 HTTPREQ_QueryDataAvailable
,
2560 /***********************************************************************
2561 * HTTP_HttpOpenRequestW (internal)
2563 * Open a HTTP request handle
2566 * HINTERNET a HTTP request handle on success
2570 static DWORD
HTTP_HttpOpenRequestW(http_session_t
*lpwhs
,
2571 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
2572 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
2573 DWORD dwFlags
, DWORD_PTR dwContext
, HINTERNET
*ret
)
2575 appinfo_t
*hIC
= NULL
;
2576 http_request_t
*lpwhr
;
2577 LPWSTR lpszHostName
= NULL
;
2578 HINTERNET handle
= NULL
;
2579 static const WCHAR szHostForm
[] = {'%','s',':','%','u',0};
2584 assert( lpwhs
->hdr
.htype
== WH_HHTTPSESSION
);
2585 hIC
= lpwhs
->lpAppInfo
;
2587 lpwhr
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(http_request_t
));
2590 res
= ERROR_OUTOFMEMORY
;
2593 lpwhr
->hdr
.htype
= WH_HHTTPREQ
;
2594 lpwhr
->hdr
.vtbl
= &HTTPREQVtbl
;
2595 lpwhr
->hdr
.dwFlags
= dwFlags
;
2596 lpwhr
->hdr
.dwContext
= dwContext
;
2597 lpwhr
->hdr
.refs
= 1;
2598 lpwhr
->hdr
.lpfnStatusCB
= lpwhs
->hdr
.lpfnStatusCB
;
2599 lpwhr
->hdr
.dwInternalFlags
= lpwhs
->hdr
.dwInternalFlags
& INET_CALLBACKW
;
2600 lpwhr
->dwContentLength
= ~0u;
2601 InitializeCriticalSection( &lpwhr
->read_section
);
2603 WININET_AddRef( &lpwhs
->hdr
);
2604 lpwhr
->lpHttpSession
= lpwhs
;
2605 list_add_head( &lpwhs
->hdr
.children
, &lpwhr
->hdr
.entry
);
2607 lpszHostName
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
) *
2608 (strlenW(lpwhs
->lpszHostName
) + 7 /* length of ":65535" + 1 */));
2609 if (NULL
== lpszHostName
)
2611 res
= ERROR_OUTOFMEMORY
;
2615 handle
= WININET_AllocHandle( &lpwhr
->hdr
);
2618 res
= ERROR_OUTOFMEMORY
;
2622 if ((res
= NETCON_init(&lpwhr
->netConnection
, dwFlags
& INTERNET_FLAG_SECURE
)) != ERROR_SUCCESS
)
2624 InternetCloseHandle( handle
);
2629 if (lpszObjectName
&& *lpszObjectName
) {
2633 rc
= UrlEscapeW(lpszObjectName
, NULL
, &len
, URL_ESCAPE_SPACES_ONLY
);
2634 if (rc
!= E_POINTER
)
2635 len
= strlenW(lpszObjectName
)+1;
2636 lpwhr
->lpszPath
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
2637 rc
= UrlEscapeW(lpszObjectName
, lpwhr
->lpszPath
, &len
,
2638 URL_ESCAPE_SPACES_ONLY
);
2641 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName
),rc
);
2642 strcpyW(lpwhr
->lpszPath
,lpszObjectName
);
2645 static const WCHAR slashW
[] = {'/',0};
2647 lpwhr
->lpszPath
= heap_strdupW(slashW
);
2650 if (lpszReferrer
&& *lpszReferrer
)
2651 HTTP_ProcessHeader(lpwhr
, HTTP_REFERER
, lpszReferrer
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2653 if (lpszAcceptTypes
)
2656 for (i
= 0; lpszAcceptTypes
[i
]; i
++)
2658 if (!*lpszAcceptTypes
[i
]) continue;
2659 HTTP_ProcessHeader(lpwhr
, HTTP_ACCEPT
, lpszAcceptTypes
[i
],
2660 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
|
2661 HTTP_ADDHDR_FLAG_REQ
|
2662 (i
== 0 ? HTTP_ADDHDR_FLAG_REPLACE
: 0));
2666 lpwhr
->lpszVerb
= heap_strdupW(lpszVerb
&& *lpszVerb
? lpszVerb
: szGET
);
2667 lpwhr
->lpszVersion
= heap_strdupW(lpszVersion
? lpszVersion
: g_szHttp1_1
);
2669 if (lpwhs
->nHostPort
!= INTERNET_INVALID_PORT_NUMBER
&&
2670 lpwhs
->nHostPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
2671 lpwhs
->nHostPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
2673 sprintfW(lpszHostName
, szHostForm
, lpwhs
->lpszHostName
, lpwhs
->nHostPort
);
2674 HTTP_ProcessHeader(lpwhr
, hostW
, lpszHostName
,
2675 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2678 HTTP_ProcessHeader(lpwhr
, hostW
, lpwhs
->lpszHostName
,
2679 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2681 if (lpwhs
->nServerPort
== INTERNET_INVALID_PORT_NUMBER
)
2682 lpwhs
->nServerPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
2683 INTERNET_DEFAULT_HTTPS_PORT
:
2684 INTERNET_DEFAULT_HTTP_PORT
);
2686 if (lpwhs
->nHostPort
== INTERNET_INVALID_PORT_NUMBER
)
2687 lpwhs
->nHostPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
2688 INTERNET_DEFAULT_HTTPS_PORT
:
2689 INTERNET_DEFAULT_HTTP_PORT
);
2691 if (NULL
!= hIC
->lpszProxy
&& hIC
->lpszProxy
[0] != 0)
2692 HTTP_DealWithProxy( hIC
, lpwhs
, lpwhr
);
2694 INTERNET_SendCallback(&lpwhs
->hdr
, dwContext
,
2695 INTERNET_STATUS_HANDLE_CREATED
, &handle
,
2699 HeapFree(GetProcessHeap(), 0, lpszHostName
);
2701 WININET_Release( &lpwhr
->hdr
);
2703 TRACE("<-- %p (%p)\n", handle
, lpwhr
);
2708 /***********************************************************************
2709 * HttpOpenRequestW (WININET.@)
2711 * Open a HTTP request handle
2714 * HINTERNET a HTTP request handle on success
2718 HINTERNET WINAPI
HttpOpenRequestW(HINTERNET hHttpSession
,
2719 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
2720 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
2721 DWORD dwFlags
, DWORD_PTR dwContext
)
2723 http_session_t
*lpwhs
;
2724 HINTERNET handle
= NULL
;
2727 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
2728 debugstr_w(lpszVerb
), debugstr_w(lpszObjectName
),
2729 debugstr_w(lpszVersion
), debugstr_w(lpszReferrer
), lpszAcceptTypes
,
2730 dwFlags
, dwContext
);
2731 if(lpszAcceptTypes
!=NULL
)
2734 for(i
=0;lpszAcceptTypes
[i
]!=NULL
;i
++)
2735 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes
[i
]));
2738 lpwhs
= (http_session_t
*) WININET_GetObject( hHttpSession
);
2739 if (NULL
== lpwhs
|| lpwhs
->hdr
.htype
!= WH_HHTTPSESSION
)
2741 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
2746 * My tests seem to show that the windows version does not
2747 * become asynchronous until after this point. And anyhow
2748 * if this call was asynchronous then how would you get the
2749 * necessary HINTERNET pointer returned by this function.
2752 res
= HTTP_HttpOpenRequestW(lpwhs
, lpszVerb
, lpszObjectName
,
2753 lpszVersion
, lpszReferrer
, lpszAcceptTypes
,
2754 dwFlags
, dwContext
, &handle
);
2757 WININET_Release( &lpwhs
->hdr
);
2758 TRACE("returning %p\n", handle
);
2759 if(res
!= ERROR_SUCCESS
)
2764 /* read any content returned by the server so that the connection can be
2766 static void HTTP_DrainContent(http_request_t
*req
)
2770 if (!NETCON_connected(&req
->netConnection
)) return;
2772 if (req
->dwContentLength
== -1)
2774 NETCON_close(&req
->netConnection
);
2777 if (!strcmpW(req
->lpszVerb
, szHEAD
)) return;
2782 if (HTTPREQ_Read(req
, buffer
, sizeof(buffer
), &bytes_read
, TRUE
) != ERROR_SUCCESS
)
2784 } while (bytes_read
);
2787 static const LPCWSTR header_lookup
[] = {
2788 szMime_Version
, /* HTTP_QUERY_MIME_VERSION = 0 */
2789 szContent_Type
, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2790 szContent_Transfer_Encoding
,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2791 szContent_ID
, /* HTTP_QUERY_CONTENT_ID = 3 */
2792 NULL
, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2793 szContent_Length
, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2794 szContent_Language
, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2795 szAllow
, /* HTTP_QUERY_ALLOW = 7 */
2796 szPublic
, /* HTTP_QUERY_PUBLIC = 8 */
2797 szDate
, /* HTTP_QUERY_DATE = 9 */
2798 szExpires
, /* HTTP_QUERY_EXPIRES = 10 */
2799 szLast_Modified
, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2800 NULL
, /* HTTP_QUERY_MESSAGE_ID = 12 */
2801 szURI
, /* HTTP_QUERY_URI = 13 */
2802 szFrom
, /* HTTP_QUERY_DERIVED_FROM = 14 */
2803 NULL
, /* HTTP_QUERY_COST = 15 */
2804 NULL
, /* HTTP_QUERY_LINK = 16 */
2805 szPragma
, /* HTTP_QUERY_PRAGMA = 17 */
2806 NULL
, /* HTTP_QUERY_VERSION = 18 */
2807 szStatus
, /* HTTP_QUERY_STATUS_CODE = 19 */
2808 NULL
, /* HTTP_QUERY_STATUS_TEXT = 20 */
2809 NULL
, /* HTTP_QUERY_RAW_HEADERS = 21 */
2810 NULL
, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2811 szConnection
, /* HTTP_QUERY_CONNECTION = 23 */
2812 szAccept
, /* HTTP_QUERY_ACCEPT = 24 */
2813 szAccept_Charset
, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2814 szAccept_Encoding
, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2815 szAccept_Language
, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2816 szAuthorization
, /* HTTP_QUERY_AUTHORIZATION = 28 */
2817 szContent_Encoding
, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2818 NULL
, /* HTTP_QUERY_FORWARDED = 30 */
2819 NULL
, /* HTTP_QUERY_FROM = 31 */
2820 szIf_Modified_Since
, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2821 szLocation
, /* HTTP_QUERY_LOCATION = 33 */
2822 NULL
, /* HTTP_QUERY_ORIG_URI = 34 */
2823 szReferer
, /* HTTP_QUERY_REFERER = 35 */
2824 szRetry_After
, /* HTTP_QUERY_RETRY_AFTER = 36 */
2825 szServer
, /* HTTP_QUERY_SERVER = 37 */
2826 NULL
, /* HTTP_TITLE = 38 */
2827 szUser_Agent
, /* HTTP_QUERY_USER_AGENT = 39 */
2828 szWWW_Authenticate
, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2829 szProxy_Authenticate
, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2830 szAccept_Ranges
, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2831 szSet_Cookie
, /* HTTP_QUERY_SET_COOKIE = 43 */
2832 szCookie
, /* HTTP_QUERY_COOKIE = 44 */
2833 NULL
, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2834 NULL
, /* HTTP_QUERY_REFRESH = 46 */
2835 NULL
, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2836 szAge
, /* HTTP_QUERY_AGE = 48 */
2837 szCache_Control
, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2838 szContent_Base
, /* HTTP_QUERY_CONTENT_BASE = 50 */
2839 szContent_Location
, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2840 szContent_MD5
, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2841 szContent_Range
, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2842 szETag
, /* HTTP_QUERY_ETAG = 54 */
2843 hostW
, /* HTTP_QUERY_HOST = 55 */
2844 szIf_Match
, /* HTTP_QUERY_IF_MATCH = 56 */
2845 szIf_None_Match
, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2846 szIf_Range
, /* HTTP_QUERY_IF_RANGE = 58 */
2847 szIf_Unmodified_Since
, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2848 szMax_Forwards
, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2849 szProxy_Authorization
, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2850 szRange
, /* HTTP_QUERY_RANGE = 62 */
2851 szTransfer_Encoding
, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2852 szUpgrade
, /* HTTP_QUERY_UPGRADE = 64 */
2853 szVary
, /* HTTP_QUERY_VARY = 65 */
2854 szVia
, /* HTTP_QUERY_VIA = 66 */
2855 szWarning
, /* HTTP_QUERY_WARNING = 67 */
2856 szExpect
, /* HTTP_QUERY_EXPECT = 68 */
2857 szProxy_Connection
, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2858 szUnless_Modified_Since
, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2861 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2863 /***********************************************************************
2864 * HTTP_HttpQueryInfoW (internal)
2866 static DWORD
HTTP_HttpQueryInfoW(http_request_t
*lpwhr
, DWORD dwInfoLevel
,
2867 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
2869 LPHTTPHEADERW lphttpHdr
= NULL
;
2870 BOOL request_only
= dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
;
2871 INT requested_index
= lpdwIndex
? *lpdwIndex
: 0;
2872 DWORD level
= (dwInfoLevel
& ~HTTP_QUERY_MODIFIER_FLAGS_MASK
);
2875 /* Find requested header structure */
2878 case HTTP_QUERY_CUSTOM
:
2879 if (!lpBuffer
) return ERROR_INVALID_PARAMETER
;
2880 index
= HTTP_GetCustomHeaderIndex(lpwhr
, lpBuffer
, requested_index
, request_only
);
2882 case HTTP_QUERY_RAW_HEADERS_CRLF
:
2886 DWORD res
= ERROR_INVALID_PARAMETER
;
2889 headers
= HTTP_BuildHeaderRequestString(lpwhr
, lpwhr
->lpszVerb
, lpwhr
->lpszPath
, lpwhr
->lpszVersion
);
2891 headers
= lpwhr
->lpszRawHeaders
;
2894 len
= strlenW(headers
) * sizeof(WCHAR
);
2896 if (len
+ sizeof(WCHAR
) > *lpdwBufferLength
)
2898 len
+= sizeof(WCHAR
);
2899 res
= ERROR_INSUFFICIENT_BUFFER
;
2904 memcpy(lpBuffer
, headers
, len
+ sizeof(WCHAR
));
2907 len
= strlenW(szCrLf
) * sizeof(WCHAR
);
2908 memcpy(lpBuffer
, szCrLf
, sizeof(szCrLf
));
2910 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
/ sizeof(WCHAR
)));
2911 res
= ERROR_SUCCESS
;
2913 *lpdwBufferLength
= len
;
2916 HeapFree(GetProcessHeap(), 0, headers
);
2919 case HTTP_QUERY_RAW_HEADERS
:
2921 LPWSTR
* ppszRawHeaderLines
= HTTP_Tokenize(lpwhr
->lpszRawHeaders
, szCrLf
);
2923 LPWSTR pszString
= lpBuffer
;
2925 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
2926 size
+= strlenW(ppszRawHeaderLines
[i
]) + 1;
2928 if (size
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
2930 HTTP_FreeTokens(ppszRawHeaderLines
);
2931 *lpdwBufferLength
= (size
+ 1) * sizeof(WCHAR
);
2932 return ERROR_INSUFFICIENT_BUFFER
;
2936 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
2938 DWORD len
= strlenW(ppszRawHeaderLines
[i
]);
2939 memcpy(pszString
, ppszRawHeaderLines
[i
], (len
+1)*sizeof(WCHAR
));
2943 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, size
));
2945 *lpdwBufferLength
= size
* sizeof(WCHAR
);
2946 HTTP_FreeTokens(ppszRawHeaderLines
);
2948 return ERROR_SUCCESS
;
2950 case HTTP_QUERY_STATUS_TEXT
:
2951 if (lpwhr
->lpszStatusText
)
2953 DWORD len
= strlenW(lpwhr
->lpszStatusText
);
2954 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
2956 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
2957 return ERROR_INSUFFICIENT_BUFFER
;
2961 memcpy(lpBuffer
, lpwhr
->lpszStatusText
, (len
+ 1) * sizeof(WCHAR
));
2962 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
2964 *lpdwBufferLength
= len
* sizeof(WCHAR
);
2965 return ERROR_SUCCESS
;
2968 case HTTP_QUERY_VERSION
:
2969 if (lpwhr
->lpszVersion
)
2971 DWORD len
= strlenW(lpwhr
->lpszVersion
);
2972 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
2974 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
2975 return ERROR_INSUFFICIENT_BUFFER
;
2979 memcpy(lpBuffer
, lpwhr
->lpszVersion
, (len
+ 1) * sizeof(WCHAR
));
2980 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
2982 *lpdwBufferLength
= len
* sizeof(WCHAR
);
2983 return ERROR_SUCCESS
;
2986 case HTTP_QUERY_CONTENT_ENCODING
:
2987 index
= HTTP_GetCustomHeaderIndex(lpwhr
, header_lookup
[lpwhr
->gzip_stream
? HTTP_QUERY_CONTENT_TYPE
: level
],
2988 requested_index
,request_only
);
2991 assert (LAST_TABLE_HEADER
== (HTTP_QUERY_UNLESS_MODIFIED_SINCE
+ 1));
2993 if (level
< LAST_TABLE_HEADER
&& header_lookup
[level
])
2994 index
= HTTP_GetCustomHeaderIndex(lpwhr
, header_lookup
[level
],
2995 requested_index
,request_only
);
2999 lphttpHdr
= &lpwhr
->pCustHeaders
[index
];
3001 /* Ensure header satisfies requested attributes */
3003 ((dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
) &&
3004 (~lphttpHdr
->wFlags
& HDR_ISREQUEST
)))
3006 return ERROR_HTTP_HEADER_NOT_FOUND
;
3009 if (lpdwIndex
&& level
!= HTTP_QUERY_STATUS_CODE
) (*lpdwIndex
)++;
3011 /* coalesce value to requested type */
3012 if (dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
&& lpBuffer
)
3014 *(int *)lpBuffer
= atoiW(lphttpHdr
->lpszValue
);
3015 TRACE(" returning number: %d\n", *(int *)lpBuffer
);
3017 else if (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
&& lpBuffer
)
3023 tmpTime
= ConvertTimeString(lphttpHdr
->lpszValue
);
3025 tmpTM
= *gmtime(&tmpTime
);
3026 STHook
= (SYSTEMTIME
*)lpBuffer
;
3027 STHook
->wDay
= tmpTM
.tm_mday
;
3028 STHook
->wHour
= tmpTM
.tm_hour
;
3029 STHook
->wMilliseconds
= 0;
3030 STHook
->wMinute
= tmpTM
.tm_min
;
3031 STHook
->wDayOfWeek
= tmpTM
.tm_wday
;
3032 STHook
->wMonth
= tmpTM
.tm_mon
+ 1;
3033 STHook
->wSecond
= tmpTM
.tm_sec
;
3034 STHook
->wYear
= tmpTM
.tm_year
;
3036 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3037 STHook
->wYear
, STHook
->wMonth
, STHook
->wDay
, STHook
->wDayOfWeek
,
3038 STHook
->wHour
, STHook
->wMinute
, STHook
->wSecond
, STHook
->wMilliseconds
);
3040 else if (lphttpHdr
->lpszValue
)
3042 DWORD len
= (strlenW(lphttpHdr
->lpszValue
) + 1) * sizeof(WCHAR
);
3044 if (len
> *lpdwBufferLength
)
3046 *lpdwBufferLength
= len
;
3047 return ERROR_INSUFFICIENT_BUFFER
;
3051 memcpy(lpBuffer
, lphttpHdr
->lpszValue
, len
);
3052 TRACE("! returning string: %s\n", debugstr_w(lpBuffer
));
3054 *lpdwBufferLength
= len
- sizeof(WCHAR
);
3056 return ERROR_SUCCESS
;
3059 /***********************************************************************
3060 * HttpQueryInfoW (WININET.@)
3062 * Queries for information about an HTTP request
3069 BOOL WINAPI
HttpQueryInfoW(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3070 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3072 http_request_t
*lpwhr
;
3075 if (TRACE_ON(wininet
)) {
3076 #define FE(x) { x, #x }
3077 static const wininet_flag_info query_flags
[] = {
3078 FE(HTTP_QUERY_MIME_VERSION
),
3079 FE(HTTP_QUERY_CONTENT_TYPE
),
3080 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING
),
3081 FE(HTTP_QUERY_CONTENT_ID
),
3082 FE(HTTP_QUERY_CONTENT_DESCRIPTION
),
3083 FE(HTTP_QUERY_CONTENT_LENGTH
),
3084 FE(HTTP_QUERY_CONTENT_LANGUAGE
),
3085 FE(HTTP_QUERY_ALLOW
),
3086 FE(HTTP_QUERY_PUBLIC
),
3087 FE(HTTP_QUERY_DATE
),
3088 FE(HTTP_QUERY_EXPIRES
),
3089 FE(HTTP_QUERY_LAST_MODIFIED
),
3090 FE(HTTP_QUERY_MESSAGE_ID
),
3092 FE(HTTP_QUERY_DERIVED_FROM
),
3093 FE(HTTP_QUERY_COST
),
3094 FE(HTTP_QUERY_LINK
),
3095 FE(HTTP_QUERY_PRAGMA
),
3096 FE(HTTP_QUERY_VERSION
),
3097 FE(HTTP_QUERY_STATUS_CODE
),
3098 FE(HTTP_QUERY_STATUS_TEXT
),
3099 FE(HTTP_QUERY_RAW_HEADERS
),
3100 FE(HTTP_QUERY_RAW_HEADERS_CRLF
),
3101 FE(HTTP_QUERY_CONNECTION
),
3102 FE(HTTP_QUERY_ACCEPT
),
3103 FE(HTTP_QUERY_ACCEPT_CHARSET
),
3104 FE(HTTP_QUERY_ACCEPT_ENCODING
),
3105 FE(HTTP_QUERY_ACCEPT_LANGUAGE
),
3106 FE(HTTP_QUERY_AUTHORIZATION
),
3107 FE(HTTP_QUERY_CONTENT_ENCODING
),
3108 FE(HTTP_QUERY_FORWARDED
),
3109 FE(HTTP_QUERY_FROM
),
3110 FE(HTTP_QUERY_IF_MODIFIED_SINCE
),
3111 FE(HTTP_QUERY_LOCATION
),
3112 FE(HTTP_QUERY_ORIG_URI
),
3113 FE(HTTP_QUERY_REFERER
),
3114 FE(HTTP_QUERY_RETRY_AFTER
),
3115 FE(HTTP_QUERY_SERVER
),
3116 FE(HTTP_QUERY_TITLE
),
3117 FE(HTTP_QUERY_USER_AGENT
),
3118 FE(HTTP_QUERY_WWW_AUTHENTICATE
),
3119 FE(HTTP_QUERY_PROXY_AUTHENTICATE
),
3120 FE(HTTP_QUERY_ACCEPT_RANGES
),
3121 FE(HTTP_QUERY_SET_COOKIE
),
3122 FE(HTTP_QUERY_COOKIE
),
3123 FE(HTTP_QUERY_REQUEST_METHOD
),
3124 FE(HTTP_QUERY_REFRESH
),
3125 FE(HTTP_QUERY_CONTENT_DISPOSITION
),
3127 FE(HTTP_QUERY_CACHE_CONTROL
),
3128 FE(HTTP_QUERY_CONTENT_BASE
),
3129 FE(HTTP_QUERY_CONTENT_LOCATION
),
3130 FE(HTTP_QUERY_CONTENT_MD5
),
3131 FE(HTTP_QUERY_CONTENT_RANGE
),
3132 FE(HTTP_QUERY_ETAG
),
3133 FE(HTTP_QUERY_HOST
),
3134 FE(HTTP_QUERY_IF_MATCH
),
3135 FE(HTTP_QUERY_IF_NONE_MATCH
),
3136 FE(HTTP_QUERY_IF_RANGE
),
3137 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE
),
3138 FE(HTTP_QUERY_MAX_FORWARDS
),
3139 FE(HTTP_QUERY_PROXY_AUTHORIZATION
),
3140 FE(HTTP_QUERY_RANGE
),
3141 FE(HTTP_QUERY_TRANSFER_ENCODING
),
3142 FE(HTTP_QUERY_UPGRADE
),
3143 FE(HTTP_QUERY_VARY
),
3145 FE(HTTP_QUERY_WARNING
),
3146 FE(HTTP_QUERY_CUSTOM
)
3148 static const wininet_flag_info modifier_flags
[] = {
3149 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS
),
3150 FE(HTTP_QUERY_FLAG_SYSTEMTIME
),
3151 FE(HTTP_QUERY_FLAG_NUMBER
),
3152 FE(HTTP_QUERY_FLAG_COALESCE
)
3155 DWORD info_mod
= dwInfoLevel
& HTTP_QUERY_MODIFIER_FLAGS_MASK
;
3156 DWORD info
= dwInfoLevel
& HTTP_QUERY_HEADER_MASK
;
3159 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest
, dwInfoLevel
, info
);
3160 TRACE(" Attribute:");
3161 for (i
= 0; i
< (sizeof(query_flags
) / sizeof(query_flags
[0])); i
++) {
3162 if (query_flags
[i
].val
== info
) {
3163 TRACE(" %s", query_flags
[i
].name
);
3167 if (i
== (sizeof(query_flags
) / sizeof(query_flags
[0]))) {
3168 TRACE(" Unknown (%08x)", info
);
3171 TRACE(" Modifier:");
3172 for (i
= 0; i
< (sizeof(modifier_flags
) / sizeof(modifier_flags
[0])); i
++) {
3173 if (modifier_flags
[i
].val
& info_mod
) {
3174 TRACE(" %s", modifier_flags
[i
].name
);
3175 info_mod
&= ~ modifier_flags
[i
].val
;
3180 TRACE(" Unknown (%08x)", info_mod
);
3185 lpwhr
= (http_request_t
*) WININET_GetObject( hHttpRequest
);
3186 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
3188 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
3192 if (lpBuffer
== NULL
)
3193 *lpdwBufferLength
= 0;
3194 res
= HTTP_HttpQueryInfoW( lpwhr
, dwInfoLevel
,
3195 lpBuffer
, lpdwBufferLength
, lpdwIndex
);
3199 WININET_Release( &lpwhr
->hdr
);
3201 TRACE("%u <--\n", res
);
3202 if(res
!= ERROR_SUCCESS
)
3204 return res
== ERROR_SUCCESS
;
3207 /***********************************************************************
3208 * HttpQueryInfoA (WININET.@)
3210 * Queries for information about an HTTP request
3217 BOOL WINAPI
HttpQueryInfoA(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3218 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3224 if((dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
) ||
3225 (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
))
3227 return HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, lpBuffer
,
3228 lpdwBufferLength
, lpdwIndex
);
3234 len
= (*lpdwBufferLength
)*sizeof(WCHAR
);
3235 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3237 alloclen
= MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, NULL
, 0 ) * sizeof(WCHAR
);
3243 bufferW
= HeapAlloc( GetProcessHeap(), 0, alloclen
);
3244 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3245 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3246 MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, bufferW
, alloclen
/ sizeof(WCHAR
) );
3253 result
= HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, bufferW
,
3257 len
= WideCharToMultiByte( CP_ACP
,0, bufferW
, len
/ sizeof(WCHAR
) + 1,
3258 lpBuffer
, *lpdwBufferLength
, NULL
, NULL
);
3259 *lpdwBufferLength
= len
- 1;
3261 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer
));
3264 /* since the strings being returned from HttpQueryInfoW should be
3265 * only ASCII characters, it is reasonable to assume that all of
3266 * the Unicode characters can be reduced to a single byte */
3267 *lpdwBufferLength
= len
/ sizeof(WCHAR
);
3269 HeapFree(GetProcessHeap(), 0, bufferW
);
3274 /***********************************************************************
3275 * HTTP_GetRedirectURL (internal)
3277 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*lpwhr
, LPCWSTR lpszUrl
)
3279 static WCHAR szHttp
[] = {'h','t','t','p',0};
3280 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3281 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
3282 URL_COMPONENTSW urlComponents
;
3283 DWORD url_length
= 0;
3285 LPWSTR combined_url
;
3287 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3288 urlComponents
.lpszScheme
= (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) ? szHttps
: szHttp
;
3289 urlComponents
.dwSchemeLength
= 0;
3290 urlComponents
.lpszHostName
= lpwhs
->lpszHostName
;
3291 urlComponents
.dwHostNameLength
= 0;
3292 urlComponents
.nPort
= lpwhs
->nHostPort
;
3293 urlComponents
.lpszUserName
= lpwhs
->lpszUserName
;
3294 urlComponents
.dwUserNameLength
= 0;
3295 urlComponents
.lpszPassword
= NULL
;
3296 urlComponents
.dwPasswordLength
= 0;
3297 urlComponents
.lpszUrlPath
= lpwhr
->lpszPath
;
3298 urlComponents
.dwUrlPathLength
= 0;
3299 urlComponents
.lpszExtraInfo
= NULL
;
3300 urlComponents
.dwExtraInfoLength
= 0;
3302 if (!InternetCreateUrlW(&urlComponents
, 0, NULL
, &url_length
) &&
3303 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3306 orig_url
= HeapAlloc(GetProcessHeap(), 0, url_length
);
3308 /* convert from bytes to characters */
3309 url_length
= url_length
/ sizeof(WCHAR
) - 1;
3310 if (!InternetCreateUrlW(&urlComponents
, 0, orig_url
, &url_length
))
3312 HeapFree(GetProcessHeap(), 0, orig_url
);
3317 if (!InternetCombineUrlW(orig_url
, lpszUrl
, NULL
, &url_length
, ICU_ENCODE_SPACES_ONLY
) &&
3318 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3320 HeapFree(GetProcessHeap(), 0, orig_url
);
3323 combined_url
= HeapAlloc(GetProcessHeap(), 0, url_length
* sizeof(WCHAR
));
3325 if (!InternetCombineUrlW(orig_url
, lpszUrl
, combined_url
, &url_length
, ICU_ENCODE_SPACES_ONLY
))
3327 HeapFree(GetProcessHeap(), 0, orig_url
);
3328 HeapFree(GetProcessHeap(), 0, combined_url
);
3331 HeapFree(GetProcessHeap(), 0, orig_url
);
3332 return combined_url
;
3336 /***********************************************************************
3337 * HTTP_HandleRedirect (internal)
3339 static DWORD
HTTP_HandleRedirect(http_request_t
*lpwhr
, LPCWSTR lpszUrl
)
3341 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
3342 appinfo_t
*hIC
= lpwhs
->lpAppInfo
;
3343 BOOL using_proxy
= hIC
->lpszProxy
&& hIC
->lpszProxy
[0];
3344 WCHAR path
[INTERNET_MAX_URL_LENGTH
];
3349 /* if it's an absolute path, keep the same session info */
3350 lstrcpynW(path
, lpszUrl
, INTERNET_MAX_URL_LENGTH
);
3354 URL_COMPONENTSW urlComponents
;
3355 WCHAR protocol
[32], hostName
[MAXHOSTNAME
], userName
[1024];
3356 static WCHAR szHttp
[] = {'h','t','t','p',0};
3357 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3363 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3364 urlComponents
.lpszScheme
= protocol
;
3365 urlComponents
.dwSchemeLength
= 32;
3366 urlComponents
.lpszHostName
= hostName
;
3367 urlComponents
.dwHostNameLength
= MAXHOSTNAME
;
3368 urlComponents
.lpszUserName
= userName
;
3369 urlComponents
.dwUserNameLength
= 1024;
3370 urlComponents
.lpszPassword
= NULL
;
3371 urlComponents
.dwPasswordLength
= 0;
3372 urlComponents
.lpszUrlPath
= path
;
3373 urlComponents
.dwUrlPathLength
= 2048;
3374 urlComponents
.lpszExtraInfo
= NULL
;
3375 urlComponents
.dwExtraInfoLength
= 0;
3376 if(!InternetCrackUrlW(lpszUrl
, strlenW(lpszUrl
), 0, &urlComponents
))
3377 return INTERNET_GetLastError();
3379 if (!strncmpW(szHttp
, urlComponents
.lpszScheme
, strlenW(szHttp
)) &&
3380 (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
))
3382 TRACE("redirect from secure page to non-secure page\n");
3383 /* FIXME: warn about from secure redirect to non-secure page */
3384 lpwhr
->hdr
.dwFlags
&= ~INTERNET_FLAG_SECURE
;
3386 if (!strncmpW(szHttps
, urlComponents
.lpszScheme
, strlenW(szHttps
)) &&
3387 !(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
))
3389 TRACE("redirect from non-secure page to secure page\n");
3390 /* FIXME: notify about redirect to secure page */
3391 lpwhr
->hdr
.dwFlags
|= INTERNET_FLAG_SECURE
;
3394 if (urlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
3396 if (lstrlenW(protocol
)>4) /*https*/
3397 urlComponents
.nPort
= INTERNET_DEFAULT_HTTPS_PORT
;
3399 urlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
3404 * This upsets redirects to binary files on sourceforge.net
3405 * and gives an html page instead of the target file
3406 * Examination of the HTTP request sent by native wininet.dll
3407 * reveals that it doesn't send a referrer in that case.
3408 * Maybe there's a flag that enables this, or maybe a referrer
3409 * shouldn't be added in case of a redirect.
3412 /* consider the current host as the referrer */
3413 if (lpwhs
->lpszServerName
&& *lpwhs
->lpszServerName
)
3414 HTTP_ProcessHeader(lpwhr
, HTTP_REFERER
, lpwhs
->lpszServerName
,
3415 HTTP_ADDHDR_FLAG_REQ
|HTTP_ADDREQ_FLAG_REPLACE
|
3416 HTTP_ADDHDR_FLAG_ADD_IF_NEW
);
3419 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszHostName
);
3420 if (urlComponents
.nPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
3421 urlComponents
.nPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
3424 static const WCHAR fmt
[] = {'%','s',':','%','i',0};
3425 len
= lstrlenW(hostName
);
3426 len
+= 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3427 lpwhs
->lpszHostName
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
3428 sprintfW(lpwhs
->lpszHostName
, fmt
, hostName
, urlComponents
.nPort
);
3431 lpwhs
->lpszHostName
= heap_strdupW(hostName
);
3433 HTTP_ProcessHeader(lpwhr
, hostW
, lpwhs
->lpszHostName
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDREQ_FLAG_REPLACE
| HTTP_ADDHDR_FLAG_REQ
);
3435 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszUserName
);
3436 lpwhs
->lpszUserName
= NULL
;
3438 lpwhs
->lpszUserName
= heap_strdupW(userName
);
3442 if (strcmpiW(lpwhs
->lpszServerName
, hostName
) || lpwhs
->nServerPort
!= urlComponents
.nPort
)
3446 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszServerName
);
3447 lpwhs
->lpszServerName
= heap_strdupW(hostName
);
3448 lpwhs
->nServerPort
= urlComponents
.nPort
;
3450 NETCON_close(&lpwhr
->netConnection
);
3451 if ((res
= HTTP_ResolveName(lpwhr
)) != ERROR_SUCCESS
)
3454 res
= NETCON_init(&lpwhr
->netConnection
, lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
);
3455 if (res
!= ERROR_SUCCESS
)
3458 lpwhr
->read_pos
= lpwhr
->read_size
= 0;
3459 lpwhr
->read_chunked
= FALSE
;
3463 TRACE("Redirect through proxy\n");
3466 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszPath
);
3467 lpwhr
->lpszPath
=NULL
;
3473 rc
= UrlEscapeW(path
, NULL
, &needed
, URL_ESCAPE_SPACES_ONLY
);
3474 if (rc
!= E_POINTER
)
3475 needed
= strlenW(path
)+1;
3476 lpwhr
->lpszPath
= HeapAlloc(GetProcessHeap(), 0, needed
*sizeof(WCHAR
));
3477 rc
= UrlEscapeW(path
, lpwhr
->lpszPath
, &needed
,
3478 URL_ESCAPE_SPACES_ONLY
);
3481 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path
),rc
);
3482 strcpyW(lpwhr
->lpszPath
,path
);
3486 /* Remove custom content-type/length headers on redirects. */
3487 index
= HTTP_GetCustomHeaderIndex(lpwhr
, szContent_Type
, 0, TRUE
);
3489 HTTP_DeleteCustomHeader(lpwhr
, index
);
3490 index
= HTTP_GetCustomHeaderIndex(lpwhr
, szContent_Length
, 0, TRUE
);
3492 HTTP_DeleteCustomHeader(lpwhr
, index
);
3494 return ERROR_SUCCESS
;
3497 /***********************************************************************
3498 * HTTP_build_req (internal)
3500 * concatenate all the strings in the request together
3502 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
)
3507 for( t
= list
; *t
; t
++ )
3508 len
+= strlenW( *t
);
3511 str
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
3514 for( t
= list
; *t
; t
++ )
3520 static DWORD
HTTP_SecureProxyConnect(http_request_t
*lpwhr
)
3523 LPWSTR requestString
;
3529 static const WCHAR szConnect
[] = {'C','O','N','N','E','C','T',0};
3530 static const WCHAR szFormat
[] = {'%','s',':','%','d',0};
3531 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
3535 lpszPath
= HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs
->lpszHostName
) + 13)*sizeof(WCHAR
) );
3536 sprintfW( lpszPath
, szFormat
, lpwhs
->lpszHostName
, lpwhs
->nHostPort
);
3537 requestString
= HTTP_BuildHeaderRequestString( lpwhr
, szConnect
, lpszPath
, g_szHttp1_1
);
3538 HeapFree( GetProcessHeap(), 0, lpszPath
);
3540 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3541 NULL
, 0, NULL
, NULL
);
3542 len
--; /* the nul terminator isn't needed */
3543 ascii_req
= HeapAlloc( GetProcessHeap(), 0, len
);
3544 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3545 ascii_req
, len
, NULL
, NULL
);
3546 HeapFree( GetProcessHeap(), 0, requestString
);
3548 TRACE("full request -> %s\n", debugstr_an( ascii_req
, len
) );
3550 res
= NETCON_send( &lpwhr
->netConnection
, ascii_req
, len
, 0, &cnt
);
3551 HeapFree( GetProcessHeap(), 0, ascii_req
);
3552 if (res
!= ERROR_SUCCESS
)
3555 responseLen
= HTTP_GetResponseHeaders( lpwhr
, TRUE
);
3557 return ERROR_HTTP_INVALID_HEADER
;
3559 return ERROR_SUCCESS
;
3562 static void HTTP_InsertCookies(http_request_t
*lpwhr
)
3564 static const WCHAR szUrlForm
[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3565 LPWSTR lpszCookies
, lpszUrl
= NULL
;
3566 DWORD nCookieSize
, size
;
3567 LPHTTPHEADERW Host
= HTTP_GetHeader(lpwhr
, hostW
);
3569 size
= (strlenW(Host
->lpszValue
) + strlenW(szUrlForm
) + strlenW(lpwhr
->lpszPath
)) * sizeof(WCHAR
);
3570 if (!(lpszUrl
= HeapAlloc(GetProcessHeap(), 0, size
))) return;
3571 sprintfW( lpszUrl
, szUrlForm
, Host
->lpszValue
, lpwhr
->lpszPath
);
3573 if (InternetGetCookieW(lpszUrl
, NULL
, NULL
, &nCookieSize
))
3576 static const WCHAR szCookie
[] = {'C','o','o','k','i','e',':',' ',0};
3578 size
= sizeof(szCookie
) + nCookieSize
* sizeof(WCHAR
) + sizeof(szCrLf
);
3579 if ((lpszCookies
= HeapAlloc(GetProcessHeap(), 0, size
)))
3581 cnt
+= sprintfW(lpszCookies
, szCookie
);
3582 InternetGetCookieW(lpszUrl
, NULL
, lpszCookies
+ cnt
, &nCookieSize
);
3583 strcatW(lpszCookies
, szCrLf
);
3585 HTTP_HttpAddRequestHeadersW(lpwhr
, lpszCookies
, strlenW(lpszCookies
), HTTP_ADDREQ_FLAG_REPLACE
);
3586 HeapFree(GetProcessHeap(), 0, lpszCookies
);
3589 HeapFree(GetProcessHeap(), 0, lpszUrl
);
3592 /***********************************************************************
3593 * HTTP_HttpSendRequestW (internal)
3595 * Sends the specified request to the HTTP server
3602 static DWORD
HTTP_HttpSendRequestW(http_request_t
*lpwhr
, LPCWSTR lpszHeaders
,
3603 DWORD dwHeaderLength
, LPVOID lpOptional
, DWORD dwOptionalLength
,
3604 DWORD dwContentLength
, BOOL bEndRequest
)
3607 BOOL redirected
= FALSE
;
3608 LPWSTR requestString
= NULL
;
3611 INTERNET_ASYNC_RESULT iar
;
3612 static const WCHAR szPost
[] = { 'P','O','S','T',0 };
3613 static const WCHAR szContentLength
[] =
3614 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3615 WCHAR contentLengthStr
[sizeof szContentLength
/2 /* includes \r\n */ + 20 /* int */ ];
3618 TRACE("--> %p\n", lpwhr
);
3620 assert(lpwhr
->hdr
.htype
== WH_HHTTPREQ
);
3622 /* if the verb is NULL default to GET */
3623 if (!lpwhr
->lpszVerb
)
3624 lpwhr
->lpszVerb
= heap_strdupW(szGET
);
3626 if (dwContentLength
|| strcmpW(lpwhr
->lpszVerb
, szGET
))
3628 sprintfW(contentLengthStr
, szContentLength
, dwContentLength
);
3629 HTTP_HttpAddRequestHeadersW(lpwhr
, contentLengthStr
, -1L, HTTP_ADDREQ_FLAG_REPLACE
);
3630 lpwhr
->dwBytesToWrite
= dwContentLength
;
3632 if (lpwhr
->lpHttpSession
->lpAppInfo
->lpszAgent
)
3634 WCHAR
*agent_header
;
3635 static const WCHAR user_agent
[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3638 len
= strlenW(lpwhr
->lpHttpSession
->lpAppInfo
->lpszAgent
) + strlenW(user_agent
);
3639 agent_header
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
3640 sprintfW(agent_header
, user_agent
, lpwhr
->lpHttpSession
->lpAppInfo
->lpszAgent
);
3642 HTTP_HttpAddRequestHeadersW(lpwhr
, agent_header
, strlenW(agent_header
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
3643 HeapFree(GetProcessHeap(), 0, agent_header
);
3645 if (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_PRAGMA_NOCACHE
)
3647 static const WCHAR pragma_nocache
[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3648 HTTP_HttpAddRequestHeadersW(lpwhr
, pragma_nocache
, strlenW(pragma_nocache
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
3650 if ((lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_CACHE_WRITE
) && !strcmpW(lpwhr
->lpszVerb
, szPost
))
3652 static const WCHAR cache_control
[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3653 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3654 HTTP_HttpAddRequestHeadersW(lpwhr
, cache_control
, strlenW(cache_control
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
3660 BOOL reusing_connection
;
3665 /* like native, just in case the caller forgot to call InternetReadFile
3666 * for all the data */
3667 HTTP_DrainContent(lpwhr
);
3668 lpwhr
->dwContentRead
= 0;
3670 lpwhr
->dwContentLength
= ~0u;
3671 lpwhr
->dwBytesToWrite
= 0;
3674 if (TRACE_ON(wininet
))
3676 LPHTTPHEADERW Host
= HTTP_GetHeader(lpwhr
, hostW
);
3677 TRACE("Going to url %s %s\n", debugstr_w(Host
->lpszValue
), debugstr_w(lpwhr
->lpszPath
));
3681 if (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_KEEP_CONNECTION
)
3683 HTTP_ProcessHeader(lpwhr
, szConnection
, szKeepAlive
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
3685 HTTP_InsertAuthorization(lpwhr
, lpwhr
->pAuthInfo
, szAuthorization
);
3686 HTTP_InsertAuthorization(lpwhr
, lpwhr
->pProxyAuthInfo
, szProxy_Authorization
);
3688 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
))
3689 HTTP_InsertCookies(lpwhr
);
3691 /* add the headers the caller supplied */
3692 if( lpszHeaders
&& dwHeaderLength
)
3694 HTTP_HttpAddRequestHeadersW(lpwhr
, lpszHeaders
, dwHeaderLength
,
3695 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REPLACE
);
3698 if (lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxy
&& lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxy
[0])
3700 WCHAR
*url
= HTTP_BuildProxyRequestUrl(lpwhr
);
3701 requestString
= HTTP_BuildHeaderRequestString(lpwhr
, lpwhr
->lpszVerb
, url
, lpwhr
->lpszVersion
);
3702 HeapFree(GetProcessHeap(), 0, url
);
3705 requestString
= HTTP_BuildHeaderRequestString(lpwhr
, lpwhr
->lpszVerb
, lpwhr
->lpszPath
, lpwhr
->lpszVersion
);
3708 TRACE("Request header -> %s\n", debugstr_w(requestString
) );
3710 /* Send the request and store the results */
3711 if(NETCON_connected(&lpwhr
->netConnection
))
3712 reusing_connection
= TRUE
;
3714 reusing_connection
= FALSE
;
3716 if ((res
= HTTP_OpenConnection(lpwhr
)) != ERROR_SUCCESS
)
3719 /* send the request as ASCII, tack on the optional data */
3720 if (!lpOptional
|| redirected
)
3721 dwOptionalLength
= 0;
3722 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3723 NULL
, 0, NULL
, NULL
);
3724 ascii_req
= HeapAlloc( GetProcessHeap(), 0, len
+ dwOptionalLength
);
3725 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3726 ascii_req
, len
, NULL
, NULL
);
3728 memcpy( &ascii_req
[len
-1], lpOptional
, dwOptionalLength
);
3729 len
= (len
+ dwOptionalLength
- 1);
3731 TRACE("full request -> %s\n", debugstr_a(ascii_req
) );
3733 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3734 INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
3736 res
= NETCON_send(&lpwhr
->netConnection
, ascii_req
, len
, 0, &cnt
);
3737 HeapFree( GetProcessHeap(), 0, ascii_req
);
3739 lpwhr
->dwBytesWritten
= dwOptionalLength
;
3741 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3742 INTERNET_STATUS_REQUEST_SENT
,
3743 &len
, sizeof(DWORD
));
3750 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3751 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
3753 if (res
!= ERROR_SUCCESS
)
3756 responseLen
= HTTP_GetResponseHeaders(lpwhr
, TRUE
);
3757 /* FIXME: We should know that connection is closed before sending
3758 * headers. Otherwise wrong callbacks are executed */
3759 if(!responseLen
&& reusing_connection
) {
3760 TRACE("Connection closed by server, reconnecting\n");
3765 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3766 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
,
3769 HTTP_ProcessCookies(lpwhr
);
3771 if (!set_content_length( lpwhr
)) HTTP_FinishedReading(lpwhr
);
3773 dwBufferSize
= sizeof(dwStatusCode
);
3774 if (HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_STATUS_CODE
,
3775 &dwStatusCode
,&dwBufferSize
,NULL
) != ERROR_SUCCESS
)
3778 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
) && responseLen
)
3780 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
3781 dwBufferSize
=sizeof(szNewLocation
);
3782 if ((dwStatusCode
== HTTP_STATUS_REDIRECT
||
3783 dwStatusCode
== HTTP_STATUS_MOVED
||
3784 dwStatusCode
== HTTP_STATUS_REDIRECT_METHOD
) &&
3785 HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_LOCATION
,szNewLocation
,&dwBufferSize
,NULL
) == ERROR_SUCCESS
)
3787 if (strcmpW(lpwhr
->lpszVerb
, szGET
) && strcmpW(lpwhr
->lpszVerb
, szHEAD
))
3789 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVerb
);
3790 lpwhr
->lpszVerb
= heap_strdupW(szGET
);
3792 HTTP_DrainContent(lpwhr
);
3793 if ((new_url
= HTTP_GetRedirectURL( lpwhr
, szNewLocation
)))
3795 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
3796 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
3797 res
= HTTP_HandleRedirect(lpwhr
, new_url
);
3798 if (res
== ERROR_SUCCESS
)
3800 HeapFree(GetProcessHeap(), 0, requestString
);
3803 HeapFree( GetProcessHeap(), 0, new_url
);
3808 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTH
) && res
== ERROR_SUCCESS
)
3810 WCHAR szAuthValue
[2048];
3812 if (dwStatusCode
== HTTP_STATUS_DENIED
)
3814 LPHTTPHEADERW Host
= HTTP_GetHeader(lpwhr
, hostW
);
3816 while (HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_WWW_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
3818 if (HTTP_DoAuthorization(lpwhr
, szAuthValue
,
3820 lpwhr
->lpHttpSession
->lpszUserName
,
3821 lpwhr
->lpHttpSession
->lpszPassword
,
3824 HeapFree(GetProcessHeap(), 0, requestString
);
3831 TRACE("Cleaning wrong authorization data\n");
3832 destroy_authinfo(lpwhr
->pAuthInfo
);
3833 lpwhr
->pAuthInfo
= NULL
;
3836 if (dwStatusCode
== HTTP_STATUS_PROXY_AUTH_REQ
)
3839 while (HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_PROXY_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
3841 if (HTTP_DoAuthorization(lpwhr
, szAuthValue
,
3842 &lpwhr
->pProxyAuthInfo
,
3843 lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxyUsername
,
3844 lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxyPassword
,
3853 TRACE("Cleaning wrong proxy authorization data\n");
3854 destroy_authinfo(lpwhr
->pProxyAuthInfo
);
3855 lpwhr
->pProxyAuthInfo
= NULL
;
3861 res
= ERROR_SUCCESS
;
3865 if(res
== ERROR_SUCCESS
) {
3866 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
3867 WCHAR cacheFileName
[MAX_PATH
+1];
3870 b
= HTTP_GetRequestURL(lpwhr
, url
);
3872 WARN("Could not get URL\n");
3876 b
= CreateUrlCacheEntryW(url
, lpwhr
->dwContentLength
> 0 ? lpwhr
->dwContentLength
: 0, NULL
, cacheFileName
, 0);
3878 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszCacheFile
);
3879 CloseHandle(lpwhr
->hCacheFile
);
3881 lpwhr
->lpszCacheFile
= heap_strdupW(cacheFileName
);
3882 lpwhr
->hCacheFile
= CreateFileW(lpwhr
->lpszCacheFile
, GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
3883 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
3884 if(lpwhr
->hCacheFile
== INVALID_HANDLE_VALUE
) {
3885 WARN("Could not create file: %u\n", GetLastError());
3886 lpwhr
->hCacheFile
= NULL
;
3889 WARN("Could not create cache entry: %08x\n", GetLastError());
3895 HeapFree(GetProcessHeap(), 0, requestString
);
3897 /* TODO: send notification for P3P header */
3899 if (lpwhr
->lpHttpSession
->lpAppInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
3901 if (res
== ERROR_SUCCESS
&& lpwhr
->dwBytesWritten
== lpwhr
->dwBytesToWrite
)
3902 HTTP_ReceiveRequestData(lpwhr
, TRUE
);
3905 iar
.dwResult
= (res
==ERROR_SUCCESS
? (DWORD_PTR
)lpwhr
->hdr
.hInternet
: 0);
3908 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3909 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
3910 sizeof(INTERNET_ASYNC_RESULT
));
3918 /***********************************************************************
3920 * Helper functions for the HttpSendRequest(Ex) functions
3923 static void AsyncHttpSendRequestProc(WORKREQUEST
*workRequest
)
3925 struct WORKREQ_HTTPSENDREQUESTW
const *req
= &workRequest
->u
.HttpSendRequestW
;
3926 http_request_t
*lpwhr
= (http_request_t
*) workRequest
->hdr
;
3928 TRACE("%p\n", lpwhr
);
3930 HTTP_HttpSendRequestW(lpwhr
, req
->lpszHeader
,
3931 req
->dwHeaderLength
, req
->lpOptional
, req
->dwOptionalLength
,
3932 req
->dwContentLength
, req
->bEndRequest
);
3934 HeapFree(GetProcessHeap(), 0, req
->lpszHeader
);
3938 static DWORD
HTTP_HttpEndRequestW(http_request_t
*lpwhr
, DWORD dwFlags
, DWORD_PTR dwContext
)
3942 INTERNET_ASYNC_RESULT iar
;
3943 DWORD res
= ERROR_SUCCESS
;
3945 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3946 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
3948 responseLen
= HTTP_GetResponseHeaders(lpwhr
, TRUE
);
3950 res
= ERROR_HTTP_HEADER_NOT_FOUND
;
3952 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3953 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
, sizeof(DWORD
));
3955 /* process cookies here. Is this right? */
3956 HTTP_ProcessCookies(lpwhr
);
3958 if (!set_content_length( lpwhr
)) HTTP_FinishedReading(lpwhr
);
3960 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
))
3962 DWORD dwCode
,dwCodeLength
= sizeof(DWORD
);
3963 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_STATUS_CODE
, &dwCode
, &dwCodeLength
, NULL
) == ERROR_SUCCESS
3964 && (dwCode
== 302 || dwCode
== 301 || dwCode
== 303))
3966 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
3967 dwBufferSize
=sizeof(szNewLocation
);
3968 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_LOCATION
, szNewLocation
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
)
3970 if (strcmpW(lpwhr
->lpszVerb
, szGET
) && strcmpW(lpwhr
->lpszVerb
, szHEAD
))
3972 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVerb
);
3973 lpwhr
->lpszVerb
= heap_strdupW(szGET
);
3975 HTTP_DrainContent(lpwhr
);
3976 if ((new_url
= HTTP_GetRedirectURL( lpwhr
, szNewLocation
)))
3978 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
3979 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
3980 res
= HTTP_HandleRedirect(lpwhr
, new_url
);
3981 if (res
== ERROR_SUCCESS
)
3982 res
= HTTP_HttpSendRequestW(lpwhr
, NULL
, 0, NULL
, 0, 0, TRUE
);
3983 HeapFree( GetProcessHeap(), 0, new_url
);
3989 iar
.dwResult
= (res
==ERROR_SUCCESS
? (DWORD_PTR
)lpwhr
->hdr
.hInternet
: 0);
3992 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3993 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
3994 sizeof(INTERNET_ASYNC_RESULT
));
3998 /***********************************************************************
3999 * HttpEndRequestA (WININET.@)
4001 * Ends an HTTP request that was started by HttpSendRequestEx
4004 * TRUE if successful
4008 BOOL WINAPI
HttpEndRequestA(HINTERNET hRequest
,
4009 LPINTERNET_BUFFERSA lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
4011 TRACE("(%p, %p, %08x, %08lx)\n", hRequest
, lpBuffersOut
, dwFlags
, dwContext
);
4015 SetLastError(ERROR_INVALID_PARAMETER
);
4019 return HttpEndRequestW(hRequest
, NULL
, dwFlags
, dwContext
);
4022 static void AsyncHttpEndRequestProc(WORKREQUEST
*work
)
4024 struct WORKREQ_HTTPENDREQUESTW
const *req
= &work
->u
.HttpEndRequestW
;
4025 http_request_t
*lpwhr
= (http_request_t
*)work
->hdr
;
4027 TRACE("%p\n", lpwhr
);
4029 HTTP_HttpEndRequestW(lpwhr
, req
->dwFlags
, req
->dwContext
);
4032 /***********************************************************************
4033 * HttpEndRequestW (WININET.@)
4035 * Ends an HTTP request that was started by HttpSendRequestEx
4038 * TRUE if successful
4042 BOOL WINAPI
HttpEndRequestW(HINTERNET hRequest
,
4043 LPINTERNET_BUFFERSW lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
4045 http_request_t
*lpwhr
;
4052 SetLastError(ERROR_INVALID_PARAMETER
);
4056 lpwhr
= (http_request_t
*) WININET_GetObject( hRequest
);
4058 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
4060 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
4062 WININET_Release( &lpwhr
->hdr
);
4065 lpwhr
->hdr
.dwFlags
|= dwFlags
;
4067 if (lpwhr
->lpHttpSession
->lpAppInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4070 struct WORKREQ_HTTPENDREQUESTW
*request
;
4072 work
.asyncproc
= AsyncHttpEndRequestProc
;
4073 work
.hdr
= WININET_AddRef( &lpwhr
->hdr
);
4075 request
= &work
.u
.HttpEndRequestW
;
4076 request
->dwFlags
= dwFlags
;
4077 request
->dwContext
= dwContext
;
4079 INTERNET_AsyncCall(&work
);
4080 res
= ERROR_IO_PENDING
;
4083 res
= HTTP_HttpEndRequestW(lpwhr
, dwFlags
, dwContext
);
4085 WININET_Release( &lpwhr
->hdr
);
4086 TRACE("%u <--\n", res
);
4087 if(res
!= ERROR_SUCCESS
)
4089 return res
== ERROR_SUCCESS
;
4092 /***********************************************************************
4093 * HttpSendRequestExA (WININET.@)
4095 * Sends the specified request to the HTTP server and allows chunked
4100 * Failure: FALSE, call GetLastError() for more information.
4102 BOOL WINAPI
HttpSendRequestExA(HINTERNET hRequest
,
4103 LPINTERNET_BUFFERSA lpBuffersIn
,
4104 LPINTERNET_BUFFERSA lpBuffersOut
,
4105 DWORD dwFlags
, DWORD_PTR dwContext
)
4107 INTERNET_BUFFERSW BuffersInW
;
4110 LPWSTR header
= NULL
;
4112 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
4113 lpBuffersOut
, dwFlags
, dwContext
);
4117 BuffersInW
.dwStructSize
= sizeof(LPINTERNET_BUFFERSW
);
4118 if (lpBuffersIn
->lpcszHeader
)
4120 headerlen
= MultiByteToWideChar(CP_ACP
,0,lpBuffersIn
->lpcszHeader
,
4121 lpBuffersIn
->dwHeadersLength
,0,0);
4122 header
= HeapAlloc(GetProcessHeap(),0,headerlen
*sizeof(WCHAR
));
4123 if (!(BuffersInW
.lpcszHeader
= header
))
4125 SetLastError(ERROR_OUTOFMEMORY
);
4128 BuffersInW
.dwHeadersLength
= MultiByteToWideChar(CP_ACP
, 0,
4129 lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
4133 BuffersInW
.lpcszHeader
= NULL
;
4134 BuffersInW
.dwHeadersTotal
= lpBuffersIn
->dwHeadersTotal
;
4135 BuffersInW
.lpvBuffer
= lpBuffersIn
->lpvBuffer
;
4136 BuffersInW
.dwBufferLength
= lpBuffersIn
->dwBufferLength
;
4137 BuffersInW
.dwBufferTotal
= lpBuffersIn
->dwBufferTotal
;
4138 BuffersInW
.Next
= NULL
;
4141 rc
= HttpSendRequestExW(hRequest
, lpBuffersIn
? &BuffersInW
: NULL
, NULL
, dwFlags
, dwContext
);
4143 HeapFree(GetProcessHeap(),0,header
);
4148 /***********************************************************************
4149 * HttpSendRequestExW (WININET.@)
4151 * Sends the specified request to the HTTP server and allows chunked
4156 * Failure: FALSE, call GetLastError() for more information.
4158 BOOL WINAPI
HttpSendRequestExW(HINTERNET hRequest
,
4159 LPINTERNET_BUFFERSW lpBuffersIn
,
4160 LPINTERNET_BUFFERSW lpBuffersOut
,
4161 DWORD dwFlags
, DWORD_PTR dwContext
)
4163 http_request_t
*lpwhr
;
4164 http_session_t
*lpwhs
;
4168 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
4169 lpBuffersOut
, dwFlags
, dwContext
);
4171 lpwhr
= (http_request_t
*) WININET_GetObject( hRequest
);
4173 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
4175 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
4179 lpwhs
= lpwhr
->lpHttpSession
;
4180 assert(lpwhs
->hdr
.htype
== WH_HHTTPSESSION
);
4181 hIC
= lpwhs
->lpAppInfo
;
4182 assert(hIC
->hdr
.htype
== WH_HINIT
);
4184 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4186 WORKREQUEST workRequest
;
4187 struct WORKREQ_HTTPSENDREQUESTW
*req
;
4189 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
4190 workRequest
.hdr
= WININET_AddRef( &lpwhr
->hdr
);
4191 req
= &workRequest
.u
.HttpSendRequestW
;
4196 if (lpBuffersIn
->lpcszHeader
)
4198 if (lpBuffersIn
->dwHeadersLength
== ~0u)
4199 size
= (strlenW( lpBuffersIn
->lpcszHeader
) + 1) * sizeof(WCHAR
);
4201 size
= lpBuffersIn
->dwHeadersLength
* sizeof(WCHAR
);
4203 req
->lpszHeader
= HeapAlloc( GetProcessHeap(), 0, size
);
4204 memcpy( req
->lpszHeader
, lpBuffersIn
->lpcszHeader
, size
);
4206 else req
->lpszHeader
= NULL
;
4208 req
->dwHeaderLength
= size
/ sizeof(WCHAR
);
4209 req
->lpOptional
= lpBuffersIn
->lpvBuffer
;
4210 req
->dwOptionalLength
= lpBuffersIn
->dwBufferLength
;
4211 req
->dwContentLength
= lpBuffersIn
->dwBufferTotal
;
4215 req
->lpszHeader
= NULL
;
4216 req
->dwHeaderLength
= 0;
4217 req
->lpOptional
= NULL
;
4218 req
->dwOptionalLength
= 0;
4219 req
->dwContentLength
= 0;
4222 req
->bEndRequest
= FALSE
;
4224 INTERNET_AsyncCall(&workRequest
);
4226 * This is from windows.
4228 res
= ERROR_IO_PENDING
;
4233 res
= HTTP_HttpSendRequestW(lpwhr
, lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
4234 lpBuffersIn
->lpvBuffer
, lpBuffersIn
->dwBufferLength
,
4235 lpBuffersIn
->dwBufferTotal
, FALSE
);
4237 res
= HTTP_HttpSendRequestW(lpwhr
, NULL
, 0, NULL
, 0, 0, FALSE
);
4242 WININET_Release( &lpwhr
->hdr
);
4246 return res
== ERROR_SUCCESS
;
4249 /***********************************************************************
4250 * HttpSendRequestW (WININET.@)
4252 * Sends the specified request to the HTTP server
4259 BOOL WINAPI
HttpSendRequestW(HINTERNET hHttpRequest
, LPCWSTR lpszHeaders
,
4260 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
4262 http_request_t
*lpwhr
;
4263 http_session_t
*lpwhs
= NULL
;
4264 appinfo_t
*hIC
= NULL
;
4265 DWORD res
= ERROR_SUCCESS
;
4267 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest
,
4268 debugstr_wn(lpszHeaders
, dwHeaderLength
), dwHeaderLength
, lpOptional
, dwOptionalLength
);
4270 lpwhr
= (http_request_t
*) WININET_GetObject( hHttpRequest
);
4271 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
4273 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
4277 lpwhs
= lpwhr
->lpHttpSession
;
4278 if (NULL
== lpwhs
|| lpwhs
->hdr
.htype
!= WH_HHTTPSESSION
)
4280 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
4284 hIC
= lpwhs
->lpAppInfo
;
4285 if (NULL
== hIC
|| hIC
->hdr
.htype
!= WH_HINIT
)
4287 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
4291 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4293 WORKREQUEST workRequest
;
4294 struct WORKREQ_HTTPSENDREQUESTW
*req
;
4296 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
4297 workRequest
.hdr
= WININET_AddRef( &lpwhr
->hdr
);
4298 req
= &workRequest
.u
.HttpSendRequestW
;
4303 if (dwHeaderLength
== ~0u) size
= (strlenW(lpszHeaders
) + 1) * sizeof(WCHAR
);
4304 else size
= dwHeaderLength
* sizeof(WCHAR
);
4306 req
->lpszHeader
= HeapAlloc(GetProcessHeap(), 0, size
);
4307 memcpy(req
->lpszHeader
, lpszHeaders
, size
);
4310 req
->lpszHeader
= 0;
4311 req
->dwHeaderLength
= dwHeaderLength
;
4312 req
->lpOptional
= lpOptional
;
4313 req
->dwOptionalLength
= dwOptionalLength
;
4314 req
->dwContentLength
= dwOptionalLength
;
4315 req
->bEndRequest
= TRUE
;
4317 INTERNET_AsyncCall(&workRequest
);
4319 * This is from windows.
4321 res
= ERROR_IO_PENDING
;
4325 res
= HTTP_HttpSendRequestW(lpwhr
, lpszHeaders
,
4326 dwHeaderLength
, lpOptional
, dwOptionalLength
,
4327 dwOptionalLength
, TRUE
);
4331 WININET_Release( &lpwhr
->hdr
);
4334 return res
== ERROR_SUCCESS
;
4337 /***********************************************************************
4338 * HttpSendRequestA (WININET.@)
4340 * Sends the specified request to the HTTP server
4347 BOOL WINAPI
HttpSendRequestA(HINTERNET hHttpRequest
, LPCSTR lpszHeaders
,
4348 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
4351 LPWSTR szHeaders
=NULL
;
4352 DWORD nLen
=dwHeaderLength
;
4353 if(lpszHeaders
!=NULL
)
4355 nLen
=MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,NULL
,0);
4356 szHeaders
=HeapAlloc(GetProcessHeap(),0,nLen
*sizeof(WCHAR
));
4357 MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,szHeaders
,nLen
);
4359 result
=HttpSendRequestW(hHttpRequest
, szHeaders
, nLen
, lpOptional
, dwOptionalLength
);
4360 HeapFree(GetProcessHeap(),0,szHeaders
);
4364 /***********************************************************************
4365 * HTTPSESSION_Destroy (internal)
4367 * Deallocate session handle
4370 static void HTTPSESSION_Destroy(object_header_t
*hdr
)
4372 http_session_t
*lpwhs
= (http_session_t
*) hdr
;
4374 TRACE("%p\n", lpwhs
);
4376 WININET_Release(&lpwhs
->lpAppInfo
->hdr
);
4378 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszHostName
);
4379 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszServerName
);
4380 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszPassword
);
4381 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszUserName
);
4382 HeapFree(GetProcessHeap(), 0, lpwhs
);
4385 static DWORD
HTTPSESSION_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
4388 case INTERNET_OPTION_HANDLE_TYPE
:
4389 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4391 if (*size
< sizeof(ULONG
))
4392 return ERROR_INSUFFICIENT_BUFFER
;
4394 *size
= sizeof(DWORD
);
4395 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_CONNECT_HTTP
;
4396 return ERROR_SUCCESS
;
4399 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
4402 static DWORD
HTTPSESSION_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
4404 http_session_t
*ses
= (http_session_t
*)hdr
;
4407 case INTERNET_OPTION_USERNAME
:
4409 HeapFree(GetProcessHeap(), 0, ses
->lpszUserName
);
4410 if (!(ses
->lpszUserName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
4411 return ERROR_SUCCESS
;
4413 case INTERNET_OPTION_PASSWORD
:
4415 HeapFree(GetProcessHeap(), 0, ses
->lpszPassword
);
4416 if (!(ses
->lpszPassword
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
4417 return ERROR_SUCCESS
;
4422 return ERROR_INTERNET_INVALID_OPTION
;
4425 static const object_vtbl_t HTTPSESSIONVtbl
= {
4426 HTTPSESSION_Destroy
,
4428 HTTPSESSION_QueryOption
,
4429 HTTPSESSION_SetOption
,
4438 /***********************************************************************
4439 * HTTP_Connect (internal)
4441 * Create http session handle
4444 * HINTERNET a session handle on success
4448 DWORD
HTTP_Connect(appinfo_t
*hIC
, LPCWSTR lpszServerName
,
4449 INTERNET_PORT nServerPort
, LPCWSTR lpszUserName
,
4450 LPCWSTR lpszPassword
, DWORD dwFlags
, DWORD_PTR dwContext
,
4451 DWORD dwInternalFlags
, HINTERNET
*ret
)
4453 http_session_t
*lpwhs
= NULL
;
4454 HINTERNET handle
= NULL
;
4455 DWORD res
= ERROR_SUCCESS
;
4459 if (!lpszServerName
|| !lpszServerName
[0])
4460 return ERROR_INVALID_PARAMETER
;
4462 assert( hIC
->hdr
.htype
== WH_HINIT
);
4464 lpwhs
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(http_session_t
));
4466 return ERROR_OUTOFMEMORY
;
4469 * According to my tests. The name is not resolved until a request is sent
4472 lpwhs
->hdr
.htype
= WH_HHTTPSESSION
;
4473 lpwhs
->hdr
.vtbl
= &HTTPSESSIONVtbl
;
4474 lpwhs
->hdr
.dwFlags
= dwFlags
;
4475 lpwhs
->hdr
.dwContext
= dwContext
;
4476 lpwhs
->hdr
.dwInternalFlags
= dwInternalFlags
| (hIC
->hdr
.dwInternalFlags
& INET_CALLBACKW
);
4477 lpwhs
->hdr
.refs
= 1;
4478 lpwhs
->hdr
.lpfnStatusCB
= hIC
->hdr
.lpfnStatusCB
;
4480 WININET_AddRef( &hIC
->hdr
);
4481 lpwhs
->lpAppInfo
= hIC
;
4482 list_add_head( &hIC
->hdr
.children
, &lpwhs
->hdr
.entry
);
4484 handle
= WININET_AllocHandle( &lpwhs
->hdr
);
4487 ERR("Failed to alloc handle\n");
4488 res
= ERROR_OUTOFMEMORY
;
4492 if(hIC
->lpszProxy
&& hIC
->dwAccessType
== INTERNET_OPEN_TYPE_PROXY
) {
4493 if(hIC
->lpszProxyBypass
)
4494 FIXME("Proxy bypass is ignored.\n");
4496 lpwhs
->lpszServerName
= heap_strdupW(lpszServerName
);
4497 lpwhs
->lpszHostName
= heap_strdupW(lpszServerName
);
4498 if (lpszUserName
&& lpszUserName
[0])
4499 lpwhs
->lpszUserName
= heap_strdupW(lpszUserName
);
4500 if (lpszPassword
&& lpszPassword
[0])
4501 lpwhs
->lpszPassword
= heap_strdupW(lpszPassword
);
4502 lpwhs
->nServerPort
= nServerPort
;
4503 lpwhs
->nHostPort
= nServerPort
;
4505 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4506 if (!(lpwhs
->hdr
.dwInternalFlags
& INET_OPENURL
))
4508 INTERNET_SendCallback(&hIC
->hdr
, dwContext
,
4509 INTERNET_STATUS_HANDLE_CREATED
, &handle
,
4515 WININET_Release( &lpwhs
->hdr
);
4518 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4522 TRACE("%p --> %p (%p)\n", hIC
, handle
, lpwhs
);
4524 if(res
== ERROR_SUCCESS
)
4530 /***********************************************************************
4531 * HTTP_OpenConnection (internal)
4533 * Connect to a web server
4540 static DWORD
HTTP_OpenConnection(http_request_t
*lpwhr
)
4542 http_session_t
*lpwhs
;
4543 appinfo_t
*hIC
= NULL
;
4544 char szaddr
[INET6_ADDRSTRLEN
];
4546 DWORD res
= ERROR_SUCCESS
;
4551 if (lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
4553 res
= ERROR_INVALID_PARAMETER
;
4557 if (NETCON_connected(&lpwhr
->netConnection
))
4559 if ((res
= HTTP_ResolveName(lpwhr
)) != ERROR_SUCCESS
) goto lend
;
4561 lpwhs
= lpwhr
->lpHttpSession
;
4563 hIC
= lpwhs
->lpAppInfo
;
4564 switch (lpwhs
->socketAddress
.ss_family
)
4567 addr
= &((struct sockaddr_in
*)&lpwhs
->socketAddress
)->sin_addr
;
4570 addr
= &((struct sockaddr_in6
*)&lpwhs
->socketAddress
)->sin6_addr
;
4573 WARN("unsupported family %d\n", lpwhs
->socketAddress
.ss_family
);
4574 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
4576 inet_ntop(lpwhs
->socketAddress
.ss_family
, addr
, szaddr
, sizeof(szaddr
));
4577 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
4578 INTERNET_STATUS_CONNECTING_TO_SERVER
,
4582 res
= NETCON_create(&lpwhr
->netConnection
, lpwhs
->socketAddress
.ss_family
, SOCK_STREAM
, 0);
4583 if (res
!= ERROR_SUCCESS
)
4585 WARN("Socket creation failed: %u\n", res
);
4589 res
= NETCON_connect(&lpwhr
->netConnection
, (struct sockaddr
*)&lpwhs
->socketAddress
,
4591 if(res
!= ERROR_SUCCESS
)
4594 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
4595 INTERNET_STATUS_CONNECTED_TO_SERVER
,
4596 szaddr
, strlen(szaddr
)+1);
4598 if (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
4600 /* Note: we differ from Microsoft's WinINet here. they seem to have
4601 * a bug that causes no status callbacks to be sent when starting
4602 * a tunnel to a proxy server using the CONNECT verb. i believe our
4603 * behaviour to be more correct and to not cause any incompatibilities
4604 * because using a secure connection through a proxy server is a rare
4605 * case that would be hard for anyone to depend on */
4606 if (hIC
->lpszProxy
&& (res
= HTTP_SecureProxyConnect(lpwhr
)) != ERROR_SUCCESS
) {
4607 HTTPREQ_CloseConnection(&lpwhr
->hdr
);
4611 res
= NETCON_secure_connect(&lpwhr
->netConnection
, lpwhs
->lpszHostName
);
4612 if(res
!= ERROR_SUCCESS
)
4614 WARN("Couldn't connect securely to host\n");
4616 if((lpwhr
->hdr
.ErrorMask
&INTERNET_ERROR_MASK_COMBINED_SEC_CERT
) && (
4617 res
== ERROR_INTERNET_SEC_CERT_DATE_INVALID
4618 || res
== ERROR_INTERNET_INVALID_CA
4619 || res
== ERROR_INTERNET_SEC_CERT_NO_REV
4620 || res
== ERROR_INTERNET_SEC_CERT_REV_FAILED
4621 || res
== ERROR_INTERNET_SEC_CERT_REVOKED
4622 || res
== ERROR_INTERNET_SEC_INVALID_CERT
4623 || res
== ERROR_INTERNET_SEC_CERT_CN_INVALID
))
4624 res
= ERROR_INTERNET_SEC_CERT_ERRORS
;
4626 HTTPREQ_CloseConnection(&lpwhr
->hdr
);
4633 lpwhr
->read_pos
= lpwhr
->read_size
= 0;
4634 lpwhr
->read_chunked
= FALSE
;
4636 TRACE("%d <--\n", res
);
4641 /***********************************************************************
4642 * HTTP_clear_response_headers (internal)
4644 * clear out any old response headers
4646 static void HTTP_clear_response_headers( http_request_t
*lpwhr
)
4650 for( i
=0; i
<lpwhr
->nCustHeaders
; i
++)
4652 if( !lpwhr
->pCustHeaders
[i
].lpszField
)
4654 if( !lpwhr
->pCustHeaders
[i
].lpszValue
)
4656 if ( lpwhr
->pCustHeaders
[i
].wFlags
& HDR_ISREQUEST
)
4658 HTTP_DeleteCustomHeader( lpwhr
, i
);
4663 /***********************************************************************
4664 * HTTP_GetResponseHeaders (internal)
4666 * Read server response
4673 static INT
HTTP_GetResponseHeaders(http_request_t
*lpwhr
, BOOL clear
)
4676 WCHAR buffer
[MAX_REPLY_LEN
];
4677 DWORD buflen
= MAX_REPLY_LEN
;
4678 BOOL bSuccess
= FALSE
;
4680 char bufferA
[MAX_REPLY_LEN
];
4681 LPWSTR status_code
= NULL
, status_text
= NULL
;
4682 DWORD cchMaxRawHeaders
= 1024;
4683 LPWSTR lpszRawHeaders
= NULL
;
4685 DWORD cchRawHeaders
= 0;
4686 BOOL codeHundred
= FALSE
;
4690 if (!NETCON_connected(&lpwhr
->netConnection
))
4694 static const WCHAR szHundred
[] = {'1','0','0',0};
4696 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4698 buflen
= MAX_REPLY_LEN
;
4699 if (!read_line(lpwhr
, bufferA
, &buflen
))
4702 /* clear old response headers (eg. from a redirect response) */
4704 HTTP_clear_response_headers( lpwhr
);
4709 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
4710 /* check is this a status code line? */
4711 if (!strncmpW(buffer
, g_szHttp1_0
, 4))
4713 /* split the version from the status code */
4714 status_code
= strchrW( buffer
, ' ' );
4719 /* split the status code from the status text */
4720 status_text
= strchrW( status_code
, ' ' );
4725 TRACE("version [%s] status code [%s] status text [%s]\n",
4726 debugstr_w(buffer
), debugstr_w(status_code
), debugstr_w(status_text
) );
4728 codeHundred
= (!strcmpW(status_code
, szHundred
));
4730 else if (!codeHundred
)
4732 WARN("No status line at head of response (%s)\n", debugstr_w(buffer
));
4734 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVersion
);
4735 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszStatusText
);
4737 lpwhr
->lpszVersion
= heap_strdupW(g_szHttp1_0
);
4738 lpwhr
->lpszStatusText
= heap_strdupW(szOK
);
4740 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszRawHeaders
);
4741 lpwhr
->lpszRawHeaders
= heap_strdupW(szDefaultHeader
);
4746 } while (codeHundred
);
4748 /* Add status code */
4749 HTTP_ProcessHeader(lpwhr
, szStatus
, status_code
,
4750 HTTP_ADDHDR_FLAG_REPLACE
);
4752 HeapFree(GetProcessHeap(),0,lpwhr
->lpszVersion
);
4753 HeapFree(GetProcessHeap(),0,lpwhr
->lpszStatusText
);
4755 lpwhr
->lpszVersion
= heap_strdupW(buffer
);
4756 lpwhr
->lpszStatusText
= heap_strdupW(status_text
);
4758 /* Restore the spaces */
4759 *(status_code
-1) = ' ';
4760 *(status_text
-1) = ' ';
4762 /* regenerate raw headers */
4763 lpszRawHeaders
= HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
4764 if (!lpszRawHeaders
) goto lend
;
4766 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
4767 cchMaxRawHeaders
*= 2;
4768 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
4769 if (temp
== NULL
) goto lend
;
4770 lpszRawHeaders
= temp
;
4771 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
4772 cchRawHeaders
+= (buflen
-1);
4773 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
4774 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
4775 lpszRawHeaders
[cchRawHeaders
] = '\0';
4777 /* Parse each response line */
4780 buflen
= MAX_REPLY_LEN
;
4781 if (read_line(lpwhr
, bufferA
, &buflen
))
4783 LPWSTR
* pFieldAndValue
;
4785 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA
));
4787 if (!bufferA
[0]) break;
4788 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
4790 pFieldAndValue
= HTTP_InterpretHttpHeader(buffer
);
4793 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
4794 cchMaxRawHeaders
*= 2;
4795 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
4796 if (temp
== NULL
) goto lend
;
4797 lpszRawHeaders
= temp
;
4798 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
4799 cchRawHeaders
+= (buflen
-1);
4800 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
4801 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
4802 lpszRawHeaders
[cchRawHeaders
] = '\0';
4804 HTTP_ProcessHeader(lpwhr
, pFieldAndValue
[0], pFieldAndValue
[1],
4805 HTTP_ADDREQ_FLAG_ADD
);
4807 HTTP_FreeTokens(pFieldAndValue
);
4818 /* make sure the response header is terminated with an empty line. Some apps really
4819 truly care about that empty line being there for some reason. Just add it to the
4821 if (cchRawHeaders
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
4823 cchMaxRawHeaders
= cchRawHeaders
+ strlenW(szCrLf
);
4824 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
4825 if (temp
== NULL
) goto lend
;
4826 lpszRawHeaders
= temp
;
4829 memcpy(&lpszRawHeaders
[cchRawHeaders
], szCrLf
, sizeof(szCrLf
));
4831 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszRawHeaders
);
4832 lpwhr
->lpszRawHeaders
= lpszRawHeaders
;
4833 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders
));
4843 HeapFree(GetProcessHeap(), 0, lpszRawHeaders
);
4848 /***********************************************************************
4849 * HTTP_InterpretHttpHeader (internal)
4851 * Parse server response
4855 * Pointer to array of field, value, NULL on success.
4858 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
)
4860 LPWSTR
* pTokenPair
;
4864 pTokenPair
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*pTokenPair
)*3);
4866 pszColon
= strchrW(buffer
, ':');
4867 /* must have two tokens */
4870 HTTP_FreeTokens(pTokenPair
);
4872 TRACE("No ':' in line: %s\n", debugstr_w(buffer
));
4876 pTokenPair
[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon
- buffer
+ 1) * sizeof(WCHAR
));
4879 HTTP_FreeTokens(pTokenPair
);
4882 memcpy(pTokenPair
[0], buffer
, (pszColon
- buffer
) * sizeof(WCHAR
));
4883 pTokenPair
[0][pszColon
- buffer
] = '\0';
4887 len
= strlenW(pszColon
);
4888 pTokenPair
[1] = HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
));
4891 HTTP_FreeTokens(pTokenPair
);
4894 memcpy(pTokenPair
[1], pszColon
, (len
+ 1) * sizeof(WCHAR
));
4896 strip_spaces(pTokenPair
[0]);
4897 strip_spaces(pTokenPair
[1]);
4899 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair
[0]), debugstr_w(pTokenPair
[1]));
4903 /***********************************************************************
4904 * HTTP_ProcessHeader (internal)
4906 * Stuff header into header tables according to <dwModifier>
4910 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4912 static DWORD
HTTP_ProcessHeader(http_request_t
*lpwhr
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
)
4914 LPHTTPHEADERW lphttpHdr
= NULL
;
4916 BOOL request_only
= dwModifier
& HTTP_ADDHDR_FLAG_REQ
;
4917 DWORD res
= ERROR_HTTP_INVALID_HEADER
;
4919 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field
), debugstr_w(value
), dwModifier
);
4921 /* REPLACE wins out over ADD */
4922 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
4923 dwModifier
&= ~HTTP_ADDHDR_FLAG_ADD
;
4925 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD
)
4928 index
= HTTP_GetCustomHeaderIndex(lpwhr
, field
, 0, request_only
);
4932 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD_IF_NEW
)
4933 return ERROR_HTTP_INVALID_HEADER
;
4934 lphttpHdr
= &lpwhr
->pCustHeaders
[index
];
4940 hdr
.lpszField
= (LPWSTR
)field
;
4941 hdr
.lpszValue
= (LPWSTR
)value
;
4942 hdr
.wFlags
= hdr
.wCount
= 0;
4944 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
4945 hdr
.wFlags
|= HDR_ISREQUEST
;
4947 return HTTP_InsertCustomHeader(lpwhr
, &hdr
);
4949 /* no value to delete */
4950 else return ERROR_SUCCESS
;
4952 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
4953 lphttpHdr
->wFlags
|= HDR_ISREQUEST
;
4955 lphttpHdr
->wFlags
&= ~HDR_ISREQUEST
;
4957 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
4959 HTTP_DeleteCustomHeader( lpwhr
, index
);
4965 hdr
.lpszField
= (LPWSTR
)field
;
4966 hdr
.lpszValue
= (LPWSTR
)value
;
4967 hdr
.wFlags
= hdr
.wCount
= 0;
4969 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
4970 hdr
.wFlags
|= HDR_ISREQUEST
;
4972 return HTTP_InsertCustomHeader(lpwhr
, &hdr
);
4975 return ERROR_SUCCESS
;
4977 else if (dwModifier
& COALESCEFLAGS
)
4982 INT origlen
= strlenW(lphttpHdr
->lpszValue
);
4983 INT valuelen
= strlenW(value
);
4985 if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
)
4988 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
4990 else if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON
)
4993 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
4996 len
= origlen
+ valuelen
+ ((ch
> 0) ? 2 : 0);
4998 lpsztmp
= HeapReAlloc(GetProcessHeap(), 0, lphttpHdr
->lpszValue
, (len
+1)*sizeof(WCHAR
));
5001 lphttpHdr
->lpszValue
= lpsztmp
;
5002 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5005 lphttpHdr
->lpszValue
[origlen
] = ch
;
5007 lphttpHdr
->lpszValue
[origlen
] = ' ';
5011 memcpy(&lphttpHdr
->lpszValue
[origlen
], value
, valuelen
*sizeof(WCHAR
));
5012 lphttpHdr
->lpszValue
[len
] = '\0';
5013 res
= ERROR_SUCCESS
;
5017 WARN("HeapReAlloc (%d bytes) failed\n",len
+1);
5018 res
= ERROR_OUTOFMEMORY
;
5021 TRACE("<-- %d\n", res
);
5026 /***********************************************************************
5027 * HTTP_FinishedReading (internal)
5029 * Called when all content from server has been read by client.
5032 static BOOL
HTTP_FinishedReading(http_request_t
*lpwhr
)
5034 BOOL keepalive
= HTTP_KeepAlive(lpwhr
);
5041 HTTPREQ_CloseConnection(&lpwhr
->hdr
);
5044 /* FIXME: store data in the URL cache here */
5050 /***********************************************************************
5051 * HTTP_GetCustomHeaderIndex (internal)
5053 * Return index of custom header from header array
5056 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*lpwhr
, LPCWSTR lpszField
,
5057 int requested_index
, BOOL request_only
)
5061 TRACE("%s, %d, %d\n", debugstr_w(lpszField
), requested_index
, request_only
);
5063 for (index
= 0; index
< lpwhr
->nCustHeaders
; index
++)
5065 if (strcmpiW(lpwhr
->pCustHeaders
[index
].lpszField
, lpszField
))
5068 if (request_only
&& !(lpwhr
->pCustHeaders
[index
].wFlags
& HDR_ISREQUEST
))
5071 if (!request_only
&& (lpwhr
->pCustHeaders
[index
].wFlags
& HDR_ISREQUEST
))
5074 if (requested_index
== 0)
5079 if (index
>= lpwhr
->nCustHeaders
)
5082 TRACE("Return: %d\n", index
);
5087 /***********************************************************************
5088 * HTTP_InsertCustomHeader (internal)
5090 * Insert header into array
5093 static DWORD
HTTP_InsertCustomHeader(http_request_t
*lpwhr
, LPHTTPHEADERW lpHdr
)
5096 LPHTTPHEADERW lph
= NULL
;
5098 TRACE("--> %s: %s\n", debugstr_w(lpHdr
->lpszField
), debugstr_w(lpHdr
->lpszValue
));
5099 count
= lpwhr
->nCustHeaders
+ 1;
5101 lph
= HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, lpwhr
->pCustHeaders
, sizeof(HTTPHEADERW
) * count
);
5103 lph
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(HTTPHEADERW
) * count
);
5106 return ERROR_OUTOFMEMORY
;
5108 lpwhr
->pCustHeaders
= lph
;
5109 lpwhr
->pCustHeaders
[count
-1].lpszField
= heap_strdupW(lpHdr
->lpszField
);
5110 lpwhr
->pCustHeaders
[count
-1].lpszValue
= heap_strdupW(lpHdr
->lpszValue
);
5111 lpwhr
->pCustHeaders
[count
-1].wFlags
= lpHdr
->wFlags
;
5112 lpwhr
->pCustHeaders
[count
-1].wCount
= lpHdr
->wCount
;
5113 lpwhr
->nCustHeaders
++;
5115 return ERROR_SUCCESS
;
5119 /***********************************************************************
5120 * HTTP_DeleteCustomHeader (internal)
5122 * Delete header from array
5123 * If this function is called, the indexs may change.
5125 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*lpwhr
, DWORD index
)
5127 if( lpwhr
->nCustHeaders
<= 0 )
5129 if( index
>= lpwhr
->nCustHeaders
)
5131 lpwhr
->nCustHeaders
--;
5133 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[index
].lpszField
);
5134 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[index
].lpszValue
);
5136 memmove( &lpwhr
->pCustHeaders
[index
], &lpwhr
->pCustHeaders
[index
+1],
5137 (lpwhr
->nCustHeaders
- index
)* sizeof(HTTPHEADERW
) );
5138 memset( &lpwhr
->pCustHeaders
[lpwhr
->nCustHeaders
], 0, sizeof(HTTPHEADERW
) );
5144 /***********************************************************************
5145 * HTTP_VerifyValidHeader (internal)
5147 * Verify the given header is not invalid for the given http request
5150 static BOOL
HTTP_VerifyValidHeader(http_request_t
*lpwhr
, LPCWSTR field
)
5152 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5153 if (!strcmpW(lpwhr
->lpszVersion
, g_szHttp1_0
) && !strcmpiW(field
, szAccept_Encoding
))
5154 return ERROR_HTTP_INVALID_HEADER
;
5156 return ERROR_SUCCESS
;
5159 /***********************************************************************
5160 * IsHostInProxyBypassList (@)
5165 BOOL WINAPI
IsHostInProxyBypassList(DWORD flags
, LPCSTR szHost
, DWORD length
)
5167 FIXME("STUB: flags=%d host=%s length=%d\n",flags
,szHost
,length
);