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 hostW
[] = { 'H','o','s','t',0 };
77 static const WCHAR szAuthorization
[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
78 static const WCHAR szProxy_Authorization
[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
79 static const WCHAR szStatus
[] = { 'S','t','a','t','u','s',0 };
80 static const WCHAR szKeepAlive
[] = {'K','e','e','p','-','A','l','i','v','e',0};
81 static const WCHAR szGET
[] = { 'G','E','T', 0 };
82 static const WCHAR szHEAD
[] = { 'H','E','A','D', 0 };
83 static const WCHAR szCrLf
[] = {'\r','\n', 0};
85 static const WCHAR szAccept
[] = { 'A','c','c','e','p','t',0 };
86 static const WCHAR szAccept_Charset
[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
87 static const WCHAR szAccept_Encoding
[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
88 static const WCHAR szAccept_Language
[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
89 static const WCHAR szAccept_Ranges
[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
90 static const WCHAR szAge
[] = { 'A','g','e',0 };
91 static const WCHAR szAllow
[] = { 'A','l','l','o','w',0 };
92 static const WCHAR szCache_Control
[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
93 static const WCHAR szConnection
[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
94 static const WCHAR szContent_Base
[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
95 static const WCHAR szContent_Encoding
[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
96 static const WCHAR szContent_ID
[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
97 static const WCHAR szContent_Language
[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
98 static const WCHAR szContent_Length
[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
99 static const WCHAR szContent_Location
[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
100 static const WCHAR szContent_MD5
[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
101 static const WCHAR szContent_Range
[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
102 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 };
103 static const WCHAR szContent_Type
[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
104 static const WCHAR szCookie
[] = { 'C','o','o','k','i','e',0 };
105 static const WCHAR szDate
[] = { 'D','a','t','e',0 };
106 static const WCHAR szFrom
[] = { 'F','r','o','m',0 };
107 static const WCHAR szETag
[] = { 'E','T','a','g',0 };
108 static const WCHAR szExpect
[] = { 'E','x','p','e','c','t',0 };
109 static const WCHAR szExpires
[] = { 'E','x','p','i','r','e','s',0 };
110 static const WCHAR szIf_Match
[] = { 'I','f','-','M','a','t','c','h',0 };
111 static const WCHAR szIf_Modified_Since
[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
112 static const WCHAR szIf_None_Match
[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
113 static const WCHAR szIf_Range
[] = { 'I','f','-','R','a','n','g','e',0 };
114 static const WCHAR szIf_Unmodified_Since
[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
115 static const WCHAR szLast_Modified
[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
116 static const WCHAR szLocation
[] = { 'L','o','c','a','t','i','o','n',0 };
117 static const WCHAR szMax_Forwards
[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
118 static const WCHAR szMime_Version
[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
119 static const WCHAR szPragma
[] = { 'P','r','a','g','m','a',0 };
120 static const WCHAR szProxy_Authenticate
[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
121 static const WCHAR szProxy_Connection
[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
122 static const WCHAR szPublic
[] = { 'P','u','b','l','i','c',0 };
123 static const WCHAR szRange
[] = { 'R','a','n','g','e',0 };
124 static const WCHAR szReferer
[] = { 'R','e','f','e','r','e','r',0 };
125 static const WCHAR szRetry_After
[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
126 static const WCHAR szServer
[] = { 'S','e','r','v','e','r',0 };
127 static const WCHAR szSet_Cookie
[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
128 static const WCHAR szTransfer_Encoding
[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
129 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 };
130 static const WCHAR szUpgrade
[] = { 'U','p','g','r','a','d','e',0 };
131 static const WCHAR szURI
[] = { 'U','R','I',0 };
132 static const WCHAR szUser_Agent
[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
133 static const WCHAR szVary
[] = { 'V','a','r','y',0 };
134 static const WCHAR szVia
[] = { 'V','i','a',0 };
135 static const WCHAR szWarning
[] = { 'W','a','r','n','i','n','g',0 };
136 static const WCHAR szWWW_Authenticate
[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
138 #define MAXHOSTNAME 100
139 #define MAX_FIELD_VALUE_LEN 256
140 #define MAX_FIELD_LEN 256
142 #define HTTP_REFERER szReferer
143 #define HTTP_ACCEPT szAccept
144 #define HTTP_USERAGENT szUser_Agent
146 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
147 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
148 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
149 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
150 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
151 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
152 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
154 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
165 unsigned int auth_data_len
;
166 BOOL finished
; /* finished authenticating */
170 struct gzip_stream_t
{
180 static BOOL
HTTP_OpenConnection(http_request_t
*req
);
181 static BOOL
HTTP_GetResponseHeaders(http_request_t
*req
, BOOL clear
);
182 static BOOL
HTTP_ProcessHeader(http_request_t
*req
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
);
183 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
);
184 static BOOL
HTTP_InsertCustomHeader(http_request_t
*req
, LPHTTPHEADERW lpHdr
);
185 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*req
, LPCWSTR lpszField
, INT index
, BOOL Request
);
186 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*req
, DWORD index
);
187 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
);
188 static BOOL
HTTP_HttpQueryInfoW(http_request_t
*, DWORD
, LPVOID
, LPDWORD
, LPDWORD
);
189 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*req
, LPCWSTR lpszUrl
);
190 static BOOL
HTTP_HandleRedirect(http_request_t
*req
, LPCWSTR lpszUrl
);
191 static UINT
HTTP_DecodeBase64(LPCWSTR base64
, LPSTR bin
);
192 static BOOL
HTTP_VerifyValidHeader(http_request_t
*req
, LPCWSTR field
);
193 static void HTTP_DrainContent(http_request_t
*req
);
194 static BOOL
HTTP_FinishedReading(http_request_t
*req
);
196 static LPHTTPHEADERW
HTTP_GetHeader(http_request_t
*req
, LPCWSTR head
)
199 HeaderIndex
= HTTP_GetCustomHeaderIndex(req
, head
, 0, TRUE
);
200 if (HeaderIndex
== -1)
203 return &req
->pCustHeaders
[HeaderIndex
];
208 static voidpf
wininet_zalloc(voidpf opaque
, uInt items
, uInt size
)
210 return HeapAlloc(GetProcessHeap(), 0, items
*size
);
213 static void wininet_zfree(voidpf opaque
, voidpf address
)
215 HeapFree(GetProcessHeap(), 0, address
);
218 static void init_gzip_stream(http_request_t
*req
)
220 gzip_stream_t
*gzip_stream
;
223 gzip_stream
= HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t
));
224 gzip_stream
->zstream
.zalloc
= wininet_zalloc
;
225 gzip_stream
->zstream
.zfree
= wininet_zfree
;
226 gzip_stream
->zstream
.opaque
= NULL
;
227 gzip_stream
->zstream
.next_in
= NULL
;
228 gzip_stream
->zstream
.avail_in
= 0;
229 gzip_stream
->zstream
.next_out
= NULL
;
230 gzip_stream
->zstream
.avail_out
= 0;
231 gzip_stream
->buf_pos
= 0;
232 gzip_stream
->buf_size
= 0;
233 gzip_stream
->end_of_data
= FALSE
;
235 zres
= inflateInit2(&gzip_stream
->zstream
, 0x1f);
237 ERR("inflateInit failed: %d\n", zres
);
238 HeapFree(GetProcessHeap(), 0, gzip_stream
);
242 req
->gzip_stream
= gzip_stream
;
244 index
= HTTP_GetCustomHeaderIndex(req
, szContent_Length
, 0, FALSE
);
246 HTTP_DeleteCustomHeader(req
, index
);
251 static void init_gzip_stream(http_request_t
*req
)
253 ERR("gzip stream not supported, missing zlib.\n");
258 /* set the request content length based on the headers */
259 static DWORD
set_content_length( http_request_t
*lpwhr
)
261 static const WCHAR szChunked
[] = {'c','h','u','n','k','e','d',0};
265 size
= sizeof(lpwhr
->dwContentLength
);
266 if (!HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_CONTENT_LENGTH
,
267 &lpwhr
->dwContentLength
, &size
, NULL
))
268 lpwhr
->dwContentLength
= ~0u;
270 size
= sizeof(encoding
);
271 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_TRANSFER_ENCODING
, encoding
, &size
, NULL
) &&
272 !strcmpiW(encoding
, szChunked
))
274 lpwhr
->dwContentLength
= ~0u;
275 lpwhr
->read_chunked
= TRUE
;
278 if(lpwhr
->decoding
) {
281 static const WCHAR gzipW
[] = {'g','z','i','p',0};
283 encoding_idx
= HTTP_GetCustomHeaderIndex(lpwhr
, szContent_Encoding
, 0, FALSE
);
284 if(encoding_idx
!= -1 && !strcmpiW(lpwhr
->pCustHeaders
[encoding_idx
].lpszValue
, gzipW
))
285 init_gzip_stream(lpwhr
);
288 return lpwhr
->dwContentLength
;
291 /***********************************************************************
292 * HTTP_Tokenize (internal)
294 * Tokenize a string, allocating memory for the tokens.
296 static LPWSTR
* HTTP_Tokenize(LPCWSTR string
, LPCWSTR token_string
)
298 LPWSTR
* token_array
;
305 /* empty string has no tokens */
309 for (i
= 0; string
[i
]; i
++)
311 if (!strncmpW(string
+i
, token_string
, strlenW(token_string
)))
315 /* we want to skip over separators, but not the null terminator */
316 for (j
= 0; j
< strlenW(token_string
) - 1; j
++)
324 /* add 1 for terminating NULL */
325 token_array
= HeapAlloc(GetProcessHeap(), 0, (tokens
+1) * sizeof(*token_array
));
326 token_array
[tokens
] = NULL
;
329 for (i
= 0; i
< tokens
; i
++)
332 next_token
= strstrW(string
, token_string
);
333 if (!next_token
) next_token
= string
+strlenW(string
);
334 len
= next_token
- string
;
335 token_array
[i
] = HeapAlloc(GetProcessHeap(), 0, (len
+1)*sizeof(WCHAR
));
336 memcpy(token_array
[i
], string
, len
*sizeof(WCHAR
));
337 token_array
[i
][len
] = '\0';
338 string
= next_token
+strlenW(token_string
);
343 /***********************************************************************
344 * HTTP_FreeTokens (internal)
346 * Frees memory returned from HTTP_Tokenize.
348 static void HTTP_FreeTokens(LPWSTR
* token_array
)
351 for (i
= 0; token_array
[i
]; i
++)
352 HeapFree(GetProcessHeap(), 0, token_array
[i
]);
353 HeapFree(GetProcessHeap(), 0, token_array
);
356 /* **********************************************************************
358 * Helper functions for the HttpSendRequest(Ex) functions
361 static void AsyncHttpSendRequestProc(WORKREQUEST
*workRequest
)
363 struct WORKREQ_HTTPSENDREQUESTW
const *req
= &workRequest
->u
.HttpSendRequestW
;
364 http_request_t
*lpwhr
= (http_request_t
*) workRequest
->hdr
;
366 TRACE("%p\n", lpwhr
);
368 HTTP_HttpSendRequestW(lpwhr
, req
->lpszHeader
,
369 req
->dwHeaderLength
, req
->lpOptional
, req
->dwOptionalLength
,
370 req
->dwContentLength
, req
->bEndRequest
);
372 HeapFree(GetProcessHeap(), 0, req
->lpszHeader
);
375 static void HTTP_FixURL(http_request_t
*lpwhr
)
377 static const WCHAR szSlash
[] = { '/',0 };
378 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/', 0 };
380 /* If we don't have a path we set it to root */
381 if (NULL
== lpwhr
->lpszPath
)
382 lpwhr
->lpszPath
= heap_strdupW(szSlash
);
383 else /* remove \r and \n*/
385 int nLen
= strlenW(lpwhr
->lpszPath
);
386 while ((nLen
>0 ) && ((lpwhr
->lpszPath
[nLen
-1] == '\r')||(lpwhr
->lpszPath
[nLen
-1] == '\n')))
389 lpwhr
->lpszPath
[nLen
]='\0';
391 /* Replace '\' with '/' */
394 if (lpwhr
->lpszPath
[nLen
] == '\\') lpwhr
->lpszPath
[nLen
]='/';
398 if(CSTR_EQUAL
!= CompareStringW( LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
399 lpwhr
->lpszPath
, strlenW(lpwhr
->lpszPath
), szHttp
, strlenW(szHttp
) )
400 && lpwhr
->lpszPath
[0] != '/') /* not an absolute path ?? --> fix it !! */
402 WCHAR
*fixurl
= HeapAlloc(GetProcessHeap(), 0,
403 (strlenW(lpwhr
->lpszPath
) + 2)*sizeof(WCHAR
));
405 strcpyW(fixurl
+ 1, lpwhr
->lpszPath
);
406 HeapFree( GetProcessHeap(), 0, lpwhr
->lpszPath
);
407 lpwhr
->lpszPath
= fixurl
;
411 static LPWSTR
HTTP_BuildHeaderRequestString( http_request_t
*lpwhr
, LPCWSTR verb
, LPCWSTR path
, LPCWSTR version
)
413 LPWSTR requestString
;
419 static const WCHAR szSpace
[] = { ' ',0 };
420 static const WCHAR szColon
[] = { ':',' ',0 };
421 static const WCHAR sztwocrlf
[] = {'\r','\n','\r','\n', 0};
423 /* allocate space for an array of all the string pointers to be added */
424 len
= (lpwhr
->nCustHeaders
)*4 + 10;
425 req
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(LPCWSTR
) );
427 /* add the verb, path and HTTP version string */
435 /* Append custom request headers */
436 for (i
= 0; i
< lpwhr
->nCustHeaders
; i
++)
438 if (lpwhr
->pCustHeaders
[i
].wFlags
& HDR_ISREQUEST
)
441 req
[n
++] = lpwhr
->pCustHeaders
[i
].lpszField
;
443 req
[n
++] = lpwhr
->pCustHeaders
[i
].lpszValue
;
445 TRACE("Adding custom header %s (%s)\n",
446 debugstr_w(lpwhr
->pCustHeaders
[i
].lpszField
),
447 debugstr_w(lpwhr
->pCustHeaders
[i
].lpszValue
));
452 ERR("oops. buffer overrun\n");
455 requestString
= HTTP_build_req( req
, 4 );
456 HeapFree( GetProcessHeap(), 0, req
);
459 * Set (header) termination string for request
460 * Make sure there's exactly two new lines at the end of the request
462 p
= &requestString
[strlenW(requestString
)-1];
463 while ( (*p
== '\n') || (*p
== '\r') )
465 strcpyW( p
+1, sztwocrlf
);
467 return requestString
;
470 static void HTTP_ProcessCookies( http_request_t
*lpwhr
)
474 LPHTTPHEADERW setCookieHeader
;
476 while((HeaderIndex
= HTTP_GetCustomHeaderIndex(lpwhr
, szSet_Cookie
, numCookies
, FALSE
)) != -1)
478 setCookieHeader
= &lpwhr
->pCustHeaders
[HeaderIndex
];
480 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
) && setCookieHeader
->lpszValue
)
483 static const WCHAR szFmt
[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
487 Host
= HTTP_GetHeader(lpwhr
, hostW
);
488 len
= lstrlenW(Host
->lpszValue
) + 9 + lstrlenW(lpwhr
->lpszPath
);
489 buf_url
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
490 sprintfW(buf_url
, szFmt
, Host
->lpszValue
, lpwhr
->lpszPath
);
491 InternetSetCookieW(buf_url
, NULL
, setCookieHeader
->lpszValue
);
493 HeapFree(GetProcessHeap(), 0, buf_url
);
499 static inline BOOL
is_basic_auth_value( LPCWSTR pszAuthValue
)
501 static const WCHAR szBasic
[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
502 return !strncmpiW(pszAuthValue
, szBasic
, ARRAYSIZE(szBasic
)) &&
503 ((pszAuthValue
[ARRAYSIZE(szBasic
)] == ' ') || !pszAuthValue
[ARRAYSIZE(szBasic
)]);
506 static BOOL
HTTP_DoAuthorization( http_request_t
*lpwhr
, LPCWSTR pszAuthValue
,
507 struct HttpAuthInfo
**ppAuthInfo
,
508 LPWSTR domain_and_username
, LPWSTR password
)
510 SECURITY_STATUS sec_status
;
511 struct HttpAuthInfo
*pAuthInfo
= *ppAuthInfo
;
514 TRACE("%s\n", debugstr_w(pszAuthValue
));
521 pAuthInfo
= HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo
));
525 SecInvalidateHandle(&pAuthInfo
->cred
);
526 SecInvalidateHandle(&pAuthInfo
->ctx
);
527 memset(&pAuthInfo
->exp
, 0, sizeof(pAuthInfo
->exp
));
529 pAuthInfo
->auth_data
= NULL
;
530 pAuthInfo
->auth_data_len
= 0;
531 pAuthInfo
->finished
= FALSE
;
533 if (is_basic_auth_value(pszAuthValue
))
535 static const WCHAR szBasic
[] = {'B','a','s','i','c',0};
536 pAuthInfo
->scheme
= heap_strdupW(szBasic
);
537 if (!pAuthInfo
->scheme
)
539 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
546 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity
;
548 pAuthInfo
->scheme
= heap_strdupW(pszAuthValue
);
549 if (!pAuthInfo
->scheme
)
551 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
555 if (domain_and_username
)
557 WCHAR
*user
= strchrW(domain_and_username
, '\\');
558 WCHAR
*domain
= domain_and_username
;
560 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
562 pAuthData
= &nt_auth_identity
;
567 user
= domain_and_username
;
571 nt_auth_identity
.Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
572 nt_auth_identity
.User
= user
;
573 nt_auth_identity
.UserLength
= strlenW(nt_auth_identity
.User
);
574 nt_auth_identity
.Domain
= domain
;
575 nt_auth_identity
.DomainLength
= domain
? user
- domain
- 1 : 0;
576 nt_auth_identity
.Password
= password
;
577 nt_auth_identity
.PasswordLength
= strlenW(nt_auth_identity
.Password
);
580 /* use default credentials */
583 sec_status
= AcquireCredentialsHandleW(NULL
, pAuthInfo
->scheme
,
584 SECPKG_CRED_OUTBOUND
, NULL
,
586 NULL
, &pAuthInfo
->cred
,
588 if (sec_status
== SEC_E_OK
)
590 PSecPkgInfoW sec_pkg_info
;
591 sec_status
= QuerySecurityPackageInfoW(pAuthInfo
->scheme
, &sec_pkg_info
);
592 if (sec_status
== SEC_E_OK
)
594 pAuthInfo
->max_token
= sec_pkg_info
->cbMaxToken
;
595 FreeContextBuffer(sec_pkg_info
);
598 if (sec_status
!= SEC_E_OK
)
600 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
601 debugstr_w(pAuthInfo
->scheme
), sec_status
);
602 HeapFree(GetProcessHeap(), 0, pAuthInfo
->scheme
);
603 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
607 *ppAuthInfo
= pAuthInfo
;
609 else if (pAuthInfo
->finished
)
612 if ((strlenW(pszAuthValue
) < strlenW(pAuthInfo
->scheme
)) ||
613 strncmpiW(pszAuthValue
, pAuthInfo
->scheme
, strlenW(pAuthInfo
->scheme
)))
615 ERR("authentication scheme changed from %s to %s\n",
616 debugstr_w(pAuthInfo
->scheme
), debugstr_w(pszAuthValue
));
620 if (is_basic_auth_value(pszAuthValue
))
626 TRACE("basic authentication\n");
628 /* we don't cache credentials for basic authentication, so we can't
629 * retrieve them if the application didn't pass us any credentials */
630 if (!domain_and_username
) return FALSE
;
632 userlen
= WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, lstrlenW(domain_and_username
), NULL
, 0, NULL
, NULL
);
633 passlen
= WideCharToMultiByte(CP_UTF8
, 0, password
, lstrlenW(password
), NULL
, 0, NULL
, NULL
);
635 /* length includes a nul terminator, which will be re-used for the ':' */
636 auth_data
= HeapAlloc(GetProcessHeap(), 0, userlen
+ 1 + passlen
);
640 WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, -1, auth_data
, userlen
, NULL
, NULL
);
641 auth_data
[userlen
] = ':';
642 WideCharToMultiByte(CP_UTF8
, 0, password
, -1, &auth_data
[userlen
+1], passlen
, NULL
, NULL
);
644 pAuthInfo
->auth_data
= auth_data
;
645 pAuthInfo
->auth_data_len
= userlen
+ 1 + passlen
;
646 pAuthInfo
->finished
= TRUE
;
653 SecBufferDesc out_desc
, in_desc
;
655 unsigned char *buffer
;
656 ULONG context_req
= ISC_REQ_CONNECTION
| ISC_REQ_USE_DCE_STYLE
|
657 ISC_REQ_MUTUAL_AUTH
| ISC_REQ_DELEGATE
;
659 in
.BufferType
= SECBUFFER_TOKEN
;
663 in_desc
.ulVersion
= 0;
664 in_desc
.cBuffers
= 1;
665 in_desc
.pBuffers
= &in
;
667 pszAuthData
= pszAuthValue
+ strlenW(pAuthInfo
->scheme
);
668 if (*pszAuthData
== ' ')
671 in
.cbBuffer
= HTTP_DecodeBase64(pszAuthData
, NULL
);
672 in
.pvBuffer
= HeapAlloc(GetProcessHeap(), 0, in
.cbBuffer
);
673 HTTP_DecodeBase64(pszAuthData
, in
.pvBuffer
);
676 buffer
= HeapAlloc(GetProcessHeap(), 0, pAuthInfo
->max_token
);
678 out
.BufferType
= SECBUFFER_TOKEN
;
679 out
.cbBuffer
= pAuthInfo
->max_token
;
680 out
.pvBuffer
= buffer
;
682 out_desc
.ulVersion
= 0;
683 out_desc
.cBuffers
= 1;
684 out_desc
.pBuffers
= &out
;
686 sec_status
= InitializeSecurityContextW(first
? &pAuthInfo
->cred
: NULL
,
687 first
? NULL
: &pAuthInfo
->ctx
,
688 first
? lpwhr
->lpHttpSession
->lpszServerName
: NULL
,
689 context_req
, 0, SECURITY_NETWORK_DREP
,
690 in
.pvBuffer
? &in_desc
: NULL
,
691 0, &pAuthInfo
->ctx
, &out_desc
,
692 &pAuthInfo
->attr
, &pAuthInfo
->exp
);
693 if (sec_status
== SEC_E_OK
)
695 pAuthInfo
->finished
= TRUE
;
696 pAuthInfo
->auth_data
= out
.pvBuffer
;
697 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
698 TRACE("sending last auth packet\n");
700 else if (sec_status
== SEC_I_CONTINUE_NEEDED
)
702 pAuthInfo
->auth_data
= out
.pvBuffer
;
703 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
704 TRACE("sending next auth packet\n");
708 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status
);
709 pAuthInfo
->finished
= TRUE
;
710 HeapFree(GetProcessHeap(), 0, out
.pvBuffer
);
718 /***********************************************************************
719 * HTTP_HttpAddRequestHeadersW (internal)
721 static BOOL
HTTP_HttpAddRequestHeadersW(http_request_t
*lpwhr
,
722 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
727 BOOL bSuccess
= FALSE
;
730 TRACE("copying header: %s\n", debugstr_wn(lpszHeader
, dwHeaderLength
));
732 if( dwHeaderLength
== ~0U )
733 len
= strlenW(lpszHeader
);
735 len
= dwHeaderLength
;
736 buffer
= HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR
)*(len
+1) );
737 lstrcpynW( buffer
, lpszHeader
, len
+ 1);
743 LPWSTR
* pFieldAndValue
;
747 while (*lpszEnd
!= '\0')
749 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
754 if (*lpszStart
== '\0')
757 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
760 lpszEnd
++; /* Jump over newline */
762 TRACE("interpreting header %s\n", debugstr_w(lpszStart
));
763 if (*lpszStart
== '\0')
765 /* Skip 0-length headers */
770 pFieldAndValue
= HTTP_InterpretHttpHeader(lpszStart
);
773 bSuccess
= HTTP_VerifyValidHeader(lpwhr
, pFieldAndValue
[0]);
775 bSuccess
= HTTP_ProcessHeader(lpwhr
, pFieldAndValue
[0],
776 pFieldAndValue
[1], dwModifier
| HTTP_ADDHDR_FLAG_REQ
);
777 HTTP_FreeTokens(pFieldAndValue
);
783 HeapFree(GetProcessHeap(), 0, buffer
);
788 /***********************************************************************
789 * HttpAddRequestHeadersW (WININET.@)
791 * Adds one or more HTTP header to the request handler
794 * On Windows if dwHeaderLength includes the trailing '\0', then
795 * HttpAddRequestHeadersW() adds it too. However this results in an
796 * invalid Http header which is rejected by some servers so we probably
797 * don't need to match Windows on that point.
804 BOOL WINAPI
HttpAddRequestHeadersW(HINTERNET hHttpRequest
,
805 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
807 BOOL bSuccess
= FALSE
;
808 http_request_t
*lpwhr
;
810 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_wn(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
815 lpwhr
= (http_request_t
*) WININET_GetObject( hHttpRequest
);
816 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
818 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
821 bSuccess
= HTTP_HttpAddRequestHeadersW( lpwhr
, lpszHeader
, dwHeaderLength
, dwModifier
);
824 WININET_Release( &lpwhr
->hdr
);
829 /***********************************************************************
830 * HttpAddRequestHeadersA (WININET.@)
832 * Adds one or more HTTP header to the request handler
839 BOOL WINAPI
HttpAddRequestHeadersA(HINTERNET hHttpRequest
,
840 LPCSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
846 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_an(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
848 len
= MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, NULL
, 0 );
849 hdr
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
850 MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, hdr
, len
);
851 if( dwHeaderLength
!= ~0U )
852 dwHeaderLength
= len
;
854 r
= HttpAddRequestHeadersW( hHttpRequest
, hdr
, dwHeaderLength
, dwModifier
);
856 HeapFree( GetProcessHeap(), 0, hdr
);
861 /***********************************************************************
862 * HttpEndRequestA (WININET.@)
864 * Ends an HTTP request that was started by HttpSendRequestEx
871 BOOL WINAPI
HttpEndRequestA(HINTERNET hRequest
,
872 LPINTERNET_BUFFERSA lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
874 TRACE("(%p, %p, %08x, %08lx)\n", hRequest
, lpBuffersOut
, dwFlags
, dwContext
);
878 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
882 return HttpEndRequestW(hRequest
, NULL
, dwFlags
, dwContext
);
885 static BOOL
HTTP_HttpEndRequestW(http_request_t
*lpwhr
, DWORD dwFlags
, DWORD_PTR dwContext
)
890 INTERNET_ASYNC_RESULT iar
;
892 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
893 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
895 responseLen
= HTTP_GetResponseHeaders(lpwhr
, TRUE
);
899 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
900 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
, sizeof(DWORD
));
902 /* process cookies here. Is this right? */
903 HTTP_ProcessCookies(lpwhr
);
905 if (!set_content_length( lpwhr
)) HTTP_FinishedReading(lpwhr
);
907 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
))
909 DWORD dwCode
,dwCodeLength
= sizeof(DWORD
);
910 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_STATUS_CODE
, &dwCode
, &dwCodeLength
, NULL
) &&
911 (dwCode
== 302 || dwCode
== 301 || dwCode
== 303))
913 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
914 dwBufferSize
=sizeof(szNewLocation
);
915 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_LOCATION
, szNewLocation
, &dwBufferSize
, NULL
))
917 if (strcmpW(lpwhr
->lpszVerb
, szGET
) && strcmpW(lpwhr
->lpszVerb
, szHEAD
))
919 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVerb
);
920 lpwhr
->lpszVerb
= heap_strdupW(szGET
);
922 HTTP_DrainContent(lpwhr
);
923 if ((new_url
= HTTP_GetRedirectURL( lpwhr
, szNewLocation
)))
925 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
926 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
927 rc
= HTTP_HandleRedirect(lpwhr
, new_url
);
929 rc
= HTTP_HttpSendRequestW(lpwhr
, NULL
, 0, NULL
, 0, 0, TRUE
);
930 HeapFree( GetProcessHeap(), 0, new_url
);
936 iar
.dwResult
= (DWORD_PTR
)lpwhr
->hdr
.hInternet
;
937 iar
.dwError
= rc
? 0 : INTERNET_GetLastError();
939 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
940 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
941 sizeof(INTERNET_ASYNC_RESULT
));
945 static void AsyncHttpEndRequestProc(WORKREQUEST
*work
)
947 struct WORKREQ_HTTPENDREQUESTW
const *req
= &work
->u
.HttpEndRequestW
;
948 http_request_t
*lpwhr
= (http_request_t
*)work
->hdr
;
950 TRACE("%p\n", lpwhr
);
952 HTTP_HttpEndRequestW(lpwhr
, req
->dwFlags
, req
->dwContext
);
955 /***********************************************************************
956 * HttpEndRequestW (WININET.@)
958 * Ends an HTTP request that was started by HttpSendRequestEx
965 BOOL WINAPI
HttpEndRequestW(HINTERNET hRequest
,
966 LPINTERNET_BUFFERSW lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
969 http_request_t
*lpwhr
;
975 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
979 lpwhr
= (http_request_t
*) WININET_GetObject( hRequest
);
981 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
983 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
985 WININET_Release( &lpwhr
->hdr
);
988 lpwhr
->hdr
.dwFlags
|= dwFlags
;
990 if (lpwhr
->lpHttpSession
->lpAppInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
993 struct WORKREQ_HTTPENDREQUESTW
*request
;
995 work
.asyncproc
= AsyncHttpEndRequestProc
;
996 work
.hdr
= WININET_AddRef( &lpwhr
->hdr
);
998 request
= &work
.u
.HttpEndRequestW
;
999 request
->dwFlags
= dwFlags
;
1000 request
->dwContext
= dwContext
;
1002 INTERNET_AsyncCall(&work
);
1003 INTERNET_SetLastError(ERROR_IO_PENDING
);
1006 rc
= HTTP_HttpEndRequestW(lpwhr
, dwFlags
, dwContext
);
1008 WININET_Release( &lpwhr
->hdr
);
1009 TRACE("%i <--\n",rc
);
1013 /***********************************************************************
1014 * HttpOpenRequestW (WININET.@)
1016 * Open a HTTP request handle
1019 * HINTERNET a HTTP request handle on success
1023 HINTERNET WINAPI
HttpOpenRequestW(HINTERNET hHttpSession
,
1024 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
1025 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
1026 DWORD dwFlags
, DWORD_PTR dwContext
)
1028 http_session_t
*lpwhs
;
1029 HINTERNET handle
= NULL
;
1031 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
1032 debugstr_w(lpszVerb
), debugstr_w(lpszObjectName
),
1033 debugstr_w(lpszVersion
), debugstr_w(lpszReferrer
), lpszAcceptTypes
,
1034 dwFlags
, dwContext
);
1035 if(lpszAcceptTypes
!=NULL
)
1038 for(i
=0;lpszAcceptTypes
[i
]!=NULL
;i
++)
1039 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes
[i
]));
1042 lpwhs
= (http_session_t
*) WININET_GetObject( hHttpSession
);
1043 if (NULL
== lpwhs
|| lpwhs
->hdr
.htype
!= WH_HHTTPSESSION
)
1045 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
1050 * My tests seem to show that the windows version does not
1051 * become asynchronous until after this point. And anyhow
1052 * if this call was asynchronous then how would you get the
1053 * necessary HINTERNET pointer returned by this function.
1056 handle
= HTTP_HttpOpenRequestW(lpwhs
, lpszVerb
, lpszObjectName
,
1057 lpszVersion
, lpszReferrer
, lpszAcceptTypes
,
1058 dwFlags
, dwContext
);
1061 WININET_Release( &lpwhs
->hdr
);
1062 TRACE("returning %p\n", handle
);
1067 /***********************************************************************
1068 * HttpOpenRequestA (WININET.@)
1070 * Open a HTTP request handle
1073 * HINTERNET a HTTP request handle on success
1077 HINTERNET WINAPI
HttpOpenRequestA(HINTERNET hHttpSession
,
1078 LPCSTR lpszVerb
, LPCSTR lpszObjectName
, LPCSTR lpszVersion
,
1079 LPCSTR lpszReferrer
, LPCSTR
*lpszAcceptTypes
,
1080 DWORD dwFlags
, DWORD_PTR dwContext
)
1082 LPWSTR szVerb
= NULL
, szObjectName
= NULL
;
1083 LPWSTR szVersion
= NULL
, szReferrer
= NULL
, *szAcceptTypes
= NULL
;
1084 INT acceptTypesCount
;
1085 HINTERNET rc
= FALSE
;
1088 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
1089 debugstr_a(lpszVerb
), debugstr_a(lpszObjectName
),
1090 debugstr_a(lpszVersion
), debugstr_a(lpszReferrer
), lpszAcceptTypes
,
1091 dwFlags
, dwContext
);
1095 szVerb
= heap_strdupAtoW(lpszVerb
);
1102 szObjectName
= heap_strdupAtoW(lpszObjectName
);
1103 if ( !szObjectName
)
1109 szVersion
= heap_strdupAtoW(lpszVersion
);
1116 szReferrer
= heap_strdupAtoW(lpszReferrer
);
1121 if (lpszAcceptTypes
)
1123 acceptTypesCount
= 0;
1124 types
= lpszAcceptTypes
;
1129 /* find out how many there are */
1130 if (*types
&& **types
)
1132 TRACE("accept type: %s\n", debugstr_a(*types
));
1138 WARN("invalid accept type pointer\n");
1143 szAcceptTypes
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
*) * (acceptTypesCount
+1));
1144 if (!szAcceptTypes
) goto end
;
1146 acceptTypesCount
= 0;
1147 types
= lpszAcceptTypes
;
1152 if (*types
&& **types
)
1153 szAcceptTypes
[acceptTypesCount
++] = heap_strdupAtoW(*types
);
1157 /* ignore invalid pointer */
1162 szAcceptTypes
[acceptTypesCount
] = NULL
;
1165 rc
= HttpOpenRequestW(hHttpSession
, szVerb
, szObjectName
,
1166 szVersion
, szReferrer
,
1167 (LPCWSTR
*)szAcceptTypes
, dwFlags
, dwContext
);
1172 acceptTypesCount
= 0;
1173 while (szAcceptTypes
[acceptTypesCount
])
1175 HeapFree(GetProcessHeap(), 0, szAcceptTypes
[acceptTypesCount
]);
1178 HeapFree(GetProcessHeap(), 0, szAcceptTypes
);
1180 HeapFree(GetProcessHeap(), 0, szReferrer
);
1181 HeapFree(GetProcessHeap(), 0, szVersion
);
1182 HeapFree(GetProcessHeap(), 0, szObjectName
);
1183 HeapFree(GetProcessHeap(), 0, szVerb
);
1188 /***********************************************************************
1191 static UINT
HTTP_EncodeBase64( LPCSTR bin
, unsigned int len
, LPWSTR base64
)
1194 static const CHAR HTTP_Base64Enc
[] =
1195 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1199 /* first 6 bits, all from bin[0] */
1200 base64
[n
++] = HTTP_Base64Enc
[(bin
[0] & 0xfc) >> 2];
1201 x
= (bin
[0] & 3) << 4;
1203 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1206 base64
[n
++] = HTTP_Base64Enc
[x
];
1211 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[1]&0xf0) >> 4 ) ];
1212 x
= ( bin
[1] & 0x0f ) << 2;
1214 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1217 base64
[n
++] = HTTP_Base64Enc
[x
];
1221 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[2]&0xc0 ) >> 6 ) ];
1223 /* last 6 bits, all from bin [2] */
1224 base64
[n
++] = HTTP_Base64Enc
[ bin
[2] & 0x3f ];
1232 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1233 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1234 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1235 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1236 static const signed char HTTP_Base64Dec
[256] =
1238 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1239 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1240 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1241 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1242 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1243 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1244 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1245 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1246 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1247 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1248 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1249 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1250 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1251 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1252 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1253 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1254 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1255 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1256 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1257 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1258 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1259 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1260 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1261 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1262 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1263 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1267 /***********************************************************************
1270 static UINT
HTTP_DecodeBase64( LPCWSTR base64
, LPSTR bin
)
1278 if (base64
[0] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1279 ((in
[0] = HTTP_Base64Dec
[base64
[0]]) == -1) ||
1280 base64
[1] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1281 ((in
[1] = HTTP_Base64Dec
[base64
[1]]) == -1))
1283 WARN("invalid base64: %s\n", debugstr_w(base64
));
1287 bin
[n
] = (unsigned char) (in
[0] << 2 | in
[1] >> 4);
1290 if ((base64
[2] == '=') && (base64
[3] == '='))
1292 if (base64
[2] > ARRAYSIZE(HTTP_Base64Dec
) ||
1293 ((in
[2] = HTTP_Base64Dec
[base64
[2]]) == -1))
1295 WARN("invalid base64: %s\n", debugstr_w(&base64
[2]));
1299 bin
[n
] = (unsigned char) (in
[1] << 4 | in
[2] >> 2);
1302 if (base64
[3] == '=')
1304 if (base64
[3] > ARRAYSIZE(HTTP_Base64Dec
) ||
1305 ((in
[3] = HTTP_Base64Dec
[base64
[3]]) == -1))
1307 WARN("invalid base64: %s\n", debugstr_w(&base64
[3]));
1311 bin
[n
] = (unsigned char) (((in
[2] << 6) & 0xc0) | in
[3]);
1320 /***********************************************************************
1321 * HTTP_InsertAuthorization
1323 * Insert or delete the authorization field in the request header.
1325 static BOOL
HTTP_InsertAuthorization( http_request_t
*lpwhr
, struct HttpAuthInfo
*pAuthInfo
, LPCWSTR header
)
1329 static const WCHAR wszSpace
[] = {' ',0};
1330 static const WCHAR wszBasic
[] = {'B','a','s','i','c',0};
1332 WCHAR
*authorization
= NULL
;
1334 if (pAuthInfo
->auth_data_len
)
1336 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1337 len
= strlenW(pAuthInfo
->scheme
)+1+((pAuthInfo
->auth_data_len
+2)*4)/3;
1338 authorization
= HeapAlloc(GetProcessHeap(), 0, (len
+1)*sizeof(WCHAR
));
1342 strcpyW(authorization
, pAuthInfo
->scheme
);
1343 strcatW(authorization
, wszSpace
);
1344 HTTP_EncodeBase64(pAuthInfo
->auth_data
,
1345 pAuthInfo
->auth_data_len
,
1346 authorization
+strlenW(authorization
));
1348 /* clear the data as it isn't valid now that it has been sent to the
1349 * server, unless it's Basic authentication which doesn't do
1350 * connection tracking */
1351 if (strcmpiW(pAuthInfo
->scheme
, wszBasic
))
1353 HeapFree(GetProcessHeap(), 0, pAuthInfo
->auth_data
);
1354 pAuthInfo
->auth_data
= NULL
;
1355 pAuthInfo
->auth_data_len
= 0;
1359 TRACE("Inserting authorization: %s\n", debugstr_w(authorization
));
1361 HTTP_ProcessHeader(lpwhr
, header
, authorization
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
1363 HeapFree(GetProcessHeap(), 0, authorization
);
1368 static WCHAR
*HTTP_BuildProxyRequestUrl(http_request_t
*req
)
1370 WCHAR new_location
[INTERNET_MAX_URL_LENGTH
], *url
;
1373 size
= sizeof(new_location
);
1374 if (HTTP_HttpQueryInfoW(req
, HTTP_QUERY_LOCATION
, new_location
, &size
, NULL
))
1376 if (!(url
= HeapAlloc( GetProcessHeap(), 0, size
+ sizeof(WCHAR
) ))) return NULL
;
1377 strcpyW( url
, new_location
);
1381 static const WCHAR slash
[] = { '/',0 };
1382 static const WCHAR format
[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1383 static const WCHAR formatSSL
[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1384 http_session_t
*session
= req
->lpHttpSession
;
1386 size
= 16; /* "https://" + sizeof(port#) + ":/\0" */
1387 size
+= strlenW( session
->lpszHostName
) + strlenW( req
->lpszPath
);
1389 if (!(url
= HeapAlloc( GetProcessHeap(), 0, size
* sizeof(WCHAR
) ))) return NULL
;
1391 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1392 sprintfW( url
, formatSSL
, session
->lpszHostName
, session
->nHostPort
);
1394 sprintfW( url
, format
, session
->lpszHostName
, session
->nHostPort
);
1395 if (req
->lpszPath
[0] != '/') strcatW( url
, slash
);
1396 strcatW( url
, req
->lpszPath
);
1398 TRACE("url=%s\n", debugstr_w(url
));
1402 /***********************************************************************
1403 * HTTP_DealWithProxy
1405 static BOOL
HTTP_DealWithProxy(appinfo_t
*hIC
, http_session_t
*lpwhs
, http_request_t
*lpwhr
)
1407 WCHAR buf
[MAXHOSTNAME
];
1408 WCHAR proxy
[MAXHOSTNAME
+ 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1409 static WCHAR szNul
[] = { 0 };
1410 URL_COMPONENTSW UrlComponents
;
1411 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/',0 };
1412 static const WCHAR szFormat
[] = { 'h','t','t','p',':','/','/','%','s',0 };
1414 memset( &UrlComponents
, 0, sizeof UrlComponents
);
1415 UrlComponents
.dwStructSize
= sizeof UrlComponents
;
1416 UrlComponents
.lpszHostName
= buf
;
1417 UrlComponents
.dwHostNameLength
= MAXHOSTNAME
;
1419 if( CSTR_EQUAL
!= CompareStringW(LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
1420 hIC
->lpszProxy
,strlenW(szHttp
),szHttp
,strlenW(szHttp
)) )
1421 sprintfW(proxy
, szFormat
, hIC
->lpszProxy
);
1423 strcpyW(proxy
, hIC
->lpszProxy
);
1424 if( !InternetCrackUrlW(proxy
, 0, 0, &UrlComponents
) )
1426 if( UrlComponents
.dwHostNameLength
== 0 )
1429 if( !lpwhr
->lpszPath
)
1430 lpwhr
->lpszPath
= szNul
;
1432 if(UrlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
1433 UrlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
1435 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszServerName
);
1436 lpwhs
->lpszServerName
= heap_strdupW(UrlComponents
.lpszHostName
);
1437 lpwhs
->nServerPort
= UrlComponents
.nPort
;
1439 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs
->lpszServerName
), lpwhs
->nServerPort
);
1443 #ifndef INET6_ADDRSTRLEN
1444 #define INET6_ADDRSTRLEN 46
1447 static BOOL
HTTP_ResolveName(http_request_t
*lpwhr
)
1449 char szaddr
[INET6_ADDRSTRLEN
];
1450 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
1453 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1454 INTERNET_STATUS_RESOLVING_NAME
,
1455 lpwhs
->lpszServerName
,
1456 strlenW(lpwhs
->lpszServerName
)+1);
1458 lpwhs
->sa_len
= sizeof(lpwhs
->socketAddress
);
1459 if (!GetAddress(lpwhs
->lpszServerName
, lpwhs
->nServerPort
,
1460 (struct sockaddr
*)&lpwhs
->socketAddress
, &lpwhs
->sa_len
))
1462 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED
);
1466 switch (lpwhs
->socketAddress
.ss_family
)
1469 addr
= &((struct sockaddr_in
*)&lpwhs
->socketAddress
)->sin_addr
;
1472 addr
= &((struct sockaddr_in6
*)&lpwhs
->socketAddress
)->sin6_addr
;
1475 WARN("unsupported family %d\n", lpwhs
->socketAddress
.ss_family
);
1476 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED
);
1479 inet_ntop(lpwhs
->socketAddress
.ss_family
, addr
, szaddr
, sizeof(szaddr
));
1480 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1481 INTERNET_STATUS_NAME_RESOLVED
,
1482 szaddr
, strlen(szaddr
)+1);
1484 TRACE("resolved %s to %s\n", debugstr_w(lpwhs
->lpszServerName
), szaddr
);
1489 /***********************************************************************
1490 * HTTPREQ_Destroy (internal)
1492 * Deallocate request handle
1495 static void HTTPREQ_Destroy(object_header_t
*hdr
)
1497 http_request_t
*lpwhr
= (http_request_t
*) hdr
;
1502 if(lpwhr
->hCacheFile
)
1503 CloseHandle(lpwhr
->hCacheFile
);
1505 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszCacheFile
);
1507 DeleteCriticalSection( &lpwhr
->read_section
);
1508 WININET_Release(&lpwhr
->lpHttpSession
->hdr
);
1510 if (lpwhr
->pAuthInfo
)
1512 if (SecIsValidHandle(&lpwhr
->pAuthInfo
->ctx
))
1513 DeleteSecurityContext(&lpwhr
->pAuthInfo
->ctx
);
1514 if (SecIsValidHandle(&lpwhr
->pAuthInfo
->cred
))
1515 FreeCredentialsHandle(&lpwhr
->pAuthInfo
->cred
);
1517 HeapFree(GetProcessHeap(), 0, lpwhr
->pAuthInfo
->auth_data
);
1518 HeapFree(GetProcessHeap(), 0, lpwhr
->pAuthInfo
->scheme
);
1519 HeapFree(GetProcessHeap(), 0, lpwhr
->pAuthInfo
);
1520 lpwhr
->pAuthInfo
= NULL
;
1523 if (lpwhr
->pProxyAuthInfo
)
1525 if (SecIsValidHandle(&lpwhr
->pProxyAuthInfo
->ctx
))
1526 DeleteSecurityContext(&lpwhr
->pProxyAuthInfo
->ctx
);
1527 if (SecIsValidHandle(&lpwhr
->pProxyAuthInfo
->cred
))
1528 FreeCredentialsHandle(&lpwhr
->pProxyAuthInfo
->cred
);
1530 HeapFree(GetProcessHeap(), 0, lpwhr
->pProxyAuthInfo
->auth_data
);
1531 HeapFree(GetProcessHeap(), 0, lpwhr
->pProxyAuthInfo
->scheme
);
1532 HeapFree(GetProcessHeap(), 0, lpwhr
->pProxyAuthInfo
);
1533 lpwhr
->pProxyAuthInfo
= NULL
;
1536 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszPath
);
1537 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVerb
);
1538 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszRawHeaders
);
1539 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVersion
);
1540 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszStatusText
);
1542 for (i
= 0; i
< lpwhr
->nCustHeaders
; i
++)
1544 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[i
].lpszField
);
1545 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[i
].lpszValue
);
1548 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
);
1549 HeapFree(GetProcessHeap(), 0, lpwhr
);
1552 static void HTTPREQ_CloseConnection(object_header_t
*hdr
)
1554 http_request_t
*lpwhr
= (http_request_t
*) hdr
;
1556 TRACE("%p\n",lpwhr
);
1559 if(lpwhr
->gzip_stream
) {
1560 inflateEnd(&lpwhr
->gzip_stream
->zstream
);
1561 HeapFree(GetProcessHeap(), 0, lpwhr
->gzip_stream
);
1562 lpwhr
->gzip_stream
= NULL
;
1566 if (!NETCON_connected(&lpwhr
->netConnection
))
1569 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1570 INTERNET_STATUS_CLOSING_CONNECTION
, 0, 0);
1572 NETCON_close(&lpwhr
->netConnection
);
1574 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1575 INTERNET_STATUS_CONNECTION_CLOSED
, 0, 0);
1578 static BOOL
HTTP_GetRequestURL(http_request_t
*req
, LPWSTR buf
)
1580 LPHTTPHEADERW host_header
;
1582 static const WCHAR formatW
[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1584 host_header
= HTTP_GetHeader(req
, hostW
);
1588 sprintfW(buf
, formatW
, host_header
->lpszValue
, req
->lpszPath
); /* FIXME */
1592 static BOOL
HTTP_KeepAlive(http_request_t
*lpwhr
)
1594 WCHAR szVersion
[10];
1595 WCHAR szConnectionResponse
[20];
1596 DWORD dwBufferSize
= sizeof(szVersion
);
1597 BOOL keepalive
= FALSE
;
1599 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1600 * the connection is keep-alive by default */
1601 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_VERSION
, szVersion
,
1602 &dwBufferSize
, NULL
) &&
1603 !strcmpiW(szVersion
, g_szHttp1_1
))
1608 dwBufferSize
= sizeof(szConnectionResponse
);
1609 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_PROXY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) ||
1610 HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
))
1612 keepalive
= !strcmpiW(szConnectionResponse
, szKeepAlive
);
1618 static DWORD
HTTPREQ_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
1620 http_request_t
*req
= (http_request_t
*)hdr
;
1623 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO
:
1625 http_session_t
*lpwhs
= req
->lpHttpSession
;
1626 INTERNET_DIAGNOSTIC_SOCKET_INFO
*info
= buffer
;
1628 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1630 if (*size
< sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
))
1631 return ERROR_INSUFFICIENT_BUFFER
;
1632 *size
= sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
);
1633 /* FIXME: can't get a SOCKET from our connection since we don't use
1637 /* FIXME: get source port from req->netConnection */
1638 info
->SourcePort
= 0;
1639 info
->DestPort
= lpwhs
->nHostPort
;
1641 if (HTTP_KeepAlive(req
))
1642 info
->Flags
|= IDSI_FLAG_KEEP_ALIVE
;
1643 if (lpwhs
->lpAppInfo
->lpszProxy
&& lpwhs
->lpAppInfo
->lpszProxy
[0] != 0)
1644 info
->Flags
|= IDSI_FLAG_PROXY
;
1645 if (req
->netConnection
.useSSL
)
1646 info
->Flags
|= IDSI_FLAG_SECURE
;
1648 return ERROR_SUCCESS
;
1651 case INTERNET_OPTION_SECURITY_FLAGS
:
1653 http_session_t
*lpwhs
;
1654 lpwhs
= req
->lpHttpSession
;
1656 if (*size
< sizeof(ULONG
))
1657 return ERROR_INSUFFICIENT_BUFFER
;
1659 *size
= sizeof(DWORD
);
1660 if (lpwhs
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1661 *(DWORD
*)buffer
= SECURITY_FLAG_SECURE
;
1663 *(DWORD
*)buffer
= 0;
1664 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD
*)buffer
);
1665 return ERROR_SUCCESS
;
1668 case INTERNET_OPTION_HANDLE_TYPE
:
1669 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1671 if (*size
< sizeof(ULONG
))
1672 return ERROR_INSUFFICIENT_BUFFER
;
1674 *size
= sizeof(DWORD
);
1675 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_HTTP_REQUEST
;
1676 return ERROR_SUCCESS
;
1678 case INTERNET_OPTION_URL
: {
1679 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1684 static const WCHAR httpW
[] = {'h','t','t','p',':','/','/',0};
1686 TRACE("INTERNET_OPTION_URL\n");
1688 host
= HTTP_GetHeader(req
, hostW
);
1689 strcpyW(url
, httpW
);
1690 strcatW(url
, host
->lpszValue
);
1691 if (NULL
!= (pch
= strchrW(url
+ strlenW(httpW
), ':')))
1693 strcatW(url
, req
->lpszPath
);
1695 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url
));
1698 len
= (strlenW(url
)+1) * sizeof(WCHAR
);
1700 return ERROR_INSUFFICIENT_BUFFER
;
1703 strcpyW(buffer
, url
);
1704 return ERROR_SUCCESS
;
1706 len
= WideCharToMultiByte(CP_ACP
, 0, url
, -1, buffer
, *size
, NULL
, NULL
);
1708 return ERROR_INSUFFICIENT_BUFFER
;
1711 return ERROR_SUCCESS
;
1715 case INTERNET_OPTION_CACHE_TIMESTAMPS
: {
1716 INTERNET_CACHE_ENTRY_INFOW
*info
;
1717 INTERNET_CACHE_TIMESTAMPS
*ts
= buffer
;
1718 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1719 DWORD nbytes
, error
;
1722 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1724 if (*size
< sizeof(*ts
))
1726 *size
= sizeof(*ts
);
1727 return ERROR_INSUFFICIENT_BUFFER
;
1730 HTTP_GetRequestURL(req
, url
);
1731 ret
= GetUrlCacheEntryInfoW(url
, NULL
, &nbytes
);
1732 error
= GetLastError();
1733 if (!ret
&& error
== ERROR_INSUFFICIENT_BUFFER
)
1735 if (!(info
= HeapAlloc(GetProcessHeap(), 0, nbytes
)))
1736 return ERROR_OUTOFMEMORY
;
1738 GetUrlCacheEntryInfoW(url
, info
, &nbytes
);
1740 ts
->ftExpires
= info
->ExpireTime
;
1741 ts
->ftLastModified
= info
->LastModifiedTime
;
1743 HeapFree(GetProcessHeap(), 0, info
);
1744 *size
= sizeof(*ts
);
1745 return ERROR_SUCCESS
;
1750 case INTERNET_OPTION_DATAFILE_NAME
: {
1753 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1755 if(!req
->lpszCacheFile
) {
1757 return ERROR_INTERNET_ITEM_NOT_FOUND
;
1761 req_size
= (lstrlenW(req
->lpszCacheFile
)+1) * sizeof(WCHAR
);
1762 if(*size
< req_size
)
1763 return ERROR_INSUFFICIENT_BUFFER
;
1766 memcpy(buffer
, req
->lpszCacheFile
, *size
);
1767 return ERROR_SUCCESS
;
1769 req_size
= WideCharToMultiByte(CP_ACP
, 0, req
->lpszCacheFile
, -1, NULL
, 0, NULL
, NULL
);
1770 if (req_size
> *size
)
1771 return ERROR_INSUFFICIENT_BUFFER
;
1773 *size
= WideCharToMultiByte(CP_ACP
, 0, req
->lpszCacheFile
,
1774 -1, buffer
, *size
, NULL
, NULL
);
1775 return ERROR_SUCCESS
;
1779 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT
: {
1780 PCCERT_CONTEXT context
;
1782 if(*size
< sizeof(INTERNET_CERTIFICATE_INFOW
)) {
1783 *size
= sizeof(INTERNET_CERTIFICATE_INFOW
);
1784 return ERROR_INSUFFICIENT_BUFFER
;
1787 context
= (PCCERT_CONTEXT
)NETCON_GetCert(&(req
->netConnection
));
1789 INTERNET_CERTIFICATE_INFOW
*info
= (INTERNET_CERTIFICATE_INFOW
*)buffer
;
1792 memset(info
, 0, sizeof(INTERNET_CERTIFICATE_INFOW
));
1793 info
->ftExpiry
= context
->pCertInfo
->NotAfter
;
1794 info
->ftStart
= context
->pCertInfo
->NotBefore
;
1796 len
= CertNameToStrW(context
->dwCertEncodingType
,
1797 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1798 info
->lpszSubjectInfo
= LocalAlloc(0, len
*sizeof(WCHAR
));
1799 if(info
->lpszSubjectInfo
)
1800 CertNameToStrW(context
->dwCertEncodingType
,
1801 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
,
1802 info
->lpszSubjectInfo
, len
);
1803 len
= CertNameToStrW(context
->dwCertEncodingType
,
1804 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1805 info
->lpszIssuerInfo
= LocalAlloc(0, len
*sizeof(WCHAR
));
1806 if (info
->lpszIssuerInfo
)
1807 CertNameToStrW(context
->dwCertEncodingType
,
1808 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
,
1809 info
->lpszIssuerInfo
, len
);
1811 INTERNET_CERTIFICATE_INFOA
*infoA
= (INTERNET_CERTIFICATE_INFOA
*)info
;
1813 len
= CertNameToStrA(context
->dwCertEncodingType
,
1814 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1815 infoA
->lpszSubjectInfo
= LocalAlloc(0, len
);
1816 if(infoA
->lpszSubjectInfo
)
1817 CertNameToStrA(context
->dwCertEncodingType
,
1818 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
,
1819 infoA
->lpszSubjectInfo
, len
);
1820 len
= CertNameToStrA(context
->dwCertEncodingType
,
1821 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1822 infoA
->lpszIssuerInfo
= LocalAlloc(0, len
);
1823 if(infoA
->lpszIssuerInfo
)
1824 CertNameToStrA(context
->dwCertEncodingType
,
1825 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
,
1826 infoA
->lpszIssuerInfo
, len
);
1830 * Contrary to MSDN, these do not appear to be set.
1832 * lpszSignatureAlgName
1833 * lpszEncryptionAlgName
1836 CertFreeCertificateContext(context
);
1837 return ERROR_SUCCESS
;
1842 return INET_QueryOption(option
, buffer
, size
, unicode
);
1845 static DWORD
HTTPREQ_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
1847 http_request_t
*req
= (http_request_t
*)hdr
;
1850 case INTERNET_OPTION_SEND_TIMEOUT
:
1851 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
1852 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1854 if (size
!= sizeof(DWORD
))
1855 return ERROR_INVALID_PARAMETER
;
1857 return NETCON_set_timeout(&req
->netConnection
, option
== INTERNET_OPTION_SEND_TIMEOUT
,
1860 case INTERNET_OPTION_USERNAME
:
1861 HeapFree(GetProcessHeap(), 0, req
->lpHttpSession
->lpszUserName
);
1862 if (!(req
->lpHttpSession
->lpszUserName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
1863 return ERROR_SUCCESS
;
1865 case INTERNET_OPTION_PASSWORD
:
1866 HeapFree(GetProcessHeap(), 0, req
->lpHttpSession
->lpszPassword
);
1867 if (!(req
->lpHttpSession
->lpszPassword
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
1868 return ERROR_SUCCESS
;
1869 case INTERNET_OPTION_HTTP_DECODING
:
1870 if(size
!= sizeof(BOOL
))
1871 return ERROR_INVALID_PARAMETER
;
1872 req
->decoding
= *(BOOL
*)buffer
;
1873 return ERROR_SUCCESS
;
1876 return ERROR_INTERNET_INVALID_OPTION
;
1879 /* read some more data into the read buffer (the read section must be held) */
1880 static BOOL
read_more_data( http_request_t
*req
, int maxlen
)
1886 /* move existing data to the start of the buffer */
1888 memmove( req
->read_buf
, req
->read_buf
+ req
->read_pos
, req
->read_size
);
1892 if (maxlen
== -1) maxlen
= sizeof(req
->read_buf
);
1894 if(!NETCON_recv( &req
->netConnection
, req
->read_buf
+ req
->read_size
,
1895 maxlen
- req
->read_size
, 0, &len
))
1898 req
->read_size
+= len
;
1902 /* remove some amount of data from the read buffer (the read section must be held) */
1903 static void remove_data( http_request_t
*req
, int count
)
1905 if (!(req
->read_size
-= count
)) req
->read_pos
= 0;
1906 else req
->read_pos
+= count
;
1909 static BOOL
read_line( http_request_t
*req
, LPSTR buffer
, DWORD
*len
)
1911 int count
, bytes_read
, pos
= 0;
1913 EnterCriticalSection( &req
->read_section
);
1916 BYTE
*eol
= memchr( req
->read_buf
+ req
->read_pos
, '\n', req
->read_size
);
1920 count
= eol
- (req
->read_buf
+ req
->read_pos
);
1921 bytes_read
= count
+ 1;
1923 else count
= bytes_read
= req
->read_size
;
1925 count
= min( count
, *len
- pos
);
1926 memcpy( buffer
+ pos
, req
->read_buf
+ req
->read_pos
, count
);
1928 remove_data( req
, bytes_read
);
1931 if (!read_more_data( req
, -1 ) || !req
->read_size
)
1934 TRACE( "returning empty string\n" );
1935 LeaveCriticalSection( &req
->read_section
);
1939 LeaveCriticalSection( &req
->read_section
);
1943 if (pos
&& buffer
[pos
- 1] == '\r') pos
--;
1946 buffer
[*len
- 1] = 0;
1947 TRACE( "returning %s\n", debugstr_a(buffer
));
1951 /* discard data contents until we reach end of line (the read section must be held) */
1952 static BOOL
discard_eol( http_request_t
*req
)
1956 BYTE
*eol
= memchr( req
->read_buf
+ req
->read_pos
, '\n', req
->read_size
);
1959 remove_data( req
, (eol
+ 1) - (req
->read_buf
+ req
->read_pos
) );
1962 req
->read_pos
= req
->read_size
= 0; /* discard everything */
1963 if (!read_more_data( req
, -1 )) return FALSE
;
1964 } while (req
->read_size
);
1968 /* read the size of the next chunk (the read section must be held) */
1969 static BOOL
start_next_chunk( http_request_t
*req
)
1971 DWORD chunk_size
= 0;
1973 if (!req
->dwContentLength
) return TRUE
;
1974 if (req
->dwContentLength
== req
->dwContentRead
)
1976 /* read terminator for the previous chunk */
1977 if (!discard_eol( req
)) return FALSE
;
1978 req
->dwContentLength
= ~0u;
1979 req
->dwContentRead
= 0;
1983 while (req
->read_size
)
1985 char ch
= req
->read_buf
[req
->read_pos
];
1986 if (ch
>= '0' && ch
<= '9') chunk_size
= chunk_size
* 16 + ch
- '0';
1987 else if (ch
>= 'a' && ch
<= 'f') chunk_size
= chunk_size
* 16 + ch
- 'a' + 10;
1988 else if (ch
>= 'A' && ch
<= 'F') chunk_size
= chunk_size
* 16 + ch
- 'A' + 10;
1989 else if (ch
== ';' || ch
== '\r' || ch
== '\n')
1991 TRACE( "reading %u byte chunk\n", chunk_size
);
1992 req
->dwContentLength
= chunk_size
;
1993 req
->dwContentRead
= 0;
1994 if (!discard_eol( req
)) return FALSE
;
1997 remove_data( req
, 1 );
1999 if (!read_more_data( req
, -1 )) return FALSE
;
2000 if (!req
->read_size
)
2002 req
->dwContentLength
= req
->dwContentRead
= 0;
2008 /* check if we have reached the end of the data to read (the read section must be held) */
2009 static BOOL
end_of_read_data( http_request_t
*req
)
2011 if (req
->gzip_stream
) return req
->gzip_stream
->end_of_data
&& !req
->gzip_stream
->buf_size
;
2012 if (req
->read_chunked
) return (req
->dwContentLength
== 0);
2013 if (req
->dwContentLength
== ~0u) return FALSE
;
2014 return (req
->dwContentLength
== req
->dwContentRead
);
2017 /* fetch some more data into the read buffer (the read section must be held) */
2018 static BOOL
refill_buffer( http_request_t
*req
)
2020 int len
= sizeof(req
->read_buf
);
2022 if (req
->read_chunked
&& (req
->dwContentLength
== ~0u || req
->dwContentLength
== req
->dwContentRead
))
2024 if (!start_next_chunk( req
)) return FALSE
;
2027 if (req
->dwContentLength
!= ~0u) len
= min( len
, req
->dwContentLength
- req
->dwContentRead
);
2028 if (len
<= req
->read_size
) return TRUE
;
2030 if (!read_more_data( req
, len
)) return FALSE
;
2031 if (!req
->read_size
) req
->dwContentLength
= req
->dwContentRead
= 0;
2035 static DWORD
read_gzip_data(http_request_t
*req
, BYTE
*buf
, int size
, BOOL sync
, int *read_ret
)
2037 DWORD ret
= ERROR_SUCCESS
;
2041 z_stream
*zstream
= &req
->gzip_stream
->zstream
;
2044 while(read
< size
&& !req
->gzip_stream
->end_of_data
) {
2045 if(!req
->read_size
) {
2046 if(!sync
|| !refill_buffer(req
))
2050 zstream
->next_in
= req
->read_buf
+req
->read_pos
;
2051 zstream
->avail_in
= req
->read_size
;
2052 zstream
->next_out
= buf
+read
;
2053 zstream
->avail_out
= size
-read
;
2054 zres
= inflate(zstream
, Z_FULL_FLUSH
);
2055 read
= size
- zstream
->avail_out
;
2056 remove_data(req
, req
->read_size
-zstream
->avail_in
);
2057 if(zres
== Z_STREAM_END
) {
2058 TRACE("end of data\n");
2059 req
->gzip_stream
->end_of_data
= TRUE
;
2060 }else if(zres
!= Z_OK
) {
2061 WARN("inflate failed %d\n", zres
);
2063 ret
= ERROR_INTERNET_DECODING_FAILED
;
2073 static void refill_gzip_buffer(http_request_t
*req
)
2078 if(!req
->gzip_stream
|| !req
->read_size
|| req
->gzip_stream
->buf_size
== sizeof(req
->gzip_stream
->buf
))
2081 if(req
->gzip_stream
->buf_pos
) {
2082 if(req
->gzip_stream
->buf_size
)
2083 memmove(req
->gzip_stream
->buf
, req
->gzip_stream
->buf
+ req
->gzip_stream
->buf_pos
, req
->gzip_stream
->buf_size
);
2084 req
->gzip_stream
->buf_pos
= 0;
2087 res
= read_gzip_data(req
, req
->gzip_stream
->buf
+ req
->gzip_stream
->buf_size
,
2088 sizeof(req
->gzip_stream
->buf
) - req
->gzip_stream
->buf_size
, FALSE
, &len
);
2089 if(res
== ERROR_SUCCESS
)
2090 req
->gzip_stream
->buf_size
+= len
;
2093 /* return the size of data available to be read immediately (the read section must be held) */
2094 static DWORD
get_avail_data( http_request_t
*req
)
2096 if (req
->gzip_stream
) {
2097 refill_gzip_buffer(req
);
2098 return req
->gzip_stream
->buf_size
;
2100 if (req
->read_chunked
&& (req
->dwContentLength
== ~0u || req
->dwContentLength
== req
->dwContentRead
))
2102 return min( req
->read_size
, req
->dwContentLength
- req
->dwContentRead
);
2105 static void HTTP_ReceiveRequestData(http_request_t
*req
, BOOL first_notif
)
2107 INTERNET_ASYNC_RESULT iar
;
2111 EnterCriticalSection( &req
->read_section
);
2112 if (refill_buffer( req
)) {
2113 iar
.dwResult
= (DWORD_PTR
)req
->hdr
.hInternet
;
2114 iar
.dwError
= first_notif
? 0 : get_avail_data(req
);
2117 iar
.dwError
= INTERNET_GetLastError();
2119 LeaveCriticalSection( &req
->read_section
);
2121 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2122 sizeof(INTERNET_ASYNC_RESULT
));
2125 /* read data from the http connection (the read section must be held) */
2126 static DWORD
HTTPREQ_Read(http_request_t
*req
, void *buffer
, DWORD size
, DWORD
*read
, BOOL sync
)
2128 BOOL finished_reading
= FALSE
;
2129 int len
, bytes_read
= 0;
2130 DWORD ret
= ERROR_SUCCESS
;
2132 EnterCriticalSection( &req
->read_section
);
2134 if (req
->read_chunked
&& (req
->dwContentLength
== ~0u || req
->dwContentLength
== req
->dwContentRead
))
2136 if (!start_next_chunk( req
)) goto done
;
2139 if(req
->gzip_stream
) {
2140 if(req
->gzip_stream
->buf_size
) {
2141 bytes_read
= min(req
->gzip_stream
->buf_size
, size
);
2142 memcpy(buffer
, req
->gzip_stream
->buf
+ req
->gzip_stream
->buf_pos
, bytes_read
);
2143 req
->gzip_stream
->buf_pos
+= bytes_read
;
2144 req
->gzip_stream
->buf_size
-= bytes_read
;
2145 }else if(!req
->read_size
&& !req
->gzip_stream
->end_of_data
) {
2149 if(size
> bytes_read
) {
2150 ret
= read_gzip_data(req
, (BYTE
*)buffer
+bytes_read
, size
-bytes_read
, sync
, &len
);
2151 if(ret
== ERROR_SUCCESS
)
2155 finished_reading
= req
->gzip_stream
->end_of_data
&& !req
->gzip_stream
->buf_size
;
2157 if (req
->dwContentLength
!= ~0u) size
= min( size
, req
->dwContentLength
- req
->dwContentRead
);
2159 if (req
->read_size
) {
2160 bytes_read
= min( req
->read_size
, size
);
2161 memcpy( buffer
, req
->read_buf
+ req
->read_pos
, bytes_read
);
2162 remove_data( req
, bytes_read
);
2165 if (size
> bytes_read
&& (!bytes_read
|| sync
)) {
2166 if (NETCON_recv( &req
->netConnection
, (char *)buffer
+ bytes_read
, size
- bytes_read
,
2167 sync
? MSG_WAITALL
: 0, &len
))
2169 /* always return success, even if the network layer returns an error */
2172 finished_reading
= !bytes_read
&& req
->dwContentRead
== req
->dwContentLength
;
2175 req
->dwContentRead
+= bytes_read
;
2178 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read
, req
->dwContentRead
, req
->dwContentLength
);
2179 LeaveCriticalSection( &req
->read_section
);
2181 if(ret
== ERROR_SUCCESS
&& req
->lpszCacheFile
) {
2183 DWORD dwBytesWritten
;
2185 res
= WriteFile(req
->hCacheFile
, buffer
, bytes_read
, &dwBytesWritten
, NULL
);
2187 WARN("WriteFile failed: %u\n", GetLastError());
2190 if(finished_reading
)
2191 HTTP_FinishedReading(req
);
2197 static DWORD
HTTPREQ_ReadFile(object_header_t
*hdr
, void *buffer
, DWORD size
, DWORD
*read
)
2199 http_request_t
*req
= (http_request_t
*)hdr
;
2200 return HTTPREQ_Read(req
, buffer
, size
, read
, TRUE
);
2203 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST
*workRequest
)
2205 struct WORKREQ_INTERNETREADFILEEXA
const *data
= &workRequest
->u
.InternetReadFileExA
;
2206 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2207 INTERNET_ASYNC_RESULT iar
;
2210 TRACE("INTERNETREADFILEEXA %p\n", workRequest
->hdr
);
2212 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2213 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2215 iar
.dwResult
= res
== ERROR_SUCCESS
;
2218 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2219 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2220 sizeof(INTERNET_ASYNC_RESULT
));
2223 static DWORD
HTTPREQ_ReadFileExA(object_header_t
*hdr
, INTERNET_BUFFERSA
*buffers
,
2224 DWORD flags
, DWORD_PTR context
)
2226 http_request_t
*req
= (http_request_t
*)hdr
;
2229 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2230 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2232 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2233 return ERROR_INVALID_PARAMETER
;
2235 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2237 if ((hdr
->dwFlags
& INTERNET_FLAG_ASYNC
) && !get_avail_data(req
))
2239 WORKREQUEST workRequest
;
2241 if (TryEnterCriticalSection( &req
->read_section
))
2243 if (get_avail_data(req
))
2245 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2246 &buffers
->dwBufferLength
, FALSE
);
2247 LeaveCriticalSection( &req
->read_section
);
2250 LeaveCriticalSection( &req
->read_section
);
2253 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExAProc
;
2254 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2255 workRequest
.u
.InternetReadFileExA
.lpBuffersOut
= buffers
;
2257 INTERNET_AsyncCall(&workRequest
);
2259 return ERROR_IO_PENDING
;
2262 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
, &buffers
->dwBufferLength
,
2263 !(flags
& IRF_NO_WAIT
));
2266 if (res
== ERROR_SUCCESS
) {
2267 DWORD size
= buffers
->dwBufferLength
;
2268 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2269 &size
, sizeof(size
));
2275 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST
*workRequest
)
2277 struct WORKREQ_INTERNETREADFILEEXW
const *data
= &workRequest
->u
.InternetReadFileExW
;
2278 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2279 INTERNET_ASYNC_RESULT iar
;
2282 TRACE("INTERNETREADFILEEXW %p\n", workRequest
->hdr
);
2284 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2285 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2287 iar
.dwResult
= res
== ERROR_SUCCESS
;
2290 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2291 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2292 sizeof(INTERNET_ASYNC_RESULT
));
2295 static DWORD
HTTPREQ_ReadFileExW(object_header_t
*hdr
, INTERNET_BUFFERSW
*buffers
,
2296 DWORD flags
, DWORD_PTR context
)
2299 http_request_t
*req
= (http_request_t
*)hdr
;
2302 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2303 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2305 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2306 return ERROR_INVALID_PARAMETER
;
2308 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2310 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2312 WORKREQUEST workRequest
;
2314 if (TryEnterCriticalSection( &req
->read_section
))
2316 if (get_avail_data(req
))
2318 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2319 &buffers
->dwBufferLength
, FALSE
);
2320 LeaveCriticalSection( &req
->read_section
);
2323 LeaveCriticalSection( &req
->read_section
);
2326 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExWProc
;
2327 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2328 workRequest
.u
.InternetReadFileExW
.lpBuffersOut
= buffers
;
2330 INTERNET_AsyncCall(&workRequest
);
2332 return ERROR_IO_PENDING
;
2335 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
, &buffers
->dwBufferLength
,
2336 !(flags
& IRF_NO_WAIT
));
2339 if (res
== ERROR_SUCCESS
) {
2340 DWORD size
= buffers
->dwBufferLength
;
2341 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2342 &size
, sizeof(size
));
2348 static BOOL
HTTPREQ_WriteFile(object_header_t
*hdr
, const void *buffer
, DWORD size
, DWORD
*written
)
2351 http_request_t
*lpwhr
= (http_request_t
*)hdr
;
2353 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
2356 if ((ret
= NETCON_send(&lpwhr
->netConnection
, buffer
, size
, 0, (LPINT
)written
)))
2357 lpwhr
->dwBytesWritten
+= *written
;
2359 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_SENT
, written
, sizeof(DWORD
));
2363 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST
*workRequest
)
2365 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2367 HTTP_ReceiveRequestData(req
, FALSE
);
2370 static DWORD
HTTPREQ_QueryDataAvailable(object_header_t
*hdr
, DWORD
*available
, DWORD flags
, DWORD_PTR ctx
)
2372 http_request_t
*req
= (http_request_t
*)hdr
;
2374 TRACE("(%p %p %x %lx)\n", req
, available
, flags
, ctx
);
2376 if (req
->lpHttpSession
->lpAppInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
2378 WORKREQUEST workRequest
;
2380 /* never wait, if we can't enter the section we queue an async request right away */
2381 if (TryEnterCriticalSection( &req
->read_section
))
2383 if ((*available
= get_avail_data( req
))) goto done
;
2384 if (end_of_read_data( req
)) goto done
;
2385 LeaveCriticalSection( &req
->read_section
);
2388 workRequest
.asyncproc
= HTTPREQ_AsyncQueryDataAvailableProc
;
2389 workRequest
.hdr
= WININET_AddRef( &req
->hdr
);
2391 INTERNET_AsyncCall(&workRequest
);
2393 return ERROR_IO_PENDING
;
2396 EnterCriticalSection( &req
->read_section
);
2398 if (!(*available
= get_avail_data( req
)) && !end_of_read_data( req
))
2400 refill_buffer( req
);
2401 *available
= get_avail_data( req
);
2405 if (*available
== sizeof(req
->read_buf
) && !req
->gzip_stream
) /* check if we have even more pending in the socket */
2408 if (NETCON_query_data_available(&req
->netConnection
, &extra
))
2409 *available
= min( *available
+ extra
, req
->dwContentLength
- req
->dwContentRead
);
2411 LeaveCriticalSection( &req
->read_section
);
2413 TRACE( "returning %u\n", *available
);
2414 return ERROR_SUCCESS
;
2417 static const object_vtbl_t HTTPREQVtbl
= {
2419 HTTPREQ_CloseConnection
,
2420 HTTPREQ_QueryOption
,
2423 HTTPREQ_ReadFileExA
,
2424 HTTPREQ_ReadFileExW
,
2426 HTTPREQ_QueryDataAvailable
,
2430 /***********************************************************************
2431 * HTTP_HttpOpenRequestW (internal)
2433 * Open a HTTP request handle
2436 * HINTERNET a HTTP request handle on success
2440 HINTERNET WINAPI
HTTP_HttpOpenRequestW(http_session_t
*lpwhs
,
2441 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
2442 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
2443 DWORD dwFlags
, DWORD_PTR dwContext
)
2445 appinfo_t
*hIC
= NULL
;
2446 http_request_t
*lpwhr
;
2447 LPWSTR lpszHostName
= NULL
;
2448 HINTERNET handle
= NULL
;
2449 static const WCHAR szHostForm
[] = {'%','s',':','%','u',0};
2454 assert( lpwhs
->hdr
.htype
== WH_HHTTPSESSION
);
2455 hIC
= lpwhs
->lpAppInfo
;
2457 lpwhr
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(http_request_t
));
2460 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
2463 lpwhr
->hdr
.htype
= WH_HHTTPREQ
;
2464 lpwhr
->hdr
.vtbl
= &HTTPREQVtbl
;
2465 lpwhr
->hdr
.dwFlags
= dwFlags
;
2466 lpwhr
->hdr
.dwContext
= dwContext
;
2467 lpwhr
->hdr
.refs
= 1;
2468 lpwhr
->hdr
.lpfnStatusCB
= lpwhs
->hdr
.lpfnStatusCB
;
2469 lpwhr
->hdr
.dwInternalFlags
= lpwhs
->hdr
.dwInternalFlags
& INET_CALLBACKW
;
2470 lpwhr
->dwContentLength
= ~0u;
2471 InitializeCriticalSection( &lpwhr
->read_section
);
2473 WININET_AddRef( &lpwhs
->hdr
);
2474 lpwhr
->lpHttpSession
= lpwhs
;
2475 list_add_head( &lpwhs
->hdr
.children
, &lpwhr
->hdr
.entry
);
2477 lpszHostName
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
) *
2478 (strlenW(lpwhs
->lpszHostName
) + 7 /* length of ":65535" + 1 */));
2479 if (NULL
== lpszHostName
)
2481 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
2485 handle
= WININET_AllocHandle( &lpwhr
->hdr
);
2488 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
2492 if (!NETCON_init(&lpwhr
->netConnection
, dwFlags
& INTERNET_FLAG_SECURE
))
2494 InternetCloseHandle( handle
);
2499 if (lpszObjectName
&& *lpszObjectName
) {
2503 rc
= UrlEscapeW(lpszObjectName
, NULL
, &len
, URL_ESCAPE_SPACES_ONLY
);
2504 if (rc
!= E_POINTER
)
2505 len
= strlenW(lpszObjectName
)+1;
2506 lpwhr
->lpszPath
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
2507 rc
= UrlEscapeW(lpszObjectName
, lpwhr
->lpszPath
, &len
,
2508 URL_ESCAPE_SPACES_ONLY
);
2511 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName
),rc
);
2512 strcpyW(lpwhr
->lpszPath
,lpszObjectName
);
2515 static const WCHAR slashW
[] = {'/',0};
2517 lpwhr
->lpszPath
= heap_strdupW(slashW
);
2520 if (lpszReferrer
&& *lpszReferrer
)
2521 HTTP_ProcessHeader(lpwhr
, HTTP_REFERER
, lpszReferrer
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2523 if (lpszAcceptTypes
)
2526 for (i
= 0; lpszAcceptTypes
[i
]; i
++)
2528 if (!*lpszAcceptTypes
[i
]) continue;
2529 HTTP_ProcessHeader(lpwhr
, HTTP_ACCEPT
, lpszAcceptTypes
[i
],
2530 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
|
2531 HTTP_ADDHDR_FLAG_REQ
|
2532 (i
== 0 ? HTTP_ADDHDR_FLAG_REPLACE
: 0));
2536 lpwhr
->lpszVerb
= heap_strdupW(lpszVerb
&& *lpszVerb
? lpszVerb
: szGET
);
2537 lpwhr
->lpszVersion
= heap_strdupW(lpszVersion
? lpszVersion
: g_szHttp1_1
);
2539 if (lpwhs
->nHostPort
!= INTERNET_INVALID_PORT_NUMBER
&&
2540 lpwhs
->nHostPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
2541 lpwhs
->nHostPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
2543 sprintfW(lpszHostName
, szHostForm
, lpwhs
->lpszHostName
, lpwhs
->nHostPort
);
2544 HTTP_ProcessHeader(lpwhr
, hostW
, lpszHostName
,
2545 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2548 HTTP_ProcessHeader(lpwhr
, hostW
, lpwhs
->lpszHostName
,
2549 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2551 if (lpwhs
->nServerPort
== INTERNET_INVALID_PORT_NUMBER
)
2552 lpwhs
->nServerPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
2553 INTERNET_DEFAULT_HTTPS_PORT
:
2554 INTERNET_DEFAULT_HTTP_PORT
);
2556 if (lpwhs
->nHostPort
== INTERNET_INVALID_PORT_NUMBER
)
2557 lpwhs
->nHostPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
2558 INTERNET_DEFAULT_HTTPS_PORT
:
2559 INTERNET_DEFAULT_HTTP_PORT
);
2561 if (NULL
!= hIC
->lpszProxy
&& hIC
->lpszProxy
[0] != 0)
2562 HTTP_DealWithProxy( hIC
, lpwhs
, lpwhr
);
2564 INTERNET_SendCallback(&lpwhs
->hdr
, dwContext
,
2565 INTERNET_STATUS_HANDLE_CREATED
, &handle
,
2569 HeapFree(GetProcessHeap(), 0, lpszHostName
);
2571 WININET_Release( &lpwhr
->hdr
);
2573 TRACE("<-- %p (%p)\n", handle
, lpwhr
);
2577 /* read any content returned by the server so that the connection can be
2579 static void HTTP_DrainContent(http_request_t
*req
)
2583 if (!NETCON_connected(&req
->netConnection
)) return;
2585 if (req
->dwContentLength
== -1)
2587 NETCON_close(&req
->netConnection
);
2590 if (!strcmpW(req
->lpszVerb
, szHEAD
)) return;
2595 if (HTTPREQ_Read(req
, buffer
, sizeof(buffer
), &bytes_read
, TRUE
) != ERROR_SUCCESS
)
2597 } while (bytes_read
);
2600 static const LPCWSTR header_lookup
[] = {
2601 szMime_Version
, /* HTTP_QUERY_MIME_VERSION = 0 */
2602 szContent_Type
, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2603 szContent_Transfer_Encoding
,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2604 szContent_ID
, /* HTTP_QUERY_CONTENT_ID = 3 */
2605 NULL
, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2606 szContent_Length
, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2607 szContent_Language
, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2608 szAllow
, /* HTTP_QUERY_ALLOW = 7 */
2609 szPublic
, /* HTTP_QUERY_PUBLIC = 8 */
2610 szDate
, /* HTTP_QUERY_DATE = 9 */
2611 szExpires
, /* HTTP_QUERY_EXPIRES = 10 */
2612 szLast_Modified
, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2613 NULL
, /* HTTP_QUERY_MESSAGE_ID = 12 */
2614 szURI
, /* HTTP_QUERY_URI = 13 */
2615 szFrom
, /* HTTP_QUERY_DERIVED_FROM = 14 */
2616 NULL
, /* HTTP_QUERY_COST = 15 */
2617 NULL
, /* HTTP_QUERY_LINK = 16 */
2618 szPragma
, /* HTTP_QUERY_PRAGMA = 17 */
2619 NULL
, /* HTTP_QUERY_VERSION = 18 */
2620 szStatus
, /* HTTP_QUERY_STATUS_CODE = 19 */
2621 NULL
, /* HTTP_QUERY_STATUS_TEXT = 20 */
2622 NULL
, /* HTTP_QUERY_RAW_HEADERS = 21 */
2623 NULL
, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2624 szConnection
, /* HTTP_QUERY_CONNECTION = 23 */
2625 szAccept
, /* HTTP_QUERY_ACCEPT = 24 */
2626 szAccept_Charset
, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2627 szAccept_Encoding
, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2628 szAccept_Language
, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2629 szAuthorization
, /* HTTP_QUERY_AUTHORIZATION = 28 */
2630 szContent_Encoding
, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2631 NULL
, /* HTTP_QUERY_FORWARDED = 30 */
2632 NULL
, /* HTTP_QUERY_FROM = 31 */
2633 szIf_Modified_Since
, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2634 szLocation
, /* HTTP_QUERY_LOCATION = 33 */
2635 NULL
, /* HTTP_QUERY_ORIG_URI = 34 */
2636 szReferer
, /* HTTP_QUERY_REFERER = 35 */
2637 szRetry_After
, /* HTTP_QUERY_RETRY_AFTER = 36 */
2638 szServer
, /* HTTP_QUERY_SERVER = 37 */
2639 NULL
, /* HTTP_TITLE = 38 */
2640 szUser_Agent
, /* HTTP_QUERY_USER_AGENT = 39 */
2641 szWWW_Authenticate
, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2642 szProxy_Authenticate
, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2643 szAccept_Ranges
, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2644 szSet_Cookie
, /* HTTP_QUERY_SET_COOKIE = 43 */
2645 szCookie
, /* HTTP_QUERY_COOKIE = 44 */
2646 NULL
, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2647 NULL
, /* HTTP_QUERY_REFRESH = 46 */
2648 NULL
, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2649 szAge
, /* HTTP_QUERY_AGE = 48 */
2650 szCache_Control
, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2651 szContent_Base
, /* HTTP_QUERY_CONTENT_BASE = 50 */
2652 szContent_Location
, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2653 szContent_MD5
, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2654 szContent_Range
, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2655 szETag
, /* HTTP_QUERY_ETAG = 54 */
2656 hostW
, /* HTTP_QUERY_HOST = 55 */
2657 szIf_Match
, /* HTTP_QUERY_IF_MATCH = 56 */
2658 szIf_None_Match
, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2659 szIf_Range
, /* HTTP_QUERY_IF_RANGE = 58 */
2660 szIf_Unmodified_Since
, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2661 szMax_Forwards
, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2662 szProxy_Authorization
, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2663 szRange
, /* HTTP_QUERY_RANGE = 62 */
2664 szTransfer_Encoding
, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2665 szUpgrade
, /* HTTP_QUERY_UPGRADE = 64 */
2666 szVary
, /* HTTP_QUERY_VARY = 65 */
2667 szVia
, /* HTTP_QUERY_VIA = 66 */
2668 szWarning
, /* HTTP_QUERY_WARNING = 67 */
2669 szExpect
, /* HTTP_QUERY_EXPECT = 68 */
2670 szProxy_Connection
, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2671 szUnless_Modified_Since
, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2674 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2676 /***********************************************************************
2677 * HTTP_HttpQueryInfoW (internal)
2679 static BOOL
HTTP_HttpQueryInfoW(http_request_t
*lpwhr
, DWORD dwInfoLevel
,
2680 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
2682 LPHTTPHEADERW lphttpHdr
= NULL
;
2683 BOOL bSuccess
= FALSE
;
2684 BOOL request_only
= dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
;
2685 INT requested_index
= lpdwIndex
? *lpdwIndex
: 0;
2686 DWORD level
= (dwInfoLevel
& ~HTTP_QUERY_MODIFIER_FLAGS_MASK
);
2689 /* Find requested header structure */
2692 case HTTP_QUERY_CUSTOM
:
2693 if (!lpBuffer
) return FALSE
;
2694 index
= HTTP_GetCustomHeaderIndex(lpwhr
, lpBuffer
, requested_index
, request_only
);
2696 case HTTP_QUERY_RAW_HEADERS_CRLF
:
2703 headers
= HTTP_BuildHeaderRequestString(lpwhr
, lpwhr
->lpszVerb
, lpwhr
->lpszPath
, lpwhr
->lpszVersion
);
2705 headers
= lpwhr
->lpszRawHeaders
;
2708 len
= strlenW(headers
) * sizeof(WCHAR
);
2710 if (len
+ sizeof(WCHAR
) > *lpdwBufferLength
)
2712 len
+= sizeof(WCHAR
);
2713 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER
);
2719 memcpy(lpBuffer
, headers
, len
+ sizeof(WCHAR
));
2722 len
= strlenW(szCrLf
) * sizeof(WCHAR
);
2723 memcpy(lpBuffer
, szCrLf
, sizeof(szCrLf
));
2725 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
/ sizeof(WCHAR
)));
2728 *lpdwBufferLength
= len
;
2731 HeapFree(GetProcessHeap(), 0, headers
);
2734 case HTTP_QUERY_RAW_HEADERS
:
2736 LPWSTR
* ppszRawHeaderLines
= HTTP_Tokenize(lpwhr
->lpszRawHeaders
, szCrLf
);
2738 LPWSTR pszString
= lpBuffer
;
2740 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
2741 size
+= strlenW(ppszRawHeaderLines
[i
]) + 1;
2743 if (size
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
2745 HTTP_FreeTokens(ppszRawHeaderLines
);
2746 *lpdwBufferLength
= (size
+ 1) * sizeof(WCHAR
);
2747 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER
);
2752 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
2754 DWORD len
= strlenW(ppszRawHeaderLines
[i
]);
2755 memcpy(pszString
, ppszRawHeaderLines
[i
], (len
+1)*sizeof(WCHAR
));
2759 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, size
));
2761 *lpdwBufferLength
= size
* sizeof(WCHAR
);
2762 HTTP_FreeTokens(ppszRawHeaderLines
);
2766 case HTTP_QUERY_STATUS_TEXT
:
2767 if (lpwhr
->lpszStatusText
)
2769 DWORD len
= strlenW(lpwhr
->lpszStatusText
);
2770 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
2772 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
2773 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER
);
2778 memcpy(lpBuffer
, lpwhr
->lpszStatusText
, (len
+ 1) * sizeof(WCHAR
));
2779 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
2781 *lpdwBufferLength
= len
* sizeof(WCHAR
);
2785 case HTTP_QUERY_VERSION
:
2786 if (lpwhr
->lpszVersion
)
2788 DWORD len
= strlenW(lpwhr
->lpszVersion
);
2789 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
2791 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
2792 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER
);
2797 memcpy(lpBuffer
, lpwhr
->lpszVersion
, (len
+ 1) * sizeof(WCHAR
));
2798 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
2800 *lpdwBufferLength
= len
* sizeof(WCHAR
);
2804 case HTTP_QUERY_CONTENT_ENCODING
:
2805 index
= HTTP_GetCustomHeaderIndex(lpwhr
, header_lookup
[lpwhr
->gzip_stream
? HTTP_QUERY_CONTENT_TYPE
: level
],
2806 requested_index
,request_only
);
2809 assert (LAST_TABLE_HEADER
== (HTTP_QUERY_UNLESS_MODIFIED_SINCE
+ 1));
2811 if (level
< LAST_TABLE_HEADER
&& header_lookup
[level
])
2812 index
= HTTP_GetCustomHeaderIndex(lpwhr
, header_lookup
[level
],
2813 requested_index
,request_only
);
2817 lphttpHdr
= &lpwhr
->pCustHeaders
[index
];
2819 /* Ensure header satisfies requested attributes */
2821 ((dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
) &&
2822 (~lphttpHdr
->wFlags
& HDR_ISREQUEST
)))
2824 INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND
);
2828 if (lpdwIndex
&& level
!= HTTP_QUERY_STATUS_CODE
) (*lpdwIndex
)++;
2830 /* coalesce value to requested type */
2831 if (dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
&& lpBuffer
)
2833 *(int *)lpBuffer
= atoiW(lphttpHdr
->lpszValue
);
2834 TRACE(" returning number: %d\n", *(int *)lpBuffer
);
2837 else if (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
&& lpBuffer
)
2843 tmpTime
= ConvertTimeString(lphttpHdr
->lpszValue
);
2845 tmpTM
= *gmtime(&tmpTime
);
2846 STHook
= (SYSTEMTIME
*)lpBuffer
;
2847 STHook
->wDay
= tmpTM
.tm_mday
;
2848 STHook
->wHour
= tmpTM
.tm_hour
;
2849 STHook
->wMilliseconds
= 0;
2850 STHook
->wMinute
= tmpTM
.tm_min
;
2851 STHook
->wDayOfWeek
= tmpTM
.tm_wday
;
2852 STHook
->wMonth
= tmpTM
.tm_mon
+ 1;
2853 STHook
->wSecond
= tmpTM
.tm_sec
;
2854 STHook
->wYear
= tmpTM
.tm_year
;
2857 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
2858 STHook
->wYear
, STHook
->wMonth
, STHook
->wDay
, STHook
->wDayOfWeek
,
2859 STHook
->wHour
, STHook
->wMinute
, STHook
->wSecond
, STHook
->wMilliseconds
);
2861 else if (lphttpHdr
->lpszValue
)
2863 DWORD len
= (strlenW(lphttpHdr
->lpszValue
) + 1) * sizeof(WCHAR
);
2865 if (len
> *lpdwBufferLength
)
2867 *lpdwBufferLength
= len
;
2868 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER
);
2873 memcpy(lpBuffer
, lphttpHdr
->lpszValue
, len
);
2874 TRACE(" returning string: %s\n", debugstr_w(lpBuffer
));
2876 *lpdwBufferLength
= len
- sizeof(WCHAR
);
2882 /***********************************************************************
2883 * HttpQueryInfoW (WININET.@)
2885 * Queries for information about an HTTP request
2892 BOOL WINAPI
HttpQueryInfoW(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
2893 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
2895 BOOL bSuccess
= FALSE
;
2896 http_request_t
*lpwhr
;
2898 if (TRACE_ON(wininet
)) {
2899 #define FE(x) { x, #x }
2900 static const wininet_flag_info query_flags
[] = {
2901 FE(HTTP_QUERY_MIME_VERSION
),
2902 FE(HTTP_QUERY_CONTENT_TYPE
),
2903 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING
),
2904 FE(HTTP_QUERY_CONTENT_ID
),
2905 FE(HTTP_QUERY_CONTENT_DESCRIPTION
),
2906 FE(HTTP_QUERY_CONTENT_LENGTH
),
2907 FE(HTTP_QUERY_CONTENT_LANGUAGE
),
2908 FE(HTTP_QUERY_ALLOW
),
2909 FE(HTTP_QUERY_PUBLIC
),
2910 FE(HTTP_QUERY_DATE
),
2911 FE(HTTP_QUERY_EXPIRES
),
2912 FE(HTTP_QUERY_LAST_MODIFIED
),
2913 FE(HTTP_QUERY_MESSAGE_ID
),
2915 FE(HTTP_QUERY_DERIVED_FROM
),
2916 FE(HTTP_QUERY_COST
),
2917 FE(HTTP_QUERY_LINK
),
2918 FE(HTTP_QUERY_PRAGMA
),
2919 FE(HTTP_QUERY_VERSION
),
2920 FE(HTTP_QUERY_STATUS_CODE
),
2921 FE(HTTP_QUERY_STATUS_TEXT
),
2922 FE(HTTP_QUERY_RAW_HEADERS
),
2923 FE(HTTP_QUERY_RAW_HEADERS_CRLF
),
2924 FE(HTTP_QUERY_CONNECTION
),
2925 FE(HTTP_QUERY_ACCEPT
),
2926 FE(HTTP_QUERY_ACCEPT_CHARSET
),
2927 FE(HTTP_QUERY_ACCEPT_ENCODING
),
2928 FE(HTTP_QUERY_ACCEPT_LANGUAGE
),
2929 FE(HTTP_QUERY_AUTHORIZATION
),
2930 FE(HTTP_QUERY_CONTENT_ENCODING
),
2931 FE(HTTP_QUERY_FORWARDED
),
2932 FE(HTTP_QUERY_FROM
),
2933 FE(HTTP_QUERY_IF_MODIFIED_SINCE
),
2934 FE(HTTP_QUERY_LOCATION
),
2935 FE(HTTP_QUERY_ORIG_URI
),
2936 FE(HTTP_QUERY_REFERER
),
2937 FE(HTTP_QUERY_RETRY_AFTER
),
2938 FE(HTTP_QUERY_SERVER
),
2939 FE(HTTP_QUERY_TITLE
),
2940 FE(HTTP_QUERY_USER_AGENT
),
2941 FE(HTTP_QUERY_WWW_AUTHENTICATE
),
2942 FE(HTTP_QUERY_PROXY_AUTHENTICATE
),
2943 FE(HTTP_QUERY_ACCEPT_RANGES
),
2944 FE(HTTP_QUERY_SET_COOKIE
),
2945 FE(HTTP_QUERY_COOKIE
),
2946 FE(HTTP_QUERY_REQUEST_METHOD
),
2947 FE(HTTP_QUERY_REFRESH
),
2948 FE(HTTP_QUERY_CONTENT_DISPOSITION
),
2950 FE(HTTP_QUERY_CACHE_CONTROL
),
2951 FE(HTTP_QUERY_CONTENT_BASE
),
2952 FE(HTTP_QUERY_CONTENT_LOCATION
),
2953 FE(HTTP_QUERY_CONTENT_MD5
),
2954 FE(HTTP_QUERY_CONTENT_RANGE
),
2955 FE(HTTP_QUERY_ETAG
),
2956 FE(HTTP_QUERY_HOST
),
2957 FE(HTTP_QUERY_IF_MATCH
),
2958 FE(HTTP_QUERY_IF_NONE_MATCH
),
2959 FE(HTTP_QUERY_IF_RANGE
),
2960 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE
),
2961 FE(HTTP_QUERY_MAX_FORWARDS
),
2962 FE(HTTP_QUERY_PROXY_AUTHORIZATION
),
2963 FE(HTTP_QUERY_RANGE
),
2964 FE(HTTP_QUERY_TRANSFER_ENCODING
),
2965 FE(HTTP_QUERY_UPGRADE
),
2966 FE(HTTP_QUERY_VARY
),
2968 FE(HTTP_QUERY_WARNING
),
2969 FE(HTTP_QUERY_CUSTOM
)
2971 static const wininet_flag_info modifier_flags
[] = {
2972 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS
),
2973 FE(HTTP_QUERY_FLAG_SYSTEMTIME
),
2974 FE(HTTP_QUERY_FLAG_NUMBER
),
2975 FE(HTTP_QUERY_FLAG_COALESCE
)
2978 DWORD info_mod
= dwInfoLevel
& HTTP_QUERY_MODIFIER_FLAGS_MASK
;
2979 DWORD info
= dwInfoLevel
& HTTP_QUERY_HEADER_MASK
;
2982 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest
, dwInfoLevel
, dwInfoLevel
);
2983 TRACE(" Attribute:");
2984 for (i
= 0; i
< (sizeof(query_flags
) / sizeof(query_flags
[0])); i
++) {
2985 if (query_flags
[i
].val
== info
) {
2986 TRACE(" %s", query_flags
[i
].name
);
2990 if (i
== (sizeof(query_flags
) / sizeof(query_flags
[0]))) {
2991 TRACE(" Unknown (%08x)", info
);
2994 TRACE(" Modifier:");
2995 for (i
= 0; i
< (sizeof(modifier_flags
) / sizeof(modifier_flags
[0])); i
++) {
2996 if (modifier_flags
[i
].val
& info_mod
) {
2997 TRACE(" %s", modifier_flags
[i
].name
);
2998 info_mod
&= ~ modifier_flags
[i
].val
;
3003 TRACE(" Unknown (%08x)", info_mod
);
3008 lpwhr
= (http_request_t
*) WININET_GetObject( hHttpRequest
);
3009 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
3011 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
3015 if (lpBuffer
== NULL
)
3016 *lpdwBufferLength
= 0;
3017 bSuccess
= HTTP_HttpQueryInfoW( lpwhr
, dwInfoLevel
,
3018 lpBuffer
, lpdwBufferLength
, lpdwIndex
);
3022 WININET_Release( &lpwhr
->hdr
);
3024 TRACE("%d <--\n", bSuccess
);
3028 /***********************************************************************
3029 * HttpQueryInfoA (WININET.@)
3031 * Queries for information about an HTTP request
3038 BOOL WINAPI
HttpQueryInfoA(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3039 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3045 if((dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
) ||
3046 (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
))
3048 return HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, lpBuffer
,
3049 lpdwBufferLength
, lpdwIndex
);
3055 len
= (*lpdwBufferLength
)*sizeof(WCHAR
);
3056 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3058 alloclen
= MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, NULL
, 0 ) * sizeof(WCHAR
);
3064 bufferW
= HeapAlloc( GetProcessHeap(), 0, alloclen
);
3065 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3066 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3067 MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, bufferW
, alloclen
/ sizeof(WCHAR
) );
3074 result
= HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, bufferW
,
3078 len
= WideCharToMultiByte( CP_ACP
,0, bufferW
, len
/ sizeof(WCHAR
) + 1,
3079 lpBuffer
, *lpdwBufferLength
, NULL
, NULL
);
3080 *lpdwBufferLength
= len
- 1;
3082 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer
));
3085 /* since the strings being returned from HttpQueryInfoW should be
3086 * only ASCII characters, it is reasonable to assume that all of
3087 * the Unicode characters can be reduced to a single byte */
3088 *lpdwBufferLength
= len
/ sizeof(WCHAR
);
3090 HeapFree(GetProcessHeap(), 0, bufferW
);
3095 /***********************************************************************
3096 * HttpSendRequestExA (WININET.@)
3098 * Sends the specified request to the HTTP server and allows chunked
3103 * Failure: FALSE, call GetLastError() for more information.
3105 BOOL WINAPI
HttpSendRequestExA(HINTERNET hRequest
,
3106 LPINTERNET_BUFFERSA lpBuffersIn
,
3107 LPINTERNET_BUFFERSA lpBuffersOut
,
3108 DWORD dwFlags
, DWORD_PTR dwContext
)
3110 INTERNET_BUFFERSW BuffersInW
;
3113 LPWSTR header
= NULL
;
3115 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
3116 lpBuffersOut
, dwFlags
, dwContext
);
3120 BuffersInW
.dwStructSize
= sizeof(LPINTERNET_BUFFERSW
);
3121 if (lpBuffersIn
->lpcszHeader
)
3123 headerlen
= MultiByteToWideChar(CP_ACP
,0,lpBuffersIn
->lpcszHeader
,
3124 lpBuffersIn
->dwHeadersLength
,0,0);
3125 header
= HeapAlloc(GetProcessHeap(),0,headerlen
*sizeof(WCHAR
));
3126 if (!(BuffersInW
.lpcszHeader
= header
))
3128 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
3131 BuffersInW
.dwHeadersLength
= MultiByteToWideChar(CP_ACP
, 0,
3132 lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
3136 BuffersInW
.lpcszHeader
= NULL
;
3137 BuffersInW
.dwHeadersTotal
= lpBuffersIn
->dwHeadersTotal
;
3138 BuffersInW
.lpvBuffer
= lpBuffersIn
->lpvBuffer
;
3139 BuffersInW
.dwBufferLength
= lpBuffersIn
->dwBufferLength
;
3140 BuffersInW
.dwBufferTotal
= lpBuffersIn
->dwBufferTotal
;
3141 BuffersInW
.Next
= NULL
;
3144 rc
= HttpSendRequestExW(hRequest
, lpBuffersIn
? &BuffersInW
: NULL
, NULL
, dwFlags
, dwContext
);
3146 HeapFree(GetProcessHeap(),0,header
);
3151 /***********************************************************************
3152 * HttpSendRequestExW (WININET.@)
3154 * Sends the specified request to the HTTP server and allows chunked
3159 * Failure: FALSE, call GetLastError() for more information.
3161 BOOL WINAPI
HttpSendRequestExW(HINTERNET hRequest
,
3162 LPINTERNET_BUFFERSW lpBuffersIn
,
3163 LPINTERNET_BUFFERSW lpBuffersOut
,
3164 DWORD dwFlags
, DWORD_PTR dwContext
)
3167 http_request_t
*lpwhr
;
3168 http_session_t
*lpwhs
;
3171 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
3172 lpBuffersOut
, dwFlags
, dwContext
);
3174 lpwhr
= (http_request_t
*) WININET_GetObject( hRequest
);
3176 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
3178 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
3182 lpwhs
= lpwhr
->lpHttpSession
;
3183 assert(lpwhs
->hdr
.htype
== WH_HHTTPSESSION
);
3184 hIC
= lpwhs
->lpAppInfo
;
3185 assert(hIC
->hdr
.htype
== WH_HINIT
);
3187 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
3189 WORKREQUEST workRequest
;
3190 struct WORKREQ_HTTPSENDREQUESTW
*req
;
3192 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
3193 workRequest
.hdr
= WININET_AddRef( &lpwhr
->hdr
);
3194 req
= &workRequest
.u
.HttpSendRequestW
;
3197 /* FIXME: this should use dwHeadersLength or may not be necessary at all */
3198 req
->lpszHeader
= heap_strdupW(lpBuffersIn
->lpcszHeader
);
3199 req
->dwHeaderLength
= lpBuffersIn
->dwHeadersLength
;
3200 req
->lpOptional
= lpBuffersIn
->lpvBuffer
;
3201 req
->dwOptionalLength
= lpBuffersIn
->dwBufferLength
;
3202 req
->dwContentLength
= lpBuffersIn
->dwBufferTotal
;
3206 req
->lpszHeader
= NULL
;
3207 req
->dwHeaderLength
= 0;
3208 req
->lpOptional
= NULL
;
3209 req
->dwOptionalLength
= 0;
3210 req
->dwContentLength
= 0;
3213 req
->bEndRequest
= FALSE
;
3215 INTERNET_AsyncCall(&workRequest
);
3217 * This is from windows.
3219 INTERNET_SetLastError(ERROR_IO_PENDING
);
3224 ret
= HTTP_HttpSendRequestW(lpwhr
, lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
3225 lpBuffersIn
->lpvBuffer
, lpBuffersIn
->dwBufferLength
,
3226 lpBuffersIn
->dwBufferTotal
, FALSE
);
3228 ret
= HTTP_HttpSendRequestW(lpwhr
, NULL
, 0, NULL
, 0, 0, FALSE
);
3233 WININET_Release( &lpwhr
->hdr
);
3239 /***********************************************************************
3240 * HttpSendRequestW (WININET.@)
3242 * Sends the specified request to the HTTP server
3249 BOOL WINAPI
HttpSendRequestW(HINTERNET hHttpRequest
, LPCWSTR lpszHeaders
,
3250 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
3252 http_request_t
*lpwhr
;
3253 http_session_t
*lpwhs
= NULL
;
3254 appinfo_t
*hIC
= NULL
;
3257 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest
,
3258 debugstr_wn(lpszHeaders
, dwHeaderLength
), dwHeaderLength
, lpOptional
, dwOptionalLength
);
3260 lpwhr
= (http_request_t
*) WININET_GetObject( hHttpRequest
);
3261 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
3263 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
3268 lpwhs
= lpwhr
->lpHttpSession
;
3269 if (NULL
== lpwhs
|| lpwhs
->hdr
.htype
!= WH_HHTTPSESSION
)
3271 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
3276 hIC
= lpwhs
->lpAppInfo
;
3277 if (NULL
== hIC
|| hIC
->hdr
.htype
!= WH_HINIT
)
3279 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
3284 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
3286 WORKREQUEST workRequest
;
3287 struct WORKREQ_HTTPSENDREQUESTW
*req
;
3289 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
3290 workRequest
.hdr
= WININET_AddRef( &lpwhr
->hdr
);
3291 req
= &workRequest
.u
.HttpSendRequestW
;
3296 if (dwHeaderLength
== ~0u) size
= (strlenW(lpszHeaders
) + 1) * sizeof(WCHAR
);
3297 else size
= dwHeaderLength
* sizeof(WCHAR
);
3299 req
->lpszHeader
= HeapAlloc(GetProcessHeap(), 0, size
);
3300 memcpy(req
->lpszHeader
, lpszHeaders
, size
);
3303 req
->lpszHeader
= 0;
3304 req
->dwHeaderLength
= dwHeaderLength
;
3305 req
->lpOptional
= lpOptional
;
3306 req
->dwOptionalLength
= dwOptionalLength
;
3307 req
->dwContentLength
= dwOptionalLength
;
3308 req
->bEndRequest
= TRUE
;
3310 INTERNET_AsyncCall(&workRequest
);
3312 * This is from windows.
3314 INTERNET_SetLastError(ERROR_IO_PENDING
);
3319 r
= HTTP_HttpSendRequestW(lpwhr
, lpszHeaders
,
3320 dwHeaderLength
, lpOptional
, dwOptionalLength
,
3321 dwOptionalLength
, TRUE
);
3325 WININET_Release( &lpwhr
->hdr
);
3329 /***********************************************************************
3330 * HttpSendRequestA (WININET.@)
3332 * Sends the specified request to the HTTP server
3339 BOOL WINAPI
HttpSendRequestA(HINTERNET hHttpRequest
, LPCSTR lpszHeaders
,
3340 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
3343 LPWSTR szHeaders
=NULL
;
3344 DWORD nLen
=dwHeaderLength
;
3345 if(lpszHeaders
!=NULL
)
3347 nLen
=MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,NULL
,0);
3348 szHeaders
=HeapAlloc(GetProcessHeap(),0,nLen
*sizeof(WCHAR
));
3349 MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,szHeaders
,nLen
);
3351 result
=HttpSendRequestW(hHttpRequest
, szHeaders
, nLen
, lpOptional
, dwOptionalLength
);
3352 HeapFree(GetProcessHeap(),0,szHeaders
);
3356 /***********************************************************************
3357 * HTTP_GetRedirectURL (internal)
3359 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*lpwhr
, LPCWSTR lpszUrl
)
3361 static WCHAR szHttp
[] = {'h','t','t','p',0};
3362 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3363 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
3364 URL_COMPONENTSW urlComponents
;
3365 DWORD url_length
= 0;
3367 LPWSTR combined_url
;
3369 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3370 urlComponents
.lpszScheme
= (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) ? szHttps
: szHttp
;
3371 urlComponents
.dwSchemeLength
= 0;
3372 urlComponents
.lpszHostName
= lpwhs
->lpszHostName
;
3373 urlComponents
.dwHostNameLength
= 0;
3374 urlComponents
.nPort
= lpwhs
->nHostPort
;
3375 urlComponents
.lpszUserName
= lpwhs
->lpszUserName
;
3376 urlComponents
.dwUserNameLength
= 0;
3377 urlComponents
.lpszPassword
= NULL
;
3378 urlComponents
.dwPasswordLength
= 0;
3379 urlComponents
.lpszUrlPath
= lpwhr
->lpszPath
;
3380 urlComponents
.dwUrlPathLength
= 0;
3381 urlComponents
.lpszExtraInfo
= NULL
;
3382 urlComponents
.dwExtraInfoLength
= 0;
3384 if (!InternetCreateUrlW(&urlComponents
, 0, NULL
, &url_length
) &&
3385 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3388 orig_url
= HeapAlloc(GetProcessHeap(), 0, url_length
);
3390 /* convert from bytes to characters */
3391 url_length
= url_length
/ sizeof(WCHAR
) - 1;
3392 if (!InternetCreateUrlW(&urlComponents
, 0, orig_url
, &url_length
))
3394 HeapFree(GetProcessHeap(), 0, orig_url
);
3399 if (!InternetCombineUrlW(orig_url
, lpszUrl
, NULL
, &url_length
, ICU_ENCODE_SPACES_ONLY
) &&
3400 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3402 HeapFree(GetProcessHeap(), 0, orig_url
);
3405 combined_url
= HeapAlloc(GetProcessHeap(), 0, url_length
* sizeof(WCHAR
));
3407 if (!InternetCombineUrlW(orig_url
, lpszUrl
, combined_url
, &url_length
, ICU_ENCODE_SPACES_ONLY
))
3409 HeapFree(GetProcessHeap(), 0, orig_url
);
3410 HeapFree(GetProcessHeap(), 0, combined_url
);
3413 HeapFree(GetProcessHeap(), 0, orig_url
);
3414 return combined_url
;
3418 /***********************************************************************
3419 * HTTP_HandleRedirect (internal)
3421 static BOOL
HTTP_HandleRedirect(http_request_t
*lpwhr
, LPCWSTR lpszUrl
)
3423 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
3424 appinfo_t
*hIC
= lpwhs
->lpAppInfo
;
3425 BOOL using_proxy
= hIC
->lpszProxy
&& hIC
->lpszProxy
[0];
3426 WCHAR path
[INTERNET_MAX_URL_LENGTH
];
3431 /* if it's an absolute path, keep the same session info */
3432 lstrcpynW(path
, lpszUrl
, INTERNET_MAX_URL_LENGTH
);
3436 URL_COMPONENTSW urlComponents
;
3437 WCHAR protocol
[32], hostName
[MAXHOSTNAME
], userName
[1024];
3438 static WCHAR szHttp
[] = {'h','t','t','p',0};
3439 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3445 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3446 urlComponents
.lpszScheme
= protocol
;
3447 urlComponents
.dwSchemeLength
= 32;
3448 urlComponents
.lpszHostName
= hostName
;
3449 urlComponents
.dwHostNameLength
= MAXHOSTNAME
;
3450 urlComponents
.lpszUserName
= userName
;
3451 urlComponents
.dwUserNameLength
= 1024;
3452 urlComponents
.lpszPassword
= NULL
;
3453 urlComponents
.dwPasswordLength
= 0;
3454 urlComponents
.lpszUrlPath
= path
;
3455 urlComponents
.dwUrlPathLength
= 2048;
3456 urlComponents
.lpszExtraInfo
= NULL
;
3457 urlComponents
.dwExtraInfoLength
= 0;
3458 if(!InternetCrackUrlW(lpszUrl
, strlenW(lpszUrl
), 0, &urlComponents
))
3461 if (!strncmpW(szHttp
, urlComponents
.lpszScheme
, strlenW(szHttp
)) &&
3462 (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
))
3464 TRACE("redirect from secure page to non-secure page\n");
3465 /* FIXME: warn about from secure redirect to non-secure page */
3466 lpwhr
->hdr
.dwFlags
&= ~INTERNET_FLAG_SECURE
;
3468 if (!strncmpW(szHttps
, urlComponents
.lpszScheme
, strlenW(szHttps
)) &&
3469 !(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
))
3471 TRACE("redirect from non-secure page to secure page\n");
3472 /* FIXME: notify about redirect to secure page */
3473 lpwhr
->hdr
.dwFlags
|= INTERNET_FLAG_SECURE
;
3476 if (urlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
3478 if (lstrlenW(protocol
)>4) /*https*/
3479 urlComponents
.nPort
= INTERNET_DEFAULT_HTTPS_PORT
;
3481 urlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
3486 * This upsets redirects to binary files on sourceforge.net
3487 * and gives an html page instead of the target file
3488 * Examination of the HTTP request sent by native wininet.dll
3489 * reveals that it doesn't send a referrer in that case.
3490 * Maybe there's a flag that enables this, or maybe a referrer
3491 * shouldn't be added in case of a redirect.
3494 /* consider the current host as the referrer */
3495 if (lpwhs
->lpszServerName
&& *lpwhs
->lpszServerName
)
3496 HTTP_ProcessHeader(lpwhr
, HTTP_REFERER
, lpwhs
->lpszServerName
,
3497 HTTP_ADDHDR_FLAG_REQ
|HTTP_ADDREQ_FLAG_REPLACE
|
3498 HTTP_ADDHDR_FLAG_ADD_IF_NEW
);
3501 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszHostName
);
3502 if (urlComponents
.nPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
3503 urlComponents
.nPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
3506 static const WCHAR fmt
[] = {'%','s',':','%','i',0};
3507 len
= lstrlenW(hostName
);
3508 len
+= 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3509 lpwhs
->lpszHostName
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
3510 sprintfW(lpwhs
->lpszHostName
, fmt
, hostName
, urlComponents
.nPort
);
3513 lpwhs
->lpszHostName
= heap_strdupW(hostName
);
3515 HTTP_ProcessHeader(lpwhr
, hostW
, lpwhs
->lpszHostName
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDREQ_FLAG_REPLACE
| HTTP_ADDHDR_FLAG_REQ
);
3517 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszUserName
);
3518 lpwhs
->lpszUserName
= NULL
;
3520 lpwhs
->lpszUserName
= heap_strdupW(userName
);
3524 if (strcmpiW(lpwhs
->lpszServerName
, hostName
) || lpwhs
->nServerPort
!= urlComponents
.nPort
)
3526 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszServerName
);
3527 lpwhs
->lpszServerName
= heap_strdupW(hostName
);
3528 lpwhs
->nServerPort
= urlComponents
.nPort
;
3530 NETCON_close(&lpwhr
->netConnection
);
3531 if (!HTTP_ResolveName(lpwhr
)) return FALSE
;
3532 if (!NETCON_init(&lpwhr
->netConnection
, lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)) return FALSE
;
3533 lpwhr
->read_pos
= lpwhr
->read_size
= 0;
3534 lpwhr
->read_chunked
= FALSE
;
3538 TRACE("Redirect through proxy\n");
3541 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszPath
);
3542 lpwhr
->lpszPath
=NULL
;
3548 rc
= UrlEscapeW(path
, NULL
, &needed
, URL_ESCAPE_SPACES_ONLY
);
3549 if (rc
!= E_POINTER
)
3550 needed
= strlenW(path
)+1;
3551 lpwhr
->lpszPath
= HeapAlloc(GetProcessHeap(), 0, needed
*sizeof(WCHAR
));
3552 rc
= UrlEscapeW(path
, lpwhr
->lpszPath
, &needed
,
3553 URL_ESCAPE_SPACES_ONLY
);
3556 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path
),rc
);
3557 strcpyW(lpwhr
->lpszPath
,path
);
3561 /* Remove custom content-type/length headers on redirects. */
3562 index
= HTTP_GetCustomHeaderIndex(lpwhr
, szContent_Type
, 0, TRUE
);
3564 HTTP_DeleteCustomHeader(lpwhr
, index
);
3565 index
= HTTP_GetCustomHeaderIndex(lpwhr
, szContent_Length
, 0, TRUE
);
3567 HTTP_DeleteCustomHeader(lpwhr
, index
);
3572 /***********************************************************************
3573 * HTTP_build_req (internal)
3575 * concatenate all the strings in the request together
3577 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
)
3582 for( t
= list
; *t
; t
++ )
3583 len
+= strlenW( *t
);
3586 str
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
3589 for( t
= list
; *t
; t
++ )
3595 static BOOL
HTTP_SecureProxyConnect(http_request_t
*lpwhr
)
3598 LPWSTR requestString
;
3604 static const WCHAR szConnect
[] = {'C','O','N','N','E','C','T',0};
3605 static const WCHAR szFormat
[] = {'%','s',':','%','d',0};
3606 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
3610 lpszPath
= HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs
->lpszHostName
) + 13)*sizeof(WCHAR
) );
3611 sprintfW( lpszPath
, szFormat
, lpwhs
->lpszHostName
, lpwhs
->nHostPort
);
3612 requestString
= HTTP_BuildHeaderRequestString( lpwhr
, szConnect
, lpszPath
, g_szHttp1_1
);
3613 HeapFree( GetProcessHeap(), 0, lpszPath
);
3615 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3616 NULL
, 0, NULL
, NULL
);
3617 len
--; /* the nul terminator isn't needed */
3618 ascii_req
= HeapAlloc( GetProcessHeap(), 0, len
);
3619 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3620 ascii_req
, len
, NULL
, NULL
);
3621 HeapFree( GetProcessHeap(), 0, requestString
);
3623 TRACE("full request -> %s\n", debugstr_an( ascii_req
, len
) );
3625 ret
= NETCON_send( &lpwhr
->netConnection
, ascii_req
, len
, 0, &cnt
);
3626 HeapFree( GetProcessHeap(), 0, ascii_req
);
3627 if (!ret
|| cnt
< 0)
3630 responseLen
= HTTP_GetResponseHeaders( lpwhr
, TRUE
);
3637 static void HTTP_InsertCookies(http_request_t
*lpwhr
)
3639 static const WCHAR szUrlForm
[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3640 LPWSTR lpszCookies
, lpszUrl
= NULL
;
3641 DWORD nCookieSize
, size
;
3642 LPHTTPHEADERW Host
= HTTP_GetHeader(lpwhr
, hostW
);
3644 size
= (strlenW(Host
->lpszValue
) + strlenW(szUrlForm
) + strlenW(lpwhr
->lpszPath
)) * sizeof(WCHAR
);
3645 if (!(lpszUrl
= HeapAlloc(GetProcessHeap(), 0, size
))) return;
3646 sprintfW( lpszUrl
, szUrlForm
, Host
->lpszValue
, lpwhr
->lpszPath
);
3648 if (InternetGetCookieW(lpszUrl
, NULL
, NULL
, &nCookieSize
))
3651 static const WCHAR szCookie
[] = {'C','o','o','k','i','e',':',' ',0};
3653 size
= sizeof(szCookie
) + nCookieSize
* sizeof(WCHAR
) + sizeof(szCrLf
);
3654 if ((lpszCookies
= HeapAlloc(GetProcessHeap(), 0, size
)))
3656 cnt
+= sprintfW(lpszCookies
, szCookie
);
3657 InternetGetCookieW(lpszUrl
, NULL
, lpszCookies
+ cnt
, &nCookieSize
);
3658 strcatW(lpszCookies
, szCrLf
);
3660 HTTP_HttpAddRequestHeadersW(lpwhr
, lpszCookies
, strlenW(lpszCookies
), HTTP_ADDREQ_FLAG_REPLACE
);
3661 HeapFree(GetProcessHeap(), 0, lpszCookies
);
3664 HeapFree(GetProcessHeap(), 0, lpszUrl
);
3667 /***********************************************************************
3668 * HTTP_HttpSendRequestW (internal)
3670 * Sends the specified request to the HTTP server
3677 BOOL WINAPI
HTTP_HttpSendRequestW(http_request_t
*lpwhr
, LPCWSTR lpszHeaders
,
3678 DWORD dwHeaderLength
, LPVOID lpOptional
, DWORD dwOptionalLength
,
3679 DWORD dwContentLength
, BOOL bEndRequest
)
3682 BOOL bSuccess
= FALSE
, redirected
= FALSE
;
3683 LPWSTR requestString
= NULL
;
3686 INTERNET_ASYNC_RESULT iar
;
3687 static const WCHAR szPost
[] = { 'P','O','S','T',0 };
3688 static const WCHAR szContentLength
[] =
3689 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3690 WCHAR contentLengthStr
[sizeof szContentLength
/2 /* includes \r\n */ + 20 /* int */ ];
3692 TRACE("--> %p\n", lpwhr
);
3694 assert(lpwhr
->hdr
.htype
== WH_HHTTPREQ
);
3696 /* if the verb is NULL default to GET */
3697 if (!lpwhr
->lpszVerb
)
3698 lpwhr
->lpszVerb
= heap_strdupW(szGET
);
3700 if (dwContentLength
|| strcmpW(lpwhr
->lpszVerb
, szGET
))
3702 sprintfW(contentLengthStr
, szContentLength
, dwContentLength
);
3703 HTTP_HttpAddRequestHeadersW(lpwhr
, contentLengthStr
, -1L, HTTP_ADDREQ_FLAG_REPLACE
);
3704 lpwhr
->dwBytesToWrite
= dwContentLength
;
3706 if (lpwhr
->lpHttpSession
->lpAppInfo
->lpszAgent
)
3708 WCHAR
*agent_header
;
3709 static const WCHAR user_agent
[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3712 len
= strlenW(lpwhr
->lpHttpSession
->lpAppInfo
->lpszAgent
) + strlenW(user_agent
);
3713 agent_header
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
3714 sprintfW(agent_header
, user_agent
, lpwhr
->lpHttpSession
->lpAppInfo
->lpszAgent
);
3716 HTTP_HttpAddRequestHeadersW(lpwhr
, agent_header
, strlenW(agent_header
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
3717 HeapFree(GetProcessHeap(), 0, agent_header
);
3719 if (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_PRAGMA_NOCACHE
)
3721 static const WCHAR pragma_nocache
[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3722 HTTP_HttpAddRequestHeadersW(lpwhr
, pragma_nocache
, strlenW(pragma_nocache
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
3724 if ((lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_CACHE_WRITE
) && !strcmpW(lpwhr
->lpszVerb
, szPost
))
3726 static const WCHAR cache_control
[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3727 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3728 HTTP_HttpAddRequestHeadersW(lpwhr
, cache_control
, strlenW(cache_control
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
3738 /* like native, just in case the caller forgot to call InternetReadFile
3739 * for all the data */
3740 HTTP_DrainContent(lpwhr
);
3741 lpwhr
->dwContentRead
= 0;
3743 if (TRACE_ON(wininet
))
3745 LPHTTPHEADERW Host
= HTTP_GetHeader(lpwhr
, hostW
);
3746 TRACE("Going to url %s %s\n", debugstr_w(Host
->lpszValue
), debugstr_w(lpwhr
->lpszPath
));
3750 if (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_KEEP_CONNECTION
)
3752 HTTP_ProcessHeader(lpwhr
, szConnection
, szKeepAlive
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
3754 HTTP_InsertAuthorization(lpwhr
, lpwhr
->pAuthInfo
, szAuthorization
);
3755 HTTP_InsertAuthorization(lpwhr
, lpwhr
->pProxyAuthInfo
, szProxy_Authorization
);
3757 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
))
3758 HTTP_InsertCookies(lpwhr
);
3760 /* add the headers the caller supplied */
3761 if( lpszHeaders
&& dwHeaderLength
)
3763 HTTP_HttpAddRequestHeadersW(lpwhr
, lpszHeaders
, dwHeaderLength
,
3764 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REPLACE
);
3767 if (lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxy
&& lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxy
[0])
3769 WCHAR
*url
= HTTP_BuildProxyRequestUrl(lpwhr
);
3770 requestString
= HTTP_BuildHeaderRequestString(lpwhr
, lpwhr
->lpszVerb
, url
, lpwhr
->lpszVersion
);
3771 HeapFree(GetProcessHeap(), 0, url
);
3774 requestString
= HTTP_BuildHeaderRequestString(lpwhr
, lpwhr
->lpszVerb
, lpwhr
->lpszPath
, lpwhr
->lpszVersion
);
3777 TRACE("Request header -> %s\n", debugstr_w(requestString
) );
3779 /* Send the request and store the results */
3780 if (!HTTP_OpenConnection(lpwhr
))
3783 /* send the request as ASCII, tack on the optional data */
3784 if (!lpOptional
|| redirected
)
3785 dwOptionalLength
= 0;
3786 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3787 NULL
, 0, NULL
, NULL
);
3788 ascii_req
= HeapAlloc( GetProcessHeap(), 0, len
+ dwOptionalLength
);
3789 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3790 ascii_req
, len
, NULL
, NULL
);
3792 memcpy( &ascii_req
[len
-1], lpOptional
, dwOptionalLength
);
3793 len
= (len
+ dwOptionalLength
- 1);
3795 TRACE("full request -> %s\n", debugstr_a(ascii_req
) );
3797 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3798 INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
3800 NETCON_send(&lpwhr
->netConnection
, ascii_req
, len
, 0, &cnt
);
3801 HeapFree( GetProcessHeap(), 0, ascii_req
);
3803 lpwhr
->dwBytesWritten
= dwOptionalLength
;
3805 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3806 INTERNET_STATUS_REQUEST_SENT
,
3807 &len
, sizeof(DWORD
));
3814 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3815 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
3820 responseLen
= HTTP_GetResponseHeaders(lpwhr
, TRUE
);
3824 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3825 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
,
3828 HTTP_ProcessCookies(lpwhr
);
3830 if (!set_content_length( lpwhr
)) HTTP_FinishedReading(lpwhr
);
3832 dwBufferSize
= sizeof(dwStatusCode
);
3833 if (!HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_STATUS_CODE
,
3834 &dwStatusCode
,&dwBufferSize
,NULL
))
3837 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
) && bSuccess
)
3839 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
3840 dwBufferSize
=sizeof(szNewLocation
);
3841 if ((dwStatusCode
==HTTP_STATUS_REDIRECT
|| dwStatusCode
==HTTP_STATUS_MOVED
) &&
3842 HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_LOCATION
,szNewLocation
,&dwBufferSize
,NULL
))
3844 if (strcmpW(lpwhr
->lpszVerb
, szGET
) && strcmpW(lpwhr
->lpszVerb
, szHEAD
))
3846 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVerb
);
3847 lpwhr
->lpszVerb
= heap_strdupW(szGET
);
3849 HTTP_DrainContent(lpwhr
);
3850 if ((new_url
= HTTP_GetRedirectURL( lpwhr
, szNewLocation
)))
3852 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
3853 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
3854 bSuccess
= HTTP_HandleRedirect(lpwhr
, new_url
);
3857 HeapFree(GetProcessHeap(), 0, requestString
);
3860 HeapFree( GetProcessHeap(), 0, new_url
);
3865 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTH
) && bSuccess
)
3867 WCHAR szAuthValue
[2048];
3869 if (dwStatusCode
== HTTP_STATUS_DENIED
)
3872 while (HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_WWW_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
))
3874 if (HTTP_DoAuthorization(lpwhr
, szAuthValue
,
3876 lpwhr
->lpHttpSession
->lpszUserName
,
3877 lpwhr
->lpHttpSession
->lpszPassword
))
3884 if (dwStatusCode
== HTTP_STATUS_PROXY_AUTH_REQ
)
3887 while (HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_PROXY_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
))
3889 if (HTTP_DoAuthorization(lpwhr
, szAuthValue
,
3890 &lpwhr
->pProxyAuthInfo
,
3891 lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxyUsername
,
3892 lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxyPassword
))
3907 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
3908 WCHAR cacheFileName
[MAX_PATH
+1];
3911 b
= HTTP_GetRequestURL(lpwhr
, url
);
3913 WARN("Could not get URL\n");
3917 b
= CreateUrlCacheEntryW(url
, lpwhr
->dwContentLength
> 0 ? lpwhr
->dwContentLength
: 0, NULL
, cacheFileName
, 0);
3919 lpwhr
->lpszCacheFile
= heap_strdupW(cacheFileName
);
3920 lpwhr
->hCacheFile
= CreateFileW(lpwhr
->lpszCacheFile
, GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
3921 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
3922 if(lpwhr
->hCacheFile
== INVALID_HANDLE_VALUE
) {
3923 WARN("Could not create file: %u\n", GetLastError());
3924 lpwhr
->hCacheFile
= NULL
;
3927 WARN("Could not create cache entry: %08x\n", GetLastError());
3933 HeapFree(GetProcessHeap(), 0, requestString
);
3935 /* TODO: send notification for P3P header */
3937 if (lpwhr
->lpHttpSession
->lpAppInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
3941 if (lpwhr
->dwBytesWritten
== lpwhr
->dwBytesToWrite
) HTTP_ReceiveRequestData(lpwhr
, TRUE
);
3944 iar
.dwResult
= (DWORD_PTR
)lpwhr
->hdr
.hInternet
;
3947 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3948 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
3949 sizeof(INTERNET_ASYNC_RESULT
));
3954 iar
.dwResult
= (DWORD_PTR
)lpwhr
->hdr
.hInternet
;
3955 iar
.dwError
= INTERNET_GetLastError();
3957 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3958 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
3959 sizeof(INTERNET_ASYNC_RESULT
));
3964 if (bSuccess
) INTERNET_SetLastError(ERROR_SUCCESS
);
3968 /***********************************************************************
3969 * HTTPSESSION_Destroy (internal)
3971 * Deallocate session handle
3974 static void HTTPSESSION_Destroy(object_header_t
*hdr
)
3976 http_session_t
*lpwhs
= (http_session_t
*) hdr
;
3978 TRACE("%p\n", lpwhs
);
3980 WININET_Release(&lpwhs
->lpAppInfo
->hdr
);
3982 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszHostName
);
3983 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszServerName
);
3984 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszPassword
);
3985 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszUserName
);
3986 HeapFree(GetProcessHeap(), 0, lpwhs
);
3989 static DWORD
HTTPSESSION_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
3992 case INTERNET_OPTION_HANDLE_TYPE
:
3993 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3995 if (*size
< sizeof(ULONG
))
3996 return ERROR_INSUFFICIENT_BUFFER
;
3998 *size
= sizeof(DWORD
);
3999 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_CONNECT_HTTP
;
4000 return ERROR_SUCCESS
;
4003 return INET_QueryOption(option
, buffer
, size
, unicode
);
4006 static DWORD
HTTPSESSION_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
4008 http_session_t
*ses
= (http_session_t
*)hdr
;
4011 case INTERNET_OPTION_USERNAME
:
4013 HeapFree(GetProcessHeap(), 0, ses
->lpszUserName
);
4014 if (!(ses
->lpszUserName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
4015 return ERROR_SUCCESS
;
4017 case INTERNET_OPTION_PASSWORD
:
4019 HeapFree(GetProcessHeap(), 0, ses
->lpszPassword
);
4020 if (!(ses
->lpszPassword
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
4021 return ERROR_SUCCESS
;
4026 return ERROR_INTERNET_INVALID_OPTION
;
4029 static const object_vtbl_t HTTPSESSIONVtbl
= {
4030 HTTPSESSION_Destroy
,
4032 HTTPSESSION_QueryOption
,
4033 HTTPSESSION_SetOption
,
4042 /***********************************************************************
4043 * HTTP_Connect (internal)
4045 * Create http session handle
4048 * HINTERNET a session handle on success
4052 HINTERNET
HTTP_Connect(appinfo_t
*hIC
, LPCWSTR lpszServerName
,
4053 INTERNET_PORT nServerPort
, LPCWSTR lpszUserName
,
4054 LPCWSTR lpszPassword
, DWORD dwFlags
, DWORD_PTR dwContext
,
4055 DWORD dwInternalFlags
)
4057 http_session_t
*lpwhs
= NULL
;
4058 HINTERNET handle
= NULL
;
4062 if (!lpszServerName
|| !lpszServerName
[0])
4064 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
4068 assert( hIC
->hdr
.htype
== WH_HINIT
);
4070 lpwhs
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(http_session_t
));
4073 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
4078 * According to my tests. The name is not resolved until a request is sent
4081 lpwhs
->hdr
.htype
= WH_HHTTPSESSION
;
4082 lpwhs
->hdr
.vtbl
= &HTTPSESSIONVtbl
;
4083 lpwhs
->hdr
.dwFlags
= dwFlags
;
4084 lpwhs
->hdr
.dwContext
= dwContext
;
4085 lpwhs
->hdr
.dwInternalFlags
= dwInternalFlags
| (hIC
->hdr
.dwInternalFlags
& INET_CALLBACKW
);
4086 lpwhs
->hdr
.refs
= 1;
4087 lpwhs
->hdr
.lpfnStatusCB
= hIC
->hdr
.lpfnStatusCB
;
4089 WININET_AddRef( &hIC
->hdr
);
4090 lpwhs
->lpAppInfo
= hIC
;
4091 list_add_head( &hIC
->hdr
.children
, &lpwhs
->hdr
.entry
);
4093 handle
= WININET_AllocHandle( &lpwhs
->hdr
);
4096 ERR("Failed to alloc handle\n");
4097 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
4101 if(hIC
->lpszProxy
&& hIC
->dwAccessType
== INTERNET_OPEN_TYPE_PROXY
) {
4102 if(strchrW(hIC
->lpszProxy
, ' '))
4103 FIXME("Several proxies not implemented.\n");
4104 if(hIC
->lpszProxyBypass
)
4105 FIXME("Proxy bypass is ignored.\n");
4107 if (lpszServerName
&& lpszServerName
[0])
4109 lpwhs
->lpszServerName
= heap_strdupW(lpszServerName
);
4110 lpwhs
->lpszHostName
= heap_strdupW(lpszServerName
);
4112 if (lpszUserName
&& lpszUserName
[0])
4113 lpwhs
->lpszUserName
= heap_strdupW(lpszUserName
);
4114 if (lpszPassword
&& lpszPassword
[0])
4115 lpwhs
->lpszPassword
= heap_strdupW(lpszPassword
);
4116 lpwhs
->nServerPort
= nServerPort
;
4117 lpwhs
->nHostPort
= nServerPort
;
4119 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4120 if (!(lpwhs
->hdr
.dwInternalFlags
& INET_OPENURL
))
4122 INTERNET_SendCallback(&hIC
->hdr
, dwContext
,
4123 INTERNET_STATUS_HANDLE_CREATED
, &handle
,
4129 WININET_Release( &lpwhs
->hdr
);
4132 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4136 TRACE("%p --> %p (%p)\n", hIC
, handle
, lpwhs
);
4141 /***********************************************************************
4142 * HTTP_OpenConnection (internal)
4144 * Connect to a web server
4151 static BOOL
HTTP_OpenConnection(http_request_t
*lpwhr
)
4153 BOOL bSuccess
= FALSE
;
4154 http_session_t
*lpwhs
;
4155 appinfo_t
*hIC
= NULL
;
4156 char szaddr
[INET6_ADDRSTRLEN
];
4162 if (lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
4164 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
4168 if (NETCON_connected(&lpwhr
->netConnection
))
4173 if (!HTTP_ResolveName(lpwhr
)) goto lend
;
4175 lpwhs
= lpwhr
->lpHttpSession
;
4177 hIC
= lpwhs
->lpAppInfo
;
4178 switch (lpwhs
->socketAddress
.ss_family
)
4181 addr
= &((struct sockaddr_in
*)&lpwhs
->socketAddress
)->sin_addr
;
4184 addr
= &((struct sockaddr_in6
*)&lpwhs
->socketAddress
)->sin6_addr
;
4187 WARN("unsupported family %d\n", lpwhs
->socketAddress
.ss_family
);
4188 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED
);
4191 inet_ntop(lpwhs
->socketAddress
.ss_family
, addr
, szaddr
, sizeof(szaddr
));
4192 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
4193 INTERNET_STATUS_CONNECTING_TO_SERVER
,
4197 if (!NETCON_create(&lpwhr
->netConnection
, lpwhs
->socketAddress
.ss_family
,
4200 WARN("Socket creation failed: %u\n", INTERNET_GetLastError());
4204 if (!NETCON_connect(&lpwhr
->netConnection
, (struct sockaddr
*)&lpwhs
->socketAddress
,
4208 if (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
4210 /* Note: we differ from Microsoft's WinINet here. they seem to have
4211 * a bug that causes no status callbacks to be sent when starting
4212 * a tunnel to a proxy server using the CONNECT verb. i believe our
4213 * behaviour to be more correct and to not cause any incompatibilities
4214 * because using a secure connection through a proxy server is a rare
4215 * case that would be hard for anyone to depend on */
4216 if (hIC
->lpszProxy
&& !HTTP_SecureProxyConnect(lpwhr
))
4219 if (!NETCON_secure_connect(&lpwhr
->netConnection
, lpwhs
->lpszHostName
))
4221 WARN("Couldn't connect securely to host\n");
4226 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
4227 INTERNET_STATUS_CONNECTED_TO_SERVER
,
4228 szaddr
, strlen(szaddr
)+1);
4233 lpwhr
->read_pos
= lpwhr
->read_size
= 0;
4234 lpwhr
->read_chunked
= FALSE
;
4236 TRACE("%d <--\n", bSuccess
);
4241 /***********************************************************************
4242 * HTTP_clear_response_headers (internal)
4244 * clear out any old response headers
4246 static void HTTP_clear_response_headers( http_request_t
*lpwhr
)
4250 for( i
=0; i
<lpwhr
->nCustHeaders
; i
++)
4252 if( !lpwhr
->pCustHeaders
[i
].lpszField
)
4254 if( !lpwhr
->pCustHeaders
[i
].lpszValue
)
4256 if ( lpwhr
->pCustHeaders
[i
].wFlags
& HDR_ISREQUEST
)
4258 HTTP_DeleteCustomHeader( lpwhr
, i
);
4263 /***********************************************************************
4264 * HTTP_GetResponseHeaders (internal)
4266 * Read server response
4273 static INT
HTTP_GetResponseHeaders(http_request_t
*lpwhr
, BOOL clear
)
4276 WCHAR buffer
[MAX_REPLY_LEN
];
4277 DWORD buflen
= MAX_REPLY_LEN
;
4278 BOOL bSuccess
= FALSE
;
4280 char bufferA
[MAX_REPLY_LEN
];
4281 LPWSTR status_code
= NULL
, status_text
= NULL
;
4282 DWORD cchMaxRawHeaders
= 1024;
4283 LPWSTR lpszRawHeaders
= HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
4285 DWORD cchRawHeaders
= 0;
4286 BOOL codeHundred
= FALSE
;
4290 /* clear old response headers (eg. from a redirect response) */
4291 if (clear
) HTTP_clear_response_headers( lpwhr
);
4293 if (!NETCON_connected(&lpwhr
->netConnection
))
4297 static const WCHAR szHundred
[] = {'1','0','0',0};
4299 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4301 buflen
= MAX_REPLY_LEN
;
4302 if (!read_line(lpwhr
, bufferA
, &buflen
))
4305 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
4306 /* check is this a status code line? */
4307 if (!strncmpW(buffer
, g_szHttp1_0
, 4))
4309 /* split the version from the status code */
4310 status_code
= strchrW( buffer
, ' ' );
4315 /* split the status code from the status text */
4316 status_text
= strchrW( status_code
, ' ' );
4321 TRACE("version [%s] status code [%s] status text [%s]\n",
4322 debugstr_w(buffer
), debugstr_w(status_code
), debugstr_w(status_text
) );
4324 codeHundred
= (!strcmpW(status_code
, szHundred
));
4326 else if (!codeHundred
)
4328 FIXME("Non status line at head of response (%s)\n",debugstr_w(buffer
));
4331 } while (codeHundred
);
4333 /* Add status code */
4334 HTTP_ProcessHeader(lpwhr
, szStatus
, status_code
,
4335 HTTP_ADDHDR_FLAG_REPLACE
);
4337 HeapFree(GetProcessHeap(),0,lpwhr
->lpszVersion
);
4338 HeapFree(GetProcessHeap(),0,lpwhr
->lpszStatusText
);
4340 lpwhr
->lpszVersion
= heap_strdupW(buffer
);
4341 lpwhr
->lpszStatusText
= heap_strdupW(status_text
);
4343 /* Restore the spaces */
4344 *(status_code
-1) = ' ';
4345 *(status_text
-1) = ' ';
4347 /* regenerate raw headers */
4348 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
4349 cchMaxRawHeaders
*= 2;
4350 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
4351 if (temp
== NULL
) goto lend
;
4352 lpszRawHeaders
= temp
;
4353 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
4354 cchRawHeaders
+= (buflen
-1);
4355 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
4356 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
4357 lpszRawHeaders
[cchRawHeaders
] = '\0';
4359 /* Parse each response line */
4362 buflen
= MAX_REPLY_LEN
;
4363 if (read_line(lpwhr
, bufferA
, &buflen
))
4365 LPWSTR
* pFieldAndValue
;
4367 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA
));
4369 if (!bufferA
[0]) break;
4370 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
4372 pFieldAndValue
= HTTP_InterpretHttpHeader(buffer
);
4375 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
4376 cchMaxRawHeaders
*= 2;
4377 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
4378 if (temp
== NULL
) goto lend
;
4379 lpszRawHeaders
= temp
;
4380 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
4381 cchRawHeaders
+= (buflen
-1);
4382 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
4383 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
4384 lpszRawHeaders
[cchRawHeaders
] = '\0';
4386 HTTP_ProcessHeader(lpwhr
, pFieldAndValue
[0], pFieldAndValue
[1],
4387 HTTP_ADDREQ_FLAG_ADD
);
4389 HTTP_FreeTokens(pFieldAndValue
);
4400 /* make sure the response header is terminated with an empty line. Some apps really
4401 truly care about that empty line being there for some reason. Just add it to the
4403 if (cchRawHeaders
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
4405 cchMaxRawHeaders
= cchRawHeaders
+ strlenW(szCrLf
);
4406 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
4407 if (temp
== NULL
) goto lend
;
4408 lpszRawHeaders
= temp
;
4411 memcpy(&lpszRawHeaders
[cchRawHeaders
], szCrLf
, sizeof(szCrLf
));
4413 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszRawHeaders
);
4414 lpwhr
->lpszRawHeaders
= lpszRawHeaders
;
4415 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders
));
4425 HeapFree(GetProcessHeap(), 0, lpszRawHeaders
);
4431 static void strip_spaces(LPWSTR start
)
4436 while (*str
== ' ' && *str
!= '\0')
4440 memmove(start
, str
, sizeof(WCHAR
) * (strlenW(str
) + 1));
4442 end
= start
+ strlenW(start
) - 1;
4443 while (end
>= start
&& *end
== ' ')
4451 /***********************************************************************
4452 * HTTP_InterpretHttpHeader (internal)
4454 * Parse server response
4458 * Pointer to array of field, value, NULL on success.
4461 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
)
4463 LPWSTR
* pTokenPair
;
4467 pTokenPair
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*pTokenPair
)*3);
4469 pszColon
= strchrW(buffer
, ':');
4470 /* must have two tokens */
4473 HTTP_FreeTokens(pTokenPair
);
4475 TRACE("No ':' in line: %s\n", debugstr_w(buffer
));
4479 pTokenPair
[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon
- buffer
+ 1) * sizeof(WCHAR
));
4482 HTTP_FreeTokens(pTokenPair
);
4485 memcpy(pTokenPair
[0], buffer
, (pszColon
- buffer
) * sizeof(WCHAR
));
4486 pTokenPair
[0][pszColon
- buffer
] = '\0';
4490 len
= strlenW(pszColon
);
4491 pTokenPair
[1] = HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
));
4494 HTTP_FreeTokens(pTokenPair
);
4497 memcpy(pTokenPair
[1], pszColon
, (len
+ 1) * sizeof(WCHAR
));
4499 strip_spaces(pTokenPair
[0]);
4500 strip_spaces(pTokenPair
[1]);
4502 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair
[0]), debugstr_w(pTokenPair
[1]));
4506 /***********************************************************************
4507 * HTTP_ProcessHeader (internal)
4509 * Stuff header into header tables according to <dwModifier>
4513 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4515 static BOOL
HTTP_ProcessHeader(http_request_t
*lpwhr
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
)
4517 LPHTTPHEADERW lphttpHdr
= NULL
;
4518 BOOL bSuccess
= FALSE
;
4520 BOOL request_only
= dwModifier
& HTTP_ADDHDR_FLAG_REQ
;
4522 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field
), debugstr_w(value
), dwModifier
);
4524 /* REPLACE wins out over ADD */
4525 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
4526 dwModifier
&= ~HTTP_ADDHDR_FLAG_ADD
;
4528 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD
)
4531 index
= HTTP_GetCustomHeaderIndex(lpwhr
, field
, 0, request_only
);
4535 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD_IF_NEW
)
4539 lphttpHdr
= &lpwhr
->pCustHeaders
[index
];
4545 hdr
.lpszField
= (LPWSTR
)field
;
4546 hdr
.lpszValue
= (LPWSTR
)value
;
4547 hdr
.wFlags
= hdr
.wCount
= 0;
4549 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
4550 hdr
.wFlags
|= HDR_ISREQUEST
;
4552 return HTTP_InsertCustomHeader(lpwhr
, &hdr
);
4554 /* no value to delete */
4557 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
4558 lphttpHdr
->wFlags
|= HDR_ISREQUEST
;
4560 lphttpHdr
->wFlags
&= ~HDR_ISREQUEST
;
4562 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
4564 HTTP_DeleteCustomHeader( lpwhr
, index
);
4570 hdr
.lpszField
= (LPWSTR
)field
;
4571 hdr
.lpszValue
= (LPWSTR
)value
;
4572 hdr
.wFlags
= hdr
.wCount
= 0;
4574 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
4575 hdr
.wFlags
|= HDR_ISREQUEST
;
4577 return HTTP_InsertCustomHeader(lpwhr
, &hdr
);
4582 else if (dwModifier
& COALESCEFLAGS
)
4587 INT origlen
= strlenW(lphttpHdr
->lpszValue
);
4588 INT valuelen
= strlenW(value
);
4590 if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
)
4593 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
4595 else if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON
)
4598 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
4601 len
= origlen
+ valuelen
+ ((ch
> 0) ? 2 : 0);
4603 lpsztmp
= HeapReAlloc(GetProcessHeap(), 0, lphttpHdr
->lpszValue
, (len
+1)*sizeof(WCHAR
));
4606 lphttpHdr
->lpszValue
= lpsztmp
;
4607 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4610 lphttpHdr
->lpszValue
[origlen
] = ch
;
4612 lphttpHdr
->lpszValue
[origlen
] = ' ';
4616 memcpy(&lphttpHdr
->lpszValue
[origlen
], value
, valuelen
*sizeof(WCHAR
));
4617 lphttpHdr
->lpszValue
[len
] = '\0';
4622 WARN("HeapReAlloc (%d bytes) failed\n",len
+1);
4623 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
4626 TRACE("<-- %d\n",bSuccess
);
4631 /***********************************************************************
4632 * HTTP_FinishedReading (internal)
4634 * Called when all content from server has been read by client.
4637 static BOOL
HTTP_FinishedReading(http_request_t
*lpwhr
)
4639 BOOL keepalive
= HTTP_KeepAlive(lpwhr
);
4646 HTTPREQ_CloseConnection(&lpwhr
->hdr
);
4649 /* FIXME: store data in the URL cache here */
4655 /***********************************************************************
4656 * HTTP_GetCustomHeaderIndex (internal)
4658 * Return index of custom header from header array
4661 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*lpwhr
, LPCWSTR lpszField
,
4662 int requested_index
, BOOL request_only
)
4666 TRACE("%s\n", debugstr_w(lpszField
));
4668 for (index
= 0; index
< lpwhr
->nCustHeaders
; index
++)
4670 if (strcmpiW(lpwhr
->pCustHeaders
[index
].lpszField
, lpszField
))
4673 if (request_only
&& !(lpwhr
->pCustHeaders
[index
].wFlags
& HDR_ISREQUEST
))
4676 if (!request_only
&& (lpwhr
->pCustHeaders
[index
].wFlags
& HDR_ISREQUEST
))
4679 if (requested_index
== 0)
4684 if (index
>= lpwhr
->nCustHeaders
)
4687 TRACE("Return: %d\n", index
);
4692 /***********************************************************************
4693 * HTTP_InsertCustomHeader (internal)
4695 * Insert header into array
4698 static BOOL
HTTP_InsertCustomHeader(http_request_t
*lpwhr
, LPHTTPHEADERW lpHdr
)
4701 LPHTTPHEADERW lph
= NULL
;
4704 TRACE("--> %s: %s\n", debugstr_w(lpHdr
->lpszField
), debugstr_w(lpHdr
->lpszValue
));
4705 count
= lpwhr
->nCustHeaders
+ 1;
4707 lph
= HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, lpwhr
->pCustHeaders
, sizeof(HTTPHEADERW
) * count
);
4709 lph
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(HTTPHEADERW
) * count
);
4713 lpwhr
->pCustHeaders
= lph
;
4714 lpwhr
->pCustHeaders
[count
-1].lpszField
= heap_strdupW(lpHdr
->lpszField
);
4715 lpwhr
->pCustHeaders
[count
-1].lpszValue
= heap_strdupW(lpHdr
->lpszValue
);
4716 lpwhr
->pCustHeaders
[count
-1].wFlags
= lpHdr
->wFlags
;
4717 lpwhr
->pCustHeaders
[count
-1].wCount
= lpHdr
->wCount
;
4718 lpwhr
->nCustHeaders
++;
4723 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
4730 /***********************************************************************
4731 * HTTP_DeleteCustomHeader (internal)
4733 * Delete header from array
4734 * If this function is called, the indexs may change.
4736 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*lpwhr
, DWORD index
)
4738 if( lpwhr
->nCustHeaders
<= 0 )
4740 if( index
>= lpwhr
->nCustHeaders
)
4742 lpwhr
->nCustHeaders
--;
4744 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[index
].lpszField
);
4745 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[index
].lpszValue
);
4747 memmove( &lpwhr
->pCustHeaders
[index
], &lpwhr
->pCustHeaders
[index
+1],
4748 (lpwhr
->nCustHeaders
- index
)* sizeof(HTTPHEADERW
) );
4749 memset( &lpwhr
->pCustHeaders
[lpwhr
->nCustHeaders
], 0, sizeof(HTTPHEADERW
) );
4755 /***********************************************************************
4756 * HTTP_VerifyValidHeader (internal)
4758 * Verify the given header is not invalid for the given http request
4761 static BOOL
HTTP_VerifyValidHeader(http_request_t
*lpwhr
, LPCWSTR field
)
4763 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4764 if (!strcmpW(lpwhr
->lpszVersion
, g_szHttp1_0
) && !strcmpiW(field
, szAccept_Encoding
))
4770 /***********************************************************************
4771 * IsHostInProxyBypassList (@)
4776 BOOL WINAPI
IsHostInProxyBypassList(DWORD flags
, LPCSTR szHost
, DWORD length
)
4778 FIXME("STUB: flags=%d host=%s length=%d\n",flags
,szHost
,length
);