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
;
247 static void init_gzip_stream(http_request_t
*req
)
249 ERR("gzip stream not supported, missing zlib.\n");
254 /* set the request content length based on the headers */
255 static DWORD
set_content_length( http_request_t
*lpwhr
)
257 static const WCHAR szChunked
[] = {'c','h','u','n','k','e','d',0};
261 size
= sizeof(lpwhr
->dwContentLength
);
262 if (!HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_CONTENT_LENGTH
,
263 &lpwhr
->dwContentLength
, &size
, NULL
))
264 lpwhr
->dwContentLength
= ~0u;
266 size
= sizeof(encoding
);
267 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_TRANSFER_ENCODING
, encoding
, &size
, NULL
) &&
268 !strcmpiW(encoding
, szChunked
))
270 lpwhr
->dwContentLength
= ~0u;
271 lpwhr
->read_chunked
= TRUE
;
274 if(lpwhr
->decoding
) {
277 static const WCHAR gzipW
[] = {'g','z','i','p',0};
279 encoding_idx
= HTTP_GetCustomHeaderIndex(lpwhr
, szContent_Encoding
, 0, FALSE
);
280 if(encoding_idx
!= -1 && !strcmpiW(lpwhr
->pCustHeaders
[encoding_idx
].lpszValue
, gzipW
))
281 init_gzip_stream(lpwhr
);
284 return lpwhr
->dwContentLength
;
287 /***********************************************************************
288 * HTTP_Tokenize (internal)
290 * Tokenize a string, allocating memory for the tokens.
292 static LPWSTR
* HTTP_Tokenize(LPCWSTR string
, LPCWSTR token_string
)
294 LPWSTR
* token_array
;
301 /* empty string has no tokens */
305 for (i
= 0; string
[i
]; i
++)
307 if (!strncmpW(string
+i
, token_string
, strlenW(token_string
)))
311 /* we want to skip over separators, but not the null terminator */
312 for (j
= 0; j
< strlenW(token_string
) - 1; j
++)
320 /* add 1 for terminating NULL */
321 token_array
= HeapAlloc(GetProcessHeap(), 0, (tokens
+1) * sizeof(*token_array
));
322 token_array
[tokens
] = NULL
;
325 for (i
= 0; i
< tokens
; i
++)
328 next_token
= strstrW(string
, token_string
);
329 if (!next_token
) next_token
= string
+strlenW(string
);
330 len
= next_token
- string
;
331 token_array
[i
] = HeapAlloc(GetProcessHeap(), 0, (len
+1)*sizeof(WCHAR
));
332 memcpy(token_array
[i
], string
, len
*sizeof(WCHAR
));
333 token_array
[i
][len
] = '\0';
334 string
= next_token
+strlenW(token_string
);
339 /***********************************************************************
340 * HTTP_FreeTokens (internal)
342 * Frees memory returned from HTTP_Tokenize.
344 static void HTTP_FreeTokens(LPWSTR
* token_array
)
347 for (i
= 0; token_array
[i
]; i
++)
348 HeapFree(GetProcessHeap(), 0, token_array
[i
]);
349 HeapFree(GetProcessHeap(), 0, token_array
);
352 /* **********************************************************************
354 * Helper functions for the HttpSendRequest(Ex) functions
357 static void AsyncHttpSendRequestProc(WORKREQUEST
*workRequest
)
359 struct WORKREQ_HTTPSENDREQUESTW
const *req
= &workRequest
->u
.HttpSendRequestW
;
360 http_request_t
*lpwhr
= (http_request_t
*) workRequest
->hdr
;
362 TRACE("%p\n", lpwhr
);
364 HTTP_HttpSendRequestW(lpwhr
, req
->lpszHeader
,
365 req
->dwHeaderLength
, req
->lpOptional
, req
->dwOptionalLength
,
366 req
->dwContentLength
, req
->bEndRequest
);
368 HeapFree(GetProcessHeap(), 0, req
->lpszHeader
);
371 static void HTTP_FixURL(http_request_t
*lpwhr
)
373 static const WCHAR szSlash
[] = { '/',0 };
374 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/', 0 };
376 /* If we don't have a path we set it to root */
377 if (NULL
== lpwhr
->lpszPath
)
378 lpwhr
->lpszPath
= heap_strdupW(szSlash
);
379 else /* remove \r and \n*/
381 int nLen
= strlenW(lpwhr
->lpszPath
);
382 while ((nLen
>0 ) && ((lpwhr
->lpszPath
[nLen
-1] == '\r')||(lpwhr
->lpszPath
[nLen
-1] == '\n')))
385 lpwhr
->lpszPath
[nLen
]='\0';
387 /* Replace '\' with '/' */
390 if (lpwhr
->lpszPath
[nLen
] == '\\') lpwhr
->lpszPath
[nLen
]='/';
394 if(CSTR_EQUAL
!= CompareStringW( LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
395 lpwhr
->lpszPath
, strlenW(lpwhr
->lpszPath
), szHttp
, strlenW(szHttp
) )
396 && lpwhr
->lpszPath
[0] != '/') /* not an absolute path ?? --> fix it !! */
398 WCHAR
*fixurl
= HeapAlloc(GetProcessHeap(), 0,
399 (strlenW(lpwhr
->lpszPath
) + 2)*sizeof(WCHAR
));
401 strcpyW(fixurl
+ 1, lpwhr
->lpszPath
);
402 HeapFree( GetProcessHeap(), 0, lpwhr
->lpszPath
);
403 lpwhr
->lpszPath
= fixurl
;
407 static LPWSTR
HTTP_BuildHeaderRequestString( http_request_t
*lpwhr
, LPCWSTR verb
, LPCWSTR path
, LPCWSTR version
)
409 LPWSTR requestString
;
415 static const WCHAR szSpace
[] = { ' ',0 };
416 static const WCHAR szColon
[] = { ':',' ',0 };
417 static const WCHAR sztwocrlf
[] = {'\r','\n','\r','\n', 0};
419 /* allocate space for an array of all the string pointers to be added */
420 len
= (lpwhr
->nCustHeaders
)*4 + 10;
421 req
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(LPCWSTR
) );
423 /* add the verb, path and HTTP version string */
431 /* Append custom request headers */
432 for (i
= 0; i
< lpwhr
->nCustHeaders
; i
++)
434 if (lpwhr
->pCustHeaders
[i
].wFlags
& HDR_ISREQUEST
)
437 req
[n
++] = lpwhr
->pCustHeaders
[i
].lpszField
;
439 req
[n
++] = lpwhr
->pCustHeaders
[i
].lpszValue
;
441 TRACE("Adding custom header %s (%s)\n",
442 debugstr_w(lpwhr
->pCustHeaders
[i
].lpszField
),
443 debugstr_w(lpwhr
->pCustHeaders
[i
].lpszValue
));
448 ERR("oops. buffer overrun\n");
451 requestString
= HTTP_build_req( req
, 4 );
452 HeapFree( GetProcessHeap(), 0, req
);
455 * Set (header) termination string for request
456 * Make sure there's exactly two new lines at the end of the request
458 p
= &requestString
[strlenW(requestString
)-1];
459 while ( (*p
== '\n') || (*p
== '\r') )
461 strcpyW( p
+1, sztwocrlf
);
463 return requestString
;
466 static void HTTP_ProcessCookies( http_request_t
*lpwhr
)
470 LPHTTPHEADERW setCookieHeader
;
472 while((HeaderIndex
= HTTP_GetCustomHeaderIndex(lpwhr
, szSet_Cookie
, numCookies
, FALSE
)) != -1)
474 setCookieHeader
= &lpwhr
->pCustHeaders
[HeaderIndex
];
476 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
) && setCookieHeader
->lpszValue
)
479 static const WCHAR szFmt
[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
483 Host
= HTTP_GetHeader(lpwhr
, hostW
);
484 len
= lstrlenW(Host
->lpszValue
) + 9 + lstrlenW(lpwhr
->lpszPath
);
485 buf_url
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
486 sprintfW(buf_url
, szFmt
, Host
->lpszValue
, lpwhr
->lpszPath
);
487 InternetSetCookieW(buf_url
, NULL
, setCookieHeader
->lpszValue
);
489 HeapFree(GetProcessHeap(), 0, buf_url
);
495 static inline BOOL
is_basic_auth_value( LPCWSTR pszAuthValue
)
497 static const WCHAR szBasic
[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
498 return !strncmpiW(pszAuthValue
, szBasic
, ARRAYSIZE(szBasic
)) &&
499 ((pszAuthValue
[ARRAYSIZE(szBasic
)] == ' ') || !pszAuthValue
[ARRAYSIZE(szBasic
)]);
502 static BOOL
HTTP_DoAuthorization( http_request_t
*lpwhr
, LPCWSTR pszAuthValue
,
503 struct HttpAuthInfo
**ppAuthInfo
,
504 LPWSTR domain_and_username
, LPWSTR password
)
506 SECURITY_STATUS sec_status
;
507 struct HttpAuthInfo
*pAuthInfo
= *ppAuthInfo
;
510 TRACE("%s\n", debugstr_w(pszAuthValue
));
517 pAuthInfo
= HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo
));
521 SecInvalidateHandle(&pAuthInfo
->cred
);
522 SecInvalidateHandle(&pAuthInfo
->ctx
);
523 memset(&pAuthInfo
->exp
, 0, sizeof(pAuthInfo
->exp
));
525 pAuthInfo
->auth_data
= NULL
;
526 pAuthInfo
->auth_data_len
= 0;
527 pAuthInfo
->finished
= FALSE
;
529 if (is_basic_auth_value(pszAuthValue
))
531 static const WCHAR szBasic
[] = {'B','a','s','i','c',0};
532 pAuthInfo
->scheme
= heap_strdupW(szBasic
);
533 if (!pAuthInfo
->scheme
)
535 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
542 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity
;
544 pAuthInfo
->scheme
= heap_strdupW(pszAuthValue
);
545 if (!pAuthInfo
->scheme
)
547 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
551 if (domain_and_username
)
553 WCHAR
*user
= strchrW(domain_and_username
, '\\');
554 WCHAR
*domain
= domain_and_username
;
556 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
558 pAuthData
= &nt_auth_identity
;
563 user
= domain_and_username
;
567 nt_auth_identity
.Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
568 nt_auth_identity
.User
= user
;
569 nt_auth_identity
.UserLength
= strlenW(nt_auth_identity
.User
);
570 nt_auth_identity
.Domain
= domain
;
571 nt_auth_identity
.DomainLength
= domain
? user
- domain
- 1 : 0;
572 nt_auth_identity
.Password
= password
;
573 nt_auth_identity
.PasswordLength
= strlenW(nt_auth_identity
.Password
);
576 /* use default credentials */
579 sec_status
= AcquireCredentialsHandleW(NULL
, pAuthInfo
->scheme
,
580 SECPKG_CRED_OUTBOUND
, NULL
,
582 NULL
, &pAuthInfo
->cred
,
584 if (sec_status
== SEC_E_OK
)
586 PSecPkgInfoW sec_pkg_info
;
587 sec_status
= QuerySecurityPackageInfoW(pAuthInfo
->scheme
, &sec_pkg_info
);
588 if (sec_status
== SEC_E_OK
)
590 pAuthInfo
->max_token
= sec_pkg_info
->cbMaxToken
;
591 FreeContextBuffer(sec_pkg_info
);
594 if (sec_status
!= SEC_E_OK
)
596 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
597 debugstr_w(pAuthInfo
->scheme
), sec_status
);
598 HeapFree(GetProcessHeap(), 0, pAuthInfo
->scheme
);
599 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
603 *ppAuthInfo
= pAuthInfo
;
605 else if (pAuthInfo
->finished
)
608 if ((strlenW(pszAuthValue
) < strlenW(pAuthInfo
->scheme
)) ||
609 strncmpiW(pszAuthValue
, pAuthInfo
->scheme
, strlenW(pAuthInfo
->scheme
)))
611 ERR("authentication scheme changed from %s to %s\n",
612 debugstr_w(pAuthInfo
->scheme
), debugstr_w(pszAuthValue
));
616 if (is_basic_auth_value(pszAuthValue
))
622 TRACE("basic authentication\n");
624 /* we don't cache credentials for basic authentication, so we can't
625 * retrieve them if the application didn't pass us any credentials */
626 if (!domain_and_username
) return FALSE
;
628 userlen
= WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, lstrlenW(domain_and_username
), NULL
, 0, NULL
, NULL
);
629 passlen
= WideCharToMultiByte(CP_UTF8
, 0, password
, lstrlenW(password
), NULL
, 0, NULL
, NULL
);
631 /* length includes a nul terminator, which will be re-used for the ':' */
632 auth_data
= HeapAlloc(GetProcessHeap(), 0, userlen
+ 1 + passlen
);
636 WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, -1, auth_data
, userlen
, NULL
, NULL
);
637 auth_data
[userlen
] = ':';
638 WideCharToMultiByte(CP_UTF8
, 0, password
, -1, &auth_data
[userlen
+1], passlen
, NULL
, NULL
);
640 pAuthInfo
->auth_data
= auth_data
;
641 pAuthInfo
->auth_data_len
= userlen
+ 1 + passlen
;
642 pAuthInfo
->finished
= TRUE
;
649 SecBufferDesc out_desc
, in_desc
;
651 unsigned char *buffer
;
652 ULONG context_req
= ISC_REQ_CONNECTION
| ISC_REQ_USE_DCE_STYLE
|
653 ISC_REQ_MUTUAL_AUTH
| ISC_REQ_DELEGATE
;
655 in
.BufferType
= SECBUFFER_TOKEN
;
659 in_desc
.ulVersion
= 0;
660 in_desc
.cBuffers
= 1;
661 in_desc
.pBuffers
= &in
;
663 pszAuthData
= pszAuthValue
+ strlenW(pAuthInfo
->scheme
);
664 if (*pszAuthData
== ' ')
667 in
.cbBuffer
= HTTP_DecodeBase64(pszAuthData
, NULL
);
668 in
.pvBuffer
= HeapAlloc(GetProcessHeap(), 0, in
.cbBuffer
);
669 HTTP_DecodeBase64(pszAuthData
, in
.pvBuffer
);
672 buffer
= HeapAlloc(GetProcessHeap(), 0, pAuthInfo
->max_token
);
674 out
.BufferType
= SECBUFFER_TOKEN
;
675 out
.cbBuffer
= pAuthInfo
->max_token
;
676 out
.pvBuffer
= buffer
;
678 out_desc
.ulVersion
= 0;
679 out_desc
.cBuffers
= 1;
680 out_desc
.pBuffers
= &out
;
682 sec_status
= InitializeSecurityContextW(first
? &pAuthInfo
->cred
: NULL
,
683 first
? NULL
: &pAuthInfo
->ctx
,
684 first
? lpwhr
->lpHttpSession
->lpszServerName
: NULL
,
685 context_req
, 0, SECURITY_NETWORK_DREP
,
686 in
.pvBuffer
? &in_desc
: NULL
,
687 0, &pAuthInfo
->ctx
, &out_desc
,
688 &pAuthInfo
->attr
, &pAuthInfo
->exp
);
689 if (sec_status
== SEC_E_OK
)
691 pAuthInfo
->finished
= TRUE
;
692 pAuthInfo
->auth_data
= out
.pvBuffer
;
693 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
694 TRACE("sending last auth packet\n");
696 else if (sec_status
== SEC_I_CONTINUE_NEEDED
)
698 pAuthInfo
->auth_data
= out
.pvBuffer
;
699 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
700 TRACE("sending next auth packet\n");
704 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status
);
705 pAuthInfo
->finished
= TRUE
;
706 HeapFree(GetProcessHeap(), 0, out
.pvBuffer
);
714 /***********************************************************************
715 * HTTP_HttpAddRequestHeadersW (internal)
717 static BOOL
HTTP_HttpAddRequestHeadersW(http_request_t
*lpwhr
,
718 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
723 BOOL bSuccess
= FALSE
;
726 TRACE("copying header: %s\n", debugstr_wn(lpszHeader
, dwHeaderLength
));
728 if( dwHeaderLength
== ~0U )
729 len
= strlenW(lpszHeader
);
731 len
= dwHeaderLength
;
732 buffer
= HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR
)*(len
+1) );
733 lstrcpynW( buffer
, lpszHeader
, len
+ 1);
739 LPWSTR
* pFieldAndValue
;
743 while (*lpszEnd
!= '\0')
745 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
750 if (*lpszStart
== '\0')
753 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
756 lpszEnd
++; /* Jump over newline */
758 TRACE("interpreting header %s\n", debugstr_w(lpszStart
));
759 if (*lpszStart
== '\0')
761 /* Skip 0-length headers */
766 pFieldAndValue
= HTTP_InterpretHttpHeader(lpszStart
);
769 bSuccess
= HTTP_VerifyValidHeader(lpwhr
, pFieldAndValue
[0]);
771 bSuccess
= HTTP_ProcessHeader(lpwhr
, pFieldAndValue
[0],
772 pFieldAndValue
[1], dwModifier
| HTTP_ADDHDR_FLAG_REQ
);
773 HTTP_FreeTokens(pFieldAndValue
);
779 HeapFree(GetProcessHeap(), 0, buffer
);
784 /***********************************************************************
785 * HttpAddRequestHeadersW (WININET.@)
787 * Adds one or more HTTP header to the request handler
790 * On Windows if dwHeaderLength includes the trailing '\0', then
791 * HttpAddRequestHeadersW() adds it too. However this results in an
792 * invalid Http header which is rejected by some servers so we probably
793 * don't need to match Windows on that point.
800 BOOL WINAPI
HttpAddRequestHeadersW(HINTERNET hHttpRequest
,
801 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
803 BOOL bSuccess
= FALSE
;
804 http_request_t
*lpwhr
;
806 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_wn(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
811 lpwhr
= (http_request_t
*) WININET_GetObject( hHttpRequest
);
812 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
814 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
817 bSuccess
= HTTP_HttpAddRequestHeadersW( lpwhr
, lpszHeader
, dwHeaderLength
, dwModifier
);
820 WININET_Release( &lpwhr
->hdr
);
825 /***********************************************************************
826 * HttpAddRequestHeadersA (WININET.@)
828 * Adds one or more HTTP header to the request handler
835 BOOL WINAPI
HttpAddRequestHeadersA(HINTERNET hHttpRequest
,
836 LPCSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
842 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_an(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
844 len
= MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, NULL
, 0 );
845 hdr
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
846 MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, hdr
, len
);
847 if( dwHeaderLength
!= ~0U )
848 dwHeaderLength
= len
;
850 r
= HttpAddRequestHeadersW( hHttpRequest
, hdr
, dwHeaderLength
, dwModifier
);
852 HeapFree( GetProcessHeap(), 0, hdr
);
857 /***********************************************************************
858 * HttpEndRequestA (WININET.@)
860 * Ends an HTTP request that was started by HttpSendRequestEx
867 BOOL WINAPI
HttpEndRequestA(HINTERNET hRequest
,
868 LPINTERNET_BUFFERSA lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
870 TRACE("(%p, %p, %08x, %08lx)\n", hRequest
, lpBuffersOut
, dwFlags
, dwContext
);
874 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
878 return HttpEndRequestW(hRequest
, NULL
, dwFlags
, dwContext
);
881 static BOOL
HTTP_HttpEndRequestW(http_request_t
*lpwhr
, DWORD dwFlags
, DWORD_PTR dwContext
)
886 INTERNET_ASYNC_RESULT iar
;
888 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
889 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
891 responseLen
= HTTP_GetResponseHeaders(lpwhr
, TRUE
);
895 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
896 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
, sizeof(DWORD
));
898 /* process cookies here. Is this right? */
899 HTTP_ProcessCookies(lpwhr
);
901 if (!set_content_length( lpwhr
)) HTTP_FinishedReading(lpwhr
);
903 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
))
905 DWORD dwCode
,dwCodeLength
= sizeof(DWORD
);
906 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_STATUS_CODE
, &dwCode
, &dwCodeLength
, NULL
) &&
907 (dwCode
== 302 || dwCode
== 301 || dwCode
== 303))
909 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
910 dwBufferSize
=sizeof(szNewLocation
);
911 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_LOCATION
, szNewLocation
, &dwBufferSize
, NULL
))
913 if (strcmpW(lpwhr
->lpszVerb
, szGET
) && strcmpW(lpwhr
->lpszVerb
, szHEAD
))
915 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVerb
);
916 lpwhr
->lpszVerb
= heap_strdupW(szGET
);
918 HTTP_DrainContent(lpwhr
);
919 if ((new_url
= HTTP_GetRedirectURL( lpwhr
, szNewLocation
)))
921 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
922 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
923 rc
= HTTP_HandleRedirect(lpwhr
, new_url
);
925 rc
= HTTP_HttpSendRequestW(lpwhr
, NULL
, 0, NULL
, 0, 0, TRUE
);
926 HeapFree( GetProcessHeap(), 0, new_url
);
932 iar
.dwResult
= (DWORD_PTR
)lpwhr
->hdr
.hInternet
;
933 iar
.dwError
= rc
? 0 : INTERNET_GetLastError();
935 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
936 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
937 sizeof(INTERNET_ASYNC_RESULT
));
941 static void AsyncHttpEndRequestProc(WORKREQUEST
*work
)
943 struct WORKREQ_HTTPENDREQUESTW
const *req
= &work
->u
.HttpEndRequestW
;
944 http_request_t
*lpwhr
= (http_request_t
*)work
->hdr
;
946 TRACE("%p\n", lpwhr
);
948 HTTP_HttpEndRequestW(lpwhr
, req
->dwFlags
, req
->dwContext
);
951 /***********************************************************************
952 * HttpEndRequestW (WININET.@)
954 * Ends an HTTP request that was started by HttpSendRequestEx
961 BOOL WINAPI
HttpEndRequestW(HINTERNET hRequest
,
962 LPINTERNET_BUFFERSW lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
965 http_request_t
*lpwhr
;
971 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
975 lpwhr
= (http_request_t
*) WININET_GetObject( hRequest
);
977 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
979 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
981 WININET_Release( &lpwhr
->hdr
);
984 lpwhr
->hdr
.dwFlags
|= dwFlags
;
986 if (lpwhr
->lpHttpSession
->lpAppInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
989 struct WORKREQ_HTTPENDREQUESTW
*request
;
991 work
.asyncproc
= AsyncHttpEndRequestProc
;
992 work
.hdr
= WININET_AddRef( &lpwhr
->hdr
);
994 request
= &work
.u
.HttpEndRequestW
;
995 request
->dwFlags
= dwFlags
;
996 request
->dwContext
= dwContext
;
998 INTERNET_AsyncCall(&work
);
999 INTERNET_SetLastError(ERROR_IO_PENDING
);
1002 rc
= HTTP_HttpEndRequestW(lpwhr
, dwFlags
, dwContext
);
1004 WININET_Release( &lpwhr
->hdr
);
1005 TRACE("%i <--\n",rc
);
1009 /***********************************************************************
1010 * HttpOpenRequestW (WININET.@)
1012 * Open a HTTP request handle
1015 * HINTERNET a HTTP request handle on success
1019 HINTERNET WINAPI
HttpOpenRequestW(HINTERNET hHttpSession
,
1020 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
1021 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
1022 DWORD dwFlags
, DWORD_PTR dwContext
)
1024 http_session_t
*lpwhs
;
1025 HINTERNET handle
= NULL
;
1027 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
1028 debugstr_w(lpszVerb
), debugstr_w(lpszObjectName
),
1029 debugstr_w(lpszVersion
), debugstr_w(lpszReferrer
), lpszAcceptTypes
,
1030 dwFlags
, dwContext
);
1031 if(lpszAcceptTypes
!=NULL
)
1034 for(i
=0;lpszAcceptTypes
[i
]!=NULL
;i
++)
1035 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes
[i
]));
1038 lpwhs
= (http_session_t
*) WININET_GetObject( hHttpSession
);
1039 if (NULL
== lpwhs
|| lpwhs
->hdr
.htype
!= WH_HHTTPSESSION
)
1041 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
1046 * My tests seem to show that the windows version does not
1047 * become asynchronous until after this point. And anyhow
1048 * if this call was asynchronous then how would you get the
1049 * necessary HINTERNET pointer returned by this function.
1052 handle
= HTTP_HttpOpenRequestW(lpwhs
, lpszVerb
, lpszObjectName
,
1053 lpszVersion
, lpszReferrer
, lpszAcceptTypes
,
1054 dwFlags
, dwContext
);
1057 WININET_Release( &lpwhs
->hdr
);
1058 TRACE("returning %p\n", handle
);
1063 /***********************************************************************
1064 * HttpOpenRequestA (WININET.@)
1066 * Open a HTTP request handle
1069 * HINTERNET a HTTP request handle on success
1073 HINTERNET WINAPI
HttpOpenRequestA(HINTERNET hHttpSession
,
1074 LPCSTR lpszVerb
, LPCSTR lpszObjectName
, LPCSTR lpszVersion
,
1075 LPCSTR lpszReferrer
, LPCSTR
*lpszAcceptTypes
,
1076 DWORD dwFlags
, DWORD_PTR dwContext
)
1078 LPWSTR szVerb
= NULL
, szObjectName
= NULL
;
1079 LPWSTR szVersion
= NULL
, szReferrer
= NULL
, *szAcceptTypes
= NULL
;
1080 INT acceptTypesCount
;
1081 HINTERNET rc
= FALSE
;
1084 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
1085 debugstr_a(lpszVerb
), debugstr_a(lpszObjectName
),
1086 debugstr_a(lpszVersion
), debugstr_a(lpszReferrer
), lpszAcceptTypes
,
1087 dwFlags
, dwContext
);
1091 szVerb
= heap_strdupAtoW(lpszVerb
);
1098 szObjectName
= heap_strdupAtoW(lpszObjectName
);
1099 if ( !szObjectName
)
1105 szVersion
= heap_strdupAtoW(lpszVersion
);
1112 szReferrer
= heap_strdupAtoW(lpszReferrer
);
1117 if (lpszAcceptTypes
)
1119 acceptTypesCount
= 0;
1120 types
= lpszAcceptTypes
;
1125 /* find out how many there are */
1126 if (*types
&& **types
)
1128 TRACE("accept type: %s\n", debugstr_a(*types
));
1134 WARN("invalid accept type pointer\n");
1139 szAcceptTypes
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
*) * (acceptTypesCount
+1));
1140 if (!szAcceptTypes
) goto end
;
1142 acceptTypesCount
= 0;
1143 types
= lpszAcceptTypes
;
1148 if (*types
&& **types
)
1149 szAcceptTypes
[acceptTypesCount
++] = heap_strdupAtoW(*types
);
1153 /* ignore invalid pointer */
1158 szAcceptTypes
[acceptTypesCount
] = NULL
;
1161 rc
= HttpOpenRequestW(hHttpSession
, szVerb
, szObjectName
,
1162 szVersion
, szReferrer
,
1163 (LPCWSTR
*)szAcceptTypes
, dwFlags
, dwContext
);
1168 acceptTypesCount
= 0;
1169 while (szAcceptTypes
[acceptTypesCount
])
1171 HeapFree(GetProcessHeap(), 0, szAcceptTypes
[acceptTypesCount
]);
1174 HeapFree(GetProcessHeap(), 0, szAcceptTypes
);
1176 HeapFree(GetProcessHeap(), 0, szReferrer
);
1177 HeapFree(GetProcessHeap(), 0, szVersion
);
1178 HeapFree(GetProcessHeap(), 0, szObjectName
);
1179 HeapFree(GetProcessHeap(), 0, szVerb
);
1184 /***********************************************************************
1187 static UINT
HTTP_EncodeBase64( LPCSTR bin
, unsigned int len
, LPWSTR base64
)
1190 static const CHAR HTTP_Base64Enc
[] =
1191 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1195 /* first 6 bits, all from bin[0] */
1196 base64
[n
++] = HTTP_Base64Enc
[(bin
[0] & 0xfc) >> 2];
1197 x
= (bin
[0] & 3) << 4;
1199 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1202 base64
[n
++] = HTTP_Base64Enc
[x
];
1207 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[1]&0xf0) >> 4 ) ];
1208 x
= ( bin
[1] & 0x0f ) << 2;
1210 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1213 base64
[n
++] = HTTP_Base64Enc
[x
];
1217 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[2]&0xc0 ) >> 6 ) ];
1219 /* last 6 bits, all from bin [2] */
1220 base64
[n
++] = HTTP_Base64Enc
[ bin
[2] & 0x3f ];
1228 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1229 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1230 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1231 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1232 static const signed char HTTP_Base64Dec
[256] =
1234 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1235 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1236 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1237 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1238 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1239 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1240 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1241 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1242 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1243 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1244 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1245 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1246 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1247 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1248 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1249 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1250 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1251 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1252 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1253 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1254 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1255 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1256 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1257 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1258 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1259 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1263 /***********************************************************************
1266 static UINT
HTTP_DecodeBase64( LPCWSTR base64
, LPSTR bin
)
1274 if (base64
[0] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1275 ((in
[0] = HTTP_Base64Dec
[base64
[0]]) == -1) ||
1276 base64
[1] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1277 ((in
[1] = HTTP_Base64Dec
[base64
[1]]) == -1))
1279 WARN("invalid base64: %s\n", debugstr_w(base64
));
1283 bin
[n
] = (unsigned char) (in
[0] << 2 | in
[1] >> 4);
1286 if ((base64
[2] == '=') && (base64
[3] == '='))
1288 if (base64
[2] > ARRAYSIZE(HTTP_Base64Dec
) ||
1289 ((in
[2] = HTTP_Base64Dec
[base64
[2]]) == -1))
1291 WARN("invalid base64: %s\n", debugstr_w(&base64
[2]));
1295 bin
[n
] = (unsigned char) (in
[1] << 4 | in
[2] >> 2);
1298 if (base64
[3] == '=')
1300 if (base64
[3] > ARRAYSIZE(HTTP_Base64Dec
) ||
1301 ((in
[3] = HTTP_Base64Dec
[base64
[3]]) == -1))
1303 WARN("invalid base64: %s\n", debugstr_w(&base64
[3]));
1307 bin
[n
] = (unsigned char) (((in
[2] << 6) & 0xc0) | in
[3]);
1316 /***********************************************************************
1317 * HTTP_InsertAuthorization
1319 * Insert or delete the authorization field in the request header.
1321 static BOOL
HTTP_InsertAuthorization( http_request_t
*lpwhr
, struct HttpAuthInfo
*pAuthInfo
, LPCWSTR header
)
1325 static const WCHAR wszSpace
[] = {' ',0};
1326 static const WCHAR wszBasic
[] = {'B','a','s','i','c',0};
1328 WCHAR
*authorization
= NULL
;
1330 if (pAuthInfo
->auth_data_len
)
1332 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1333 len
= strlenW(pAuthInfo
->scheme
)+1+((pAuthInfo
->auth_data_len
+2)*4)/3;
1334 authorization
= HeapAlloc(GetProcessHeap(), 0, (len
+1)*sizeof(WCHAR
));
1338 strcpyW(authorization
, pAuthInfo
->scheme
);
1339 strcatW(authorization
, wszSpace
);
1340 HTTP_EncodeBase64(pAuthInfo
->auth_data
,
1341 pAuthInfo
->auth_data_len
,
1342 authorization
+strlenW(authorization
));
1344 /* clear the data as it isn't valid now that it has been sent to the
1345 * server, unless it's Basic authentication which doesn't do
1346 * connection tracking */
1347 if (strcmpiW(pAuthInfo
->scheme
, wszBasic
))
1349 HeapFree(GetProcessHeap(), 0, pAuthInfo
->auth_data
);
1350 pAuthInfo
->auth_data
= NULL
;
1351 pAuthInfo
->auth_data_len
= 0;
1355 TRACE("Inserting authorization: %s\n", debugstr_w(authorization
));
1357 HTTP_ProcessHeader(lpwhr
, header
, authorization
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
1359 HeapFree(GetProcessHeap(), 0, authorization
);
1364 static WCHAR
*HTTP_BuildProxyRequestUrl(http_request_t
*req
)
1366 WCHAR new_location
[INTERNET_MAX_URL_LENGTH
], *url
;
1369 size
= sizeof(new_location
);
1370 if (HTTP_HttpQueryInfoW(req
, HTTP_QUERY_LOCATION
, new_location
, &size
, NULL
))
1372 if (!(url
= HeapAlloc( GetProcessHeap(), 0, size
+ sizeof(WCHAR
) ))) return NULL
;
1373 strcpyW( url
, new_location
);
1377 static const WCHAR slash
[] = { '/',0 };
1378 static const WCHAR format
[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1379 static const WCHAR formatSSL
[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1380 http_session_t
*session
= req
->lpHttpSession
;
1382 size
= 16; /* "https://" + sizeof(port#) + ":/\0" */
1383 size
+= strlenW( session
->lpszHostName
) + strlenW( req
->lpszPath
);
1385 if (!(url
= HeapAlloc( GetProcessHeap(), 0, size
* sizeof(WCHAR
) ))) return NULL
;
1387 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1388 sprintfW( url
, formatSSL
, session
->lpszHostName
, session
->nHostPort
);
1390 sprintfW( url
, format
, session
->lpszHostName
, session
->nHostPort
);
1391 if (req
->lpszPath
[0] != '/') strcatW( url
, slash
);
1392 strcatW( url
, req
->lpszPath
);
1394 TRACE("url=%s\n", debugstr_w(url
));
1398 /***********************************************************************
1399 * HTTP_DealWithProxy
1401 static BOOL
HTTP_DealWithProxy(appinfo_t
*hIC
, http_session_t
*lpwhs
, http_request_t
*lpwhr
)
1403 WCHAR buf
[MAXHOSTNAME
];
1404 WCHAR proxy
[MAXHOSTNAME
+ 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1405 static WCHAR szNul
[] = { 0 };
1406 URL_COMPONENTSW UrlComponents
;
1407 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/',0 };
1408 static const WCHAR szFormat
[] = { 'h','t','t','p',':','/','/','%','s',0 };
1410 memset( &UrlComponents
, 0, sizeof UrlComponents
);
1411 UrlComponents
.dwStructSize
= sizeof UrlComponents
;
1412 UrlComponents
.lpszHostName
= buf
;
1413 UrlComponents
.dwHostNameLength
= MAXHOSTNAME
;
1415 if( CSTR_EQUAL
!= CompareStringW(LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
1416 hIC
->lpszProxy
,strlenW(szHttp
),szHttp
,strlenW(szHttp
)) )
1417 sprintfW(proxy
, szFormat
, hIC
->lpszProxy
);
1419 strcpyW(proxy
, hIC
->lpszProxy
);
1420 if( !InternetCrackUrlW(proxy
, 0, 0, &UrlComponents
) )
1422 if( UrlComponents
.dwHostNameLength
== 0 )
1425 if( !lpwhr
->lpszPath
)
1426 lpwhr
->lpszPath
= szNul
;
1428 if(UrlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
1429 UrlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
1431 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszServerName
);
1432 lpwhs
->lpszServerName
= heap_strdupW(UrlComponents
.lpszHostName
);
1433 lpwhs
->nServerPort
= UrlComponents
.nPort
;
1435 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs
->lpszServerName
), lpwhs
->nServerPort
);
1439 #ifndef INET6_ADDRSTRLEN
1440 #define INET6_ADDRSTRLEN 46
1443 static BOOL
HTTP_ResolveName(http_request_t
*lpwhr
)
1445 char szaddr
[INET6_ADDRSTRLEN
];
1446 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
1449 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1450 INTERNET_STATUS_RESOLVING_NAME
,
1451 lpwhs
->lpszServerName
,
1452 strlenW(lpwhs
->lpszServerName
)+1);
1454 lpwhs
->sa_len
= sizeof(lpwhs
->socketAddress
);
1455 if (!GetAddress(lpwhs
->lpszServerName
, lpwhs
->nServerPort
,
1456 (struct sockaddr
*)&lpwhs
->socketAddress
, &lpwhs
->sa_len
))
1458 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED
);
1462 switch (lpwhs
->socketAddress
.ss_family
)
1465 addr
= &((struct sockaddr_in
*)&lpwhs
->socketAddress
)->sin_addr
;
1468 addr
= &((struct sockaddr_in6
*)&lpwhs
->socketAddress
)->sin6_addr
;
1471 WARN("unsupported family %d\n", lpwhs
->socketAddress
.ss_family
);
1472 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED
);
1475 inet_ntop(lpwhs
->socketAddress
.ss_family
, addr
, szaddr
, sizeof(szaddr
));
1476 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1477 INTERNET_STATUS_NAME_RESOLVED
,
1478 szaddr
, strlen(szaddr
)+1);
1480 TRACE("resolved %s to %s\n", debugstr_w(lpwhs
->lpszServerName
), szaddr
);
1485 /***********************************************************************
1486 * HTTPREQ_Destroy (internal)
1488 * Deallocate request handle
1491 static void HTTPREQ_Destroy(object_header_t
*hdr
)
1493 http_request_t
*lpwhr
= (http_request_t
*) hdr
;
1498 if(lpwhr
->hCacheFile
)
1499 CloseHandle(lpwhr
->hCacheFile
);
1501 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszCacheFile
);
1503 DeleteCriticalSection( &lpwhr
->read_section
);
1504 WININET_Release(&lpwhr
->lpHttpSession
->hdr
);
1506 if (lpwhr
->pAuthInfo
)
1508 if (SecIsValidHandle(&lpwhr
->pAuthInfo
->ctx
))
1509 DeleteSecurityContext(&lpwhr
->pAuthInfo
->ctx
);
1510 if (SecIsValidHandle(&lpwhr
->pAuthInfo
->cred
))
1511 FreeCredentialsHandle(&lpwhr
->pAuthInfo
->cred
);
1513 HeapFree(GetProcessHeap(), 0, lpwhr
->pAuthInfo
->auth_data
);
1514 HeapFree(GetProcessHeap(), 0, lpwhr
->pAuthInfo
->scheme
);
1515 HeapFree(GetProcessHeap(), 0, lpwhr
->pAuthInfo
);
1516 lpwhr
->pAuthInfo
= NULL
;
1519 if (lpwhr
->pProxyAuthInfo
)
1521 if (SecIsValidHandle(&lpwhr
->pProxyAuthInfo
->ctx
))
1522 DeleteSecurityContext(&lpwhr
->pProxyAuthInfo
->ctx
);
1523 if (SecIsValidHandle(&lpwhr
->pProxyAuthInfo
->cred
))
1524 FreeCredentialsHandle(&lpwhr
->pProxyAuthInfo
->cred
);
1526 HeapFree(GetProcessHeap(), 0, lpwhr
->pProxyAuthInfo
->auth_data
);
1527 HeapFree(GetProcessHeap(), 0, lpwhr
->pProxyAuthInfo
->scheme
);
1528 HeapFree(GetProcessHeap(), 0, lpwhr
->pProxyAuthInfo
);
1529 lpwhr
->pProxyAuthInfo
= NULL
;
1532 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszPath
);
1533 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVerb
);
1534 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszRawHeaders
);
1535 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVersion
);
1536 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszStatusText
);
1538 for (i
= 0; i
< lpwhr
->nCustHeaders
; i
++)
1540 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[i
].lpszField
);
1541 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[i
].lpszValue
);
1544 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
);
1545 HeapFree(GetProcessHeap(), 0, lpwhr
);
1548 static void HTTPREQ_CloseConnection(object_header_t
*hdr
)
1550 http_request_t
*lpwhr
= (http_request_t
*) hdr
;
1552 TRACE("%p\n",lpwhr
);
1555 if(lpwhr
->gzip_stream
) {
1556 inflateEnd(&lpwhr
->gzip_stream
->zstream
);
1557 HeapFree(GetProcessHeap(), 0, lpwhr
->gzip_stream
);
1558 lpwhr
->gzip_stream
= NULL
;
1562 if (!NETCON_connected(&lpwhr
->netConnection
))
1565 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1566 INTERNET_STATUS_CLOSING_CONNECTION
, 0, 0);
1568 NETCON_close(&lpwhr
->netConnection
);
1570 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1571 INTERNET_STATUS_CONNECTION_CLOSED
, 0, 0);
1574 static BOOL
HTTP_GetRequestURL(http_request_t
*req
, LPWSTR buf
)
1576 LPHTTPHEADERW host_header
;
1578 static const WCHAR formatW
[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1580 host_header
= HTTP_GetHeader(req
, hostW
);
1584 sprintfW(buf
, formatW
, host_header
->lpszValue
, req
->lpszPath
); /* FIXME */
1588 static DWORD
HTTPREQ_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
1590 http_request_t
*req
= (http_request_t
*)hdr
;
1593 case INTERNET_OPTION_SECURITY_FLAGS
:
1595 http_session_t
*lpwhs
;
1596 lpwhs
= req
->lpHttpSession
;
1598 if (*size
< sizeof(ULONG
))
1599 return ERROR_INSUFFICIENT_BUFFER
;
1601 *size
= sizeof(DWORD
);
1602 if (lpwhs
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1603 *(DWORD
*)buffer
= SECURITY_FLAG_SECURE
;
1605 *(DWORD
*)buffer
= 0;
1606 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD
*)buffer
);
1607 return ERROR_SUCCESS
;
1610 case INTERNET_OPTION_HANDLE_TYPE
:
1611 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1613 if (*size
< sizeof(ULONG
))
1614 return ERROR_INSUFFICIENT_BUFFER
;
1616 *size
= sizeof(DWORD
);
1617 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_HTTP_REQUEST
;
1618 return ERROR_SUCCESS
;
1620 case INTERNET_OPTION_URL
: {
1621 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1626 static const WCHAR httpW
[] = {'h','t','t','p',':','/','/',0};
1628 TRACE("INTERNET_OPTION_URL\n");
1630 host
= HTTP_GetHeader(req
, hostW
);
1631 strcpyW(url
, httpW
);
1632 strcatW(url
, host
->lpszValue
);
1633 if (NULL
!= (pch
= strchrW(url
+ strlenW(httpW
), ':')))
1635 strcatW(url
, req
->lpszPath
);
1637 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url
));
1640 len
= (strlenW(url
)+1) * sizeof(WCHAR
);
1642 return ERROR_INSUFFICIENT_BUFFER
;
1645 strcpyW(buffer
, url
);
1646 return ERROR_SUCCESS
;
1648 len
= WideCharToMultiByte(CP_ACP
, 0, url
, -1, buffer
, *size
, NULL
, NULL
);
1650 return ERROR_INSUFFICIENT_BUFFER
;
1653 return ERROR_SUCCESS
;
1657 case INTERNET_OPTION_CACHE_TIMESTAMPS
: {
1658 INTERNET_CACHE_ENTRY_INFOW
*info
;
1659 INTERNET_CACHE_TIMESTAMPS
*ts
= buffer
;
1660 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1661 DWORD nbytes
, error
;
1664 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1666 if (*size
< sizeof(*ts
))
1668 *size
= sizeof(*ts
);
1669 return ERROR_INSUFFICIENT_BUFFER
;
1672 HTTP_GetRequestURL(req
, url
);
1673 ret
= GetUrlCacheEntryInfoW(url
, NULL
, &nbytes
);
1674 error
= GetLastError();
1675 if (!ret
&& error
== ERROR_INSUFFICIENT_BUFFER
)
1677 if (!(info
= HeapAlloc(GetProcessHeap(), 0, nbytes
)))
1678 return ERROR_OUTOFMEMORY
;
1680 GetUrlCacheEntryInfoW(url
, info
, &nbytes
);
1682 ts
->ftExpires
= info
->ExpireTime
;
1683 ts
->ftLastModified
= info
->LastModifiedTime
;
1685 HeapFree(GetProcessHeap(), 0, info
);
1686 *size
= sizeof(*ts
);
1687 return ERROR_SUCCESS
;
1692 case INTERNET_OPTION_DATAFILE_NAME
: {
1695 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1697 if(!req
->lpszCacheFile
) {
1699 return ERROR_INTERNET_ITEM_NOT_FOUND
;
1703 req_size
= (lstrlenW(req
->lpszCacheFile
)+1) * sizeof(WCHAR
);
1704 if(*size
< req_size
)
1705 return ERROR_INSUFFICIENT_BUFFER
;
1708 memcpy(buffer
, req
->lpszCacheFile
, *size
);
1709 return ERROR_SUCCESS
;
1711 req_size
= WideCharToMultiByte(CP_ACP
, 0, req
->lpszCacheFile
, -1, NULL
, 0, NULL
, NULL
);
1712 if (req_size
> *size
)
1713 return ERROR_INSUFFICIENT_BUFFER
;
1715 *size
= WideCharToMultiByte(CP_ACP
, 0, req
->lpszCacheFile
,
1716 -1, buffer
, *size
, NULL
, NULL
);
1717 return ERROR_SUCCESS
;
1721 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT
: {
1722 PCCERT_CONTEXT context
;
1724 if(*size
< sizeof(INTERNET_CERTIFICATE_INFOW
)) {
1725 *size
= sizeof(INTERNET_CERTIFICATE_INFOW
);
1726 return ERROR_INSUFFICIENT_BUFFER
;
1729 context
= (PCCERT_CONTEXT
)NETCON_GetCert(&(req
->netConnection
));
1731 INTERNET_CERTIFICATE_INFOW
*info
= (INTERNET_CERTIFICATE_INFOW
*)buffer
;
1734 memset(info
, 0, sizeof(INTERNET_CERTIFICATE_INFOW
));
1735 info
->ftExpiry
= context
->pCertInfo
->NotAfter
;
1736 info
->ftStart
= context
->pCertInfo
->NotBefore
;
1738 len
= CertNameToStrW(context
->dwCertEncodingType
,
1739 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1740 info
->lpszSubjectInfo
= LocalAlloc(0, len
*sizeof(WCHAR
));
1741 if(info
->lpszSubjectInfo
)
1742 CertNameToStrW(context
->dwCertEncodingType
,
1743 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
,
1744 info
->lpszSubjectInfo
, len
);
1745 len
= CertNameToStrW(context
->dwCertEncodingType
,
1746 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1747 info
->lpszIssuerInfo
= LocalAlloc(0, len
*sizeof(WCHAR
));
1748 if (info
->lpszIssuerInfo
)
1749 CertNameToStrW(context
->dwCertEncodingType
,
1750 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
,
1751 info
->lpszIssuerInfo
, len
);
1753 INTERNET_CERTIFICATE_INFOA
*infoA
= (INTERNET_CERTIFICATE_INFOA
*)info
;
1755 len
= CertNameToStrA(context
->dwCertEncodingType
,
1756 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1757 infoA
->lpszSubjectInfo
= LocalAlloc(0, len
);
1758 if(infoA
->lpszSubjectInfo
)
1759 CertNameToStrA(context
->dwCertEncodingType
,
1760 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
,
1761 infoA
->lpszSubjectInfo
, len
);
1762 len
= CertNameToStrA(context
->dwCertEncodingType
,
1763 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1764 infoA
->lpszIssuerInfo
= LocalAlloc(0, len
);
1765 if(infoA
->lpszIssuerInfo
)
1766 CertNameToStrA(context
->dwCertEncodingType
,
1767 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
,
1768 infoA
->lpszIssuerInfo
, len
);
1772 * Contrary to MSDN, these do not appear to be set.
1774 * lpszSignatureAlgName
1775 * lpszEncryptionAlgName
1778 CertFreeCertificateContext(context
);
1779 return ERROR_SUCCESS
;
1784 return INET_QueryOption(option
, buffer
, size
, unicode
);
1787 static DWORD
HTTPREQ_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
1789 http_request_t
*req
= (http_request_t
*)hdr
;
1792 case INTERNET_OPTION_SEND_TIMEOUT
:
1793 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
1794 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1796 if (size
!= sizeof(DWORD
))
1797 return ERROR_INVALID_PARAMETER
;
1799 return NETCON_set_timeout(&req
->netConnection
, option
== INTERNET_OPTION_SEND_TIMEOUT
,
1802 case INTERNET_OPTION_USERNAME
:
1803 HeapFree(GetProcessHeap(), 0, req
->lpHttpSession
->lpszUserName
);
1804 if (!(req
->lpHttpSession
->lpszUserName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
1805 return ERROR_SUCCESS
;
1807 case INTERNET_OPTION_PASSWORD
:
1808 HeapFree(GetProcessHeap(), 0, req
->lpHttpSession
->lpszPassword
);
1809 if (!(req
->lpHttpSession
->lpszPassword
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
1810 return ERROR_SUCCESS
;
1811 case INTERNET_OPTION_HTTP_DECODING
:
1812 if(size
!= sizeof(BOOL
))
1813 return ERROR_INVALID_PARAMETER
;
1814 req
->decoding
= *(BOOL
*)buffer
;
1815 return ERROR_SUCCESS
;
1818 return ERROR_INTERNET_INVALID_OPTION
;
1821 /* read some more data into the read buffer (the read section must be held) */
1822 static BOOL
read_more_data( http_request_t
*req
, int maxlen
)
1828 /* move existing data to the start of the buffer */
1830 memmove( req
->read_buf
, req
->read_buf
+ req
->read_pos
, req
->read_size
);
1834 if (maxlen
== -1) maxlen
= sizeof(req
->read_buf
);
1836 if(!NETCON_recv( &req
->netConnection
, req
->read_buf
+ req
->read_size
,
1837 maxlen
- req
->read_size
, 0, &len
))
1840 req
->read_size
+= len
;
1844 /* remove some amount of data from the read buffer (the read section must be held) */
1845 static void remove_data( http_request_t
*req
, int count
)
1847 if (!(req
->read_size
-= count
)) req
->read_pos
= 0;
1848 else req
->read_pos
+= count
;
1851 static BOOL
read_line( http_request_t
*req
, LPSTR buffer
, DWORD
*len
)
1853 int count
, bytes_read
, pos
= 0;
1855 EnterCriticalSection( &req
->read_section
);
1858 BYTE
*eol
= memchr( req
->read_buf
+ req
->read_pos
, '\n', req
->read_size
);
1862 count
= eol
- (req
->read_buf
+ req
->read_pos
);
1863 bytes_read
= count
+ 1;
1865 else count
= bytes_read
= req
->read_size
;
1867 count
= min( count
, *len
- pos
);
1868 memcpy( buffer
+ pos
, req
->read_buf
+ req
->read_pos
, count
);
1870 remove_data( req
, bytes_read
);
1873 if (!read_more_data( req
, -1 ) || !req
->read_size
)
1876 TRACE( "returning empty string\n" );
1877 LeaveCriticalSection( &req
->read_section
);
1881 LeaveCriticalSection( &req
->read_section
);
1885 if (pos
&& buffer
[pos
- 1] == '\r') pos
--;
1888 buffer
[*len
- 1] = 0;
1889 TRACE( "returning %s\n", debugstr_a(buffer
));
1893 /* discard data contents until we reach end of line (the read section must be held) */
1894 static BOOL
discard_eol( http_request_t
*req
)
1898 BYTE
*eol
= memchr( req
->read_buf
+ req
->read_pos
, '\n', req
->read_size
);
1901 remove_data( req
, (eol
+ 1) - (req
->read_buf
+ req
->read_pos
) );
1904 req
->read_pos
= req
->read_size
= 0; /* discard everything */
1905 if (!read_more_data( req
, -1 )) return FALSE
;
1906 } while (req
->read_size
);
1910 /* read the size of the next chunk (the read section must be held) */
1911 static BOOL
start_next_chunk( http_request_t
*req
)
1913 DWORD chunk_size
= 0;
1915 if (!req
->dwContentLength
) return TRUE
;
1916 if (req
->dwContentLength
== req
->dwContentRead
)
1918 /* read terminator for the previous chunk */
1919 if (!discard_eol( req
)) return FALSE
;
1920 req
->dwContentLength
= ~0u;
1921 req
->dwContentRead
= 0;
1925 while (req
->read_size
)
1927 char ch
= req
->read_buf
[req
->read_pos
];
1928 if (ch
>= '0' && ch
<= '9') chunk_size
= chunk_size
* 16 + ch
- '0';
1929 else if (ch
>= 'a' && ch
<= 'f') chunk_size
= chunk_size
* 16 + ch
- 'a' + 10;
1930 else if (ch
>= 'A' && ch
<= 'F') chunk_size
= chunk_size
* 16 + ch
- 'A' + 10;
1931 else if (ch
== ';' || ch
== '\r' || ch
== '\n')
1933 TRACE( "reading %u byte chunk\n", chunk_size
);
1934 req
->dwContentLength
= chunk_size
;
1935 req
->dwContentRead
= 0;
1936 if (!discard_eol( req
)) return FALSE
;
1939 remove_data( req
, 1 );
1941 if (!read_more_data( req
, -1 )) return FALSE
;
1942 if (!req
->read_size
)
1944 req
->dwContentLength
= req
->dwContentRead
= 0;
1950 /* check if we have reached the end of the data to read (the read section must be held) */
1951 static BOOL
end_of_read_data( http_request_t
*req
)
1953 if (req
->gzip_stream
) return req
->gzip_stream
->end_of_data
&& !req
->gzip_stream
->buf_size
;
1954 if (req
->read_chunked
) return (req
->dwContentLength
== 0);
1955 if (req
->dwContentLength
== ~0u) return FALSE
;
1956 return (req
->dwContentLength
== req
->dwContentRead
);
1959 /* fetch some more data into the read buffer (the read section must be held) */
1960 static BOOL
refill_buffer( http_request_t
*req
)
1962 int len
= sizeof(req
->read_buf
);
1964 if (req
->read_chunked
&& (req
->dwContentLength
== ~0u || req
->dwContentLength
== req
->dwContentRead
))
1966 if (!start_next_chunk( req
)) return FALSE
;
1969 if (req
->dwContentLength
!= ~0u) len
= min( len
, req
->dwContentLength
- req
->dwContentRead
);
1970 if (len
<= req
->read_size
) return TRUE
;
1972 if (!read_more_data( req
, len
)) return FALSE
;
1973 if (!req
->read_size
) req
->dwContentLength
= req
->dwContentRead
= 0;
1977 static DWORD
read_gzip_data(http_request_t
*req
, BYTE
*buf
, int size
, BOOL sync
, int *read_ret
)
1979 DWORD ret
= ERROR_SUCCESS
;
1983 z_stream
*zstream
= &req
->gzip_stream
->zstream
;
1986 while(read
< size
&& !req
->gzip_stream
->end_of_data
) {
1987 if(!req
->read_size
) {
1988 if(!sync
|| !refill_buffer(req
))
1992 zstream
->next_in
= req
->read_buf
+req
->read_pos
;
1993 zstream
->avail_in
= req
->read_size
;
1994 zstream
->next_out
= buf
+read
;
1995 zstream
->avail_out
= size
-read
;
1996 zres
= inflate(zstream
, Z_FULL_FLUSH
);
1997 read
= size
- zstream
->avail_out
;
1998 remove_data(req
, req
->read_size
-zstream
->avail_in
);
1999 if(zres
== Z_STREAM_END
) {
2000 TRACE("end of data\n");
2001 req
->gzip_stream
->end_of_data
= TRUE
;
2002 }else if(zres
!= Z_OK
) {
2003 WARN("inflate failed %d\n", zres
);
2005 ret
= ERROR_INTERNET_DECODING_FAILED
;
2015 static void refill_gzip_buffer(http_request_t
*req
)
2020 if(!req
->gzip_stream
|| !req
->read_size
|| req
->gzip_stream
->buf_size
== sizeof(req
->gzip_stream
->buf
))
2023 if(req
->gzip_stream
->buf_pos
) {
2024 if(req
->gzip_stream
->buf_size
)
2025 memmove(req
->gzip_stream
->buf
, req
->gzip_stream
->buf
+ req
->gzip_stream
->buf_pos
, req
->gzip_stream
->buf_size
);
2026 req
->gzip_stream
->buf_pos
= 0;
2029 res
= read_gzip_data(req
, req
->gzip_stream
->buf
+ req
->gzip_stream
->buf_size
,
2030 sizeof(req
->gzip_stream
->buf
) - req
->gzip_stream
->buf_size
, FALSE
, &len
);
2031 if(res
== ERROR_SUCCESS
)
2032 req
->gzip_stream
->buf_size
+= len
;
2035 /* return the size of data available to be read immediately (the read section must be held) */
2036 static DWORD
get_avail_data( http_request_t
*req
)
2038 if (req
->gzip_stream
) {
2039 refill_gzip_buffer(req
);
2040 return req
->gzip_stream
->buf_size
;
2042 if (req
->read_chunked
&& (req
->dwContentLength
== ~0u || req
->dwContentLength
== req
->dwContentRead
))
2044 return min( req
->read_size
, req
->dwContentLength
- req
->dwContentRead
);
2047 static void HTTP_ReceiveRequestData(http_request_t
*req
, BOOL first_notif
)
2049 INTERNET_ASYNC_RESULT iar
;
2053 EnterCriticalSection( &req
->read_section
);
2054 if (refill_buffer( req
)) {
2055 iar
.dwResult
= (DWORD_PTR
)req
->hdr
.hInternet
;
2056 iar
.dwError
= first_notif
? 0 : get_avail_data(req
);
2059 iar
.dwError
= INTERNET_GetLastError();
2061 LeaveCriticalSection( &req
->read_section
);
2063 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2064 sizeof(INTERNET_ASYNC_RESULT
));
2067 /* read data from the http connection (the read section must be held) */
2068 static DWORD
HTTPREQ_Read(http_request_t
*req
, void *buffer
, DWORD size
, DWORD
*read
, BOOL sync
)
2070 BOOL finished_reading
= FALSE
;
2071 int len
, bytes_read
= 0;
2072 DWORD ret
= ERROR_SUCCESS
;
2074 EnterCriticalSection( &req
->read_section
);
2076 if (req
->read_chunked
&& (req
->dwContentLength
== ~0u || req
->dwContentLength
== req
->dwContentRead
))
2078 if (!start_next_chunk( req
)) goto done
;
2081 if(req
->gzip_stream
) {
2082 if(req
->gzip_stream
->buf_size
) {
2083 bytes_read
= min(req
->gzip_stream
->buf_size
, size
);
2084 memcpy(buffer
, req
->gzip_stream
->buf
+ req
->gzip_stream
->buf_pos
, bytes_read
);
2085 req
->gzip_stream
->buf_pos
+= bytes_read
;
2086 req
->gzip_stream
->buf_size
-= bytes_read
;
2087 }else if(!req
->read_size
&& !req
->gzip_stream
->end_of_data
) {
2091 if(size
> bytes_read
) {
2092 ret
= read_gzip_data(req
, (BYTE
*)buffer
+bytes_read
, size
-bytes_read
, sync
, &len
);
2093 if(ret
== ERROR_SUCCESS
)
2097 finished_reading
= req
->gzip_stream
->end_of_data
&& !req
->gzip_stream
->buf_size
;
2099 if (req
->dwContentLength
!= ~0u) size
= min( size
, req
->dwContentLength
- req
->dwContentRead
);
2101 if (req
->read_size
) {
2102 bytes_read
= min( req
->read_size
, size
);
2103 memcpy( buffer
, req
->read_buf
+ req
->read_pos
, bytes_read
);
2104 remove_data( req
, bytes_read
);
2107 if (size
> bytes_read
&& (!bytes_read
|| sync
)) {
2108 if (NETCON_recv( &req
->netConnection
, (char *)buffer
+ bytes_read
, size
- bytes_read
,
2109 sync
? MSG_WAITALL
: 0, &len
))
2111 /* always return success, even if the network layer returns an error */
2114 finished_reading
= !bytes_read
&& req
->dwContentRead
== req
->dwContentLength
;
2117 req
->dwContentRead
+= bytes_read
;
2120 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read
, req
->dwContentRead
, req
->dwContentLength
);
2121 LeaveCriticalSection( &req
->read_section
);
2123 if(ret
== ERROR_SUCCESS
&& req
->lpszCacheFile
) {
2125 DWORD dwBytesWritten
;
2127 res
= WriteFile(req
->hCacheFile
, buffer
, bytes_read
, &dwBytesWritten
, NULL
);
2129 WARN("WriteFile failed: %u\n", GetLastError());
2132 if(finished_reading
)
2133 HTTP_FinishedReading(req
);
2139 static DWORD
HTTPREQ_ReadFile(object_header_t
*hdr
, void *buffer
, DWORD size
, DWORD
*read
)
2141 http_request_t
*req
= (http_request_t
*)hdr
;
2142 return HTTPREQ_Read(req
, buffer
, size
, read
, TRUE
);
2145 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST
*workRequest
)
2147 struct WORKREQ_INTERNETREADFILEEXA
const *data
= &workRequest
->u
.InternetReadFileExA
;
2148 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2149 INTERNET_ASYNC_RESULT iar
;
2152 TRACE("INTERNETREADFILEEXA %p\n", workRequest
->hdr
);
2154 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2155 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2157 iar
.dwResult
= res
== ERROR_SUCCESS
;
2160 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2161 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2162 sizeof(INTERNET_ASYNC_RESULT
));
2165 static DWORD
HTTPREQ_ReadFileExA(object_header_t
*hdr
, INTERNET_BUFFERSA
*buffers
,
2166 DWORD flags
, DWORD_PTR context
)
2168 http_request_t
*req
= (http_request_t
*)hdr
;
2171 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2172 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2174 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2175 return ERROR_INVALID_PARAMETER
;
2177 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2179 if ((hdr
->dwFlags
& INTERNET_FLAG_ASYNC
) && !get_avail_data(req
))
2181 WORKREQUEST workRequest
;
2183 if (TryEnterCriticalSection( &req
->read_section
))
2185 if (get_avail_data(req
))
2187 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2188 &buffers
->dwBufferLength
, FALSE
);
2189 LeaveCriticalSection( &req
->read_section
);
2192 LeaveCriticalSection( &req
->read_section
);
2195 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExAProc
;
2196 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2197 workRequest
.u
.InternetReadFileExA
.lpBuffersOut
= buffers
;
2199 INTERNET_AsyncCall(&workRequest
);
2201 return ERROR_IO_PENDING
;
2204 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
, &buffers
->dwBufferLength
,
2205 !(flags
& IRF_NO_WAIT
));
2208 if (res
== ERROR_SUCCESS
) {
2209 DWORD size
= buffers
->dwBufferLength
;
2210 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2211 &size
, sizeof(size
));
2217 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST
*workRequest
)
2219 struct WORKREQ_INTERNETREADFILEEXW
const *data
= &workRequest
->u
.InternetReadFileExW
;
2220 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2221 INTERNET_ASYNC_RESULT iar
;
2224 TRACE("INTERNETREADFILEEXW %p\n", workRequest
->hdr
);
2226 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2227 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2229 iar
.dwResult
= res
== ERROR_SUCCESS
;
2232 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2233 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2234 sizeof(INTERNET_ASYNC_RESULT
));
2237 static DWORD
HTTPREQ_ReadFileExW(object_header_t
*hdr
, INTERNET_BUFFERSW
*buffers
,
2238 DWORD flags
, DWORD_PTR context
)
2241 http_request_t
*req
= (http_request_t
*)hdr
;
2244 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2245 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2247 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2248 return ERROR_INVALID_PARAMETER
;
2250 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2252 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2254 WORKREQUEST workRequest
;
2256 if (TryEnterCriticalSection( &req
->read_section
))
2258 if (get_avail_data(req
))
2260 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2261 &buffers
->dwBufferLength
, FALSE
);
2262 LeaveCriticalSection( &req
->read_section
);
2265 LeaveCriticalSection( &req
->read_section
);
2268 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExWProc
;
2269 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2270 workRequest
.u
.InternetReadFileExW
.lpBuffersOut
= buffers
;
2272 INTERNET_AsyncCall(&workRequest
);
2274 return ERROR_IO_PENDING
;
2277 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
, &buffers
->dwBufferLength
,
2278 !(flags
& IRF_NO_WAIT
));
2281 if (res
== ERROR_SUCCESS
) {
2282 DWORD size
= buffers
->dwBufferLength
;
2283 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2284 &size
, sizeof(size
));
2290 static BOOL
HTTPREQ_WriteFile(object_header_t
*hdr
, const void *buffer
, DWORD size
, DWORD
*written
)
2293 http_request_t
*lpwhr
= (http_request_t
*)hdr
;
2295 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
2298 if ((ret
= NETCON_send(&lpwhr
->netConnection
, buffer
, size
, 0, (LPINT
)written
)))
2299 lpwhr
->dwBytesWritten
+= *written
;
2301 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_SENT
, written
, sizeof(DWORD
));
2305 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST
*workRequest
)
2307 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2309 HTTP_ReceiveRequestData(req
, FALSE
);
2312 static DWORD
HTTPREQ_QueryDataAvailable(object_header_t
*hdr
, DWORD
*available
, DWORD flags
, DWORD_PTR ctx
)
2314 http_request_t
*req
= (http_request_t
*)hdr
;
2316 TRACE("(%p %p %x %lx)\n", req
, available
, flags
, ctx
);
2318 if (req
->lpHttpSession
->lpAppInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
2320 WORKREQUEST workRequest
;
2322 /* never wait, if we can't enter the section we queue an async request right away */
2323 if (TryEnterCriticalSection( &req
->read_section
))
2325 if ((*available
= get_avail_data( req
))) goto done
;
2326 if (end_of_read_data( req
)) goto done
;
2327 LeaveCriticalSection( &req
->read_section
);
2330 workRequest
.asyncproc
= HTTPREQ_AsyncQueryDataAvailableProc
;
2331 workRequest
.hdr
= WININET_AddRef( &req
->hdr
);
2333 INTERNET_AsyncCall(&workRequest
);
2335 return ERROR_IO_PENDING
;
2338 EnterCriticalSection( &req
->read_section
);
2340 if (!(*available
= get_avail_data( req
)) && !end_of_read_data( req
))
2342 refill_buffer( req
);
2343 *available
= get_avail_data( req
);
2347 if (*available
== sizeof(req
->read_buf
) && !req
->gzip_stream
) /* check if we have even more pending in the socket */
2350 if (NETCON_query_data_available(&req
->netConnection
, &extra
))
2351 *available
= min( *available
+ extra
, req
->dwContentLength
- req
->dwContentRead
);
2353 LeaveCriticalSection( &req
->read_section
);
2355 TRACE( "returning %u\n", *available
);
2356 return ERROR_SUCCESS
;
2359 static const object_vtbl_t HTTPREQVtbl
= {
2361 HTTPREQ_CloseConnection
,
2362 HTTPREQ_QueryOption
,
2365 HTTPREQ_ReadFileExA
,
2366 HTTPREQ_ReadFileExW
,
2368 HTTPREQ_QueryDataAvailable
,
2372 /***********************************************************************
2373 * HTTP_HttpOpenRequestW (internal)
2375 * Open a HTTP request handle
2378 * HINTERNET a HTTP request handle on success
2382 HINTERNET WINAPI
HTTP_HttpOpenRequestW(http_session_t
*lpwhs
,
2383 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
2384 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
2385 DWORD dwFlags
, DWORD_PTR dwContext
)
2387 appinfo_t
*hIC
= NULL
;
2388 http_request_t
*lpwhr
;
2389 LPWSTR lpszHostName
= NULL
;
2390 HINTERNET handle
= NULL
;
2391 static const WCHAR szHostForm
[] = {'%','s',':','%','u',0};
2396 assert( lpwhs
->hdr
.htype
== WH_HHTTPSESSION
);
2397 hIC
= lpwhs
->lpAppInfo
;
2399 lpwhr
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(http_request_t
));
2402 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
2405 lpwhr
->hdr
.htype
= WH_HHTTPREQ
;
2406 lpwhr
->hdr
.vtbl
= &HTTPREQVtbl
;
2407 lpwhr
->hdr
.dwFlags
= dwFlags
;
2408 lpwhr
->hdr
.dwContext
= dwContext
;
2409 lpwhr
->hdr
.refs
= 1;
2410 lpwhr
->hdr
.lpfnStatusCB
= lpwhs
->hdr
.lpfnStatusCB
;
2411 lpwhr
->hdr
.dwInternalFlags
= lpwhs
->hdr
.dwInternalFlags
& INET_CALLBACKW
;
2412 lpwhr
->dwContentLength
= ~0u;
2413 InitializeCriticalSection( &lpwhr
->read_section
);
2415 WININET_AddRef( &lpwhs
->hdr
);
2416 lpwhr
->lpHttpSession
= lpwhs
;
2417 list_add_head( &lpwhs
->hdr
.children
, &lpwhr
->hdr
.entry
);
2419 lpszHostName
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
) *
2420 (strlenW(lpwhs
->lpszHostName
) + 7 /* length of ":65535" + 1 */));
2421 if (NULL
== lpszHostName
)
2423 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
2427 handle
= WININET_AllocHandle( &lpwhr
->hdr
);
2430 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
2434 if (!NETCON_init(&lpwhr
->netConnection
, dwFlags
& INTERNET_FLAG_SECURE
))
2436 InternetCloseHandle( handle
);
2441 if (lpszObjectName
&& *lpszObjectName
) {
2445 rc
= UrlEscapeW(lpszObjectName
, NULL
, &len
, URL_ESCAPE_SPACES_ONLY
);
2446 if (rc
!= E_POINTER
)
2447 len
= strlenW(lpszObjectName
)+1;
2448 lpwhr
->lpszPath
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
2449 rc
= UrlEscapeW(lpszObjectName
, lpwhr
->lpszPath
, &len
,
2450 URL_ESCAPE_SPACES_ONLY
);
2453 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName
),rc
);
2454 strcpyW(lpwhr
->lpszPath
,lpszObjectName
);
2457 static const WCHAR slashW
[] = {'/',0};
2459 lpwhr
->lpszPath
= heap_strdupW(slashW
);
2462 if (lpszReferrer
&& *lpszReferrer
)
2463 HTTP_ProcessHeader(lpwhr
, HTTP_REFERER
, lpszReferrer
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2465 if (lpszAcceptTypes
)
2468 for (i
= 0; lpszAcceptTypes
[i
]; i
++)
2470 if (!*lpszAcceptTypes
[i
]) continue;
2471 HTTP_ProcessHeader(lpwhr
, HTTP_ACCEPT
, lpszAcceptTypes
[i
],
2472 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
|
2473 HTTP_ADDHDR_FLAG_REQ
|
2474 (i
== 0 ? HTTP_ADDHDR_FLAG_REPLACE
: 0));
2478 lpwhr
->lpszVerb
= heap_strdupW(lpszVerb
&& *lpszVerb
? lpszVerb
: szGET
);
2479 lpwhr
->lpszVersion
= heap_strdupW(lpszVersion
? lpszVersion
: g_szHttp1_1
);
2481 if (lpwhs
->nHostPort
!= INTERNET_INVALID_PORT_NUMBER
&&
2482 lpwhs
->nHostPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
2483 lpwhs
->nHostPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
2485 sprintfW(lpszHostName
, szHostForm
, lpwhs
->lpszHostName
, lpwhs
->nHostPort
);
2486 HTTP_ProcessHeader(lpwhr
, hostW
, lpszHostName
,
2487 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2490 HTTP_ProcessHeader(lpwhr
, hostW
, lpwhs
->lpszHostName
,
2491 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2493 if (lpwhs
->nServerPort
== INTERNET_INVALID_PORT_NUMBER
)
2494 lpwhs
->nServerPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
2495 INTERNET_DEFAULT_HTTPS_PORT
:
2496 INTERNET_DEFAULT_HTTP_PORT
);
2498 if (lpwhs
->nHostPort
== INTERNET_INVALID_PORT_NUMBER
)
2499 lpwhs
->nHostPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
2500 INTERNET_DEFAULT_HTTPS_PORT
:
2501 INTERNET_DEFAULT_HTTP_PORT
);
2503 if (NULL
!= hIC
->lpszProxy
&& hIC
->lpszProxy
[0] != 0)
2504 HTTP_DealWithProxy( hIC
, lpwhs
, lpwhr
);
2506 INTERNET_SendCallback(&lpwhs
->hdr
, dwContext
,
2507 INTERNET_STATUS_HANDLE_CREATED
, &handle
,
2511 HeapFree(GetProcessHeap(), 0, lpszHostName
);
2513 WININET_Release( &lpwhr
->hdr
);
2515 TRACE("<-- %p (%p)\n", handle
, lpwhr
);
2519 /* read any content returned by the server so that the connection can be
2521 static void HTTP_DrainContent(http_request_t
*req
)
2525 if (!NETCON_connected(&req
->netConnection
)) return;
2527 if (req
->dwContentLength
== -1)
2529 NETCON_close(&req
->netConnection
);
2532 if (!strcmpW(req
->lpszVerb
, szHEAD
)) return;
2537 if (HTTPREQ_Read(req
, buffer
, sizeof(buffer
), &bytes_read
, TRUE
) != ERROR_SUCCESS
)
2539 } while (bytes_read
);
2542 static const LPCWSTR header_lookup
[] = {
2543 szMime_Version
, /* HTTP_QUERY_MIME_VERSION = 0 */
2544 szContent_Type
, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2545 szContent_Transfer_Encoding
,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2546 szContent_ID
, /* HTTP_QUERY_CONTENT_ID = 3 */
2547 NULL
, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2548 szContent_Length
, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2549 szContent_Language
, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2550 szAllow
, /* HTTP_QUERY_ALLOW = 7 */
2551 szPublic
, /* HTTP_QUERY_PUBLIC = 8 */
2552 szDate
, /* HTTP_QUERY_DATE = 9 */
2553 szExpires
, /* HTTP_QUERY_EXPIRES = 10 */
2554 szLast_Modified
, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2555 NULL
, /* HTTP_QUERY_MESSAGE_ID = 12 */
2556 szURI
, /* HTTP_QUERY_URI = 13 */
2557 szFrom
, /* HTTP_QUERY_DERIVED_FROM = 14 */
2558 NULL
, /* HTTP_QUERY_COST = 15 */
2559 NULL
, /* HTTP_QUERY_LINK = 16 */
2560 szPragma
, /* HTTP_QUERY_PRAGMA = 17 */
2561 NULL
, /* HTTP_QUERY_VERSION = 18 */
2562 szStatus
, /* HTTP_QUERY_STATUS_CODE = 19 */
2563 NULL
, /* HTTP_QUERY_STATUS_TEXT = 20 */
2564 NULL
, /* HTTP_QUERY_RAW_HEADERS = 21 */
2565 NULL
, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2566 szConnection
, /* HTTP_QUERY_CONNECTION = 23 */
2567 szAccept
, /* HTTP_QUERY_ACCEPT = 24 */
2568 szAccept_Charset
, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2569 szAccept_Encoding
, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2570 szAccept_Language
, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2571 szAuthorization
, /* HTTP_QUERY_AUTHORIZATION = 28 */
2572 szContent_Encoding
, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2573 NULL
, /* HTTP_QUERY_FORWARDED = 30 */
2574 NULL
, /* HTTP_QUERY_FROM = 31 */
2575 szIf_Modified_Since
, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2576 szLocation
, /* HTTP_QUERY_LOCATION = 33 */
2577 NULL
, /* HTTP_QUERY_ORIG_URI = 34 */
2578 szReferer
, /* HTTP_QUERY_REFERER = 35 */
2579 szRetry_After
, /* HTTP_QUERY_RETRY_AFTER = 36 */
2580 szServer
, /* HTTP_QUERY_SERVER = 37 */
2581 NULL
, /* HTTP_TITLE = 38 */
2582 szUser_Agent
, /* HTTP_QUERY_USER_AGENT = 39 */
2583 szWWW_Authenticate
, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2584 szProxy_Authenticate
, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2585 szAccept_Ranges
, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2586 szSet_Cookie
, /* HTTP_QUERY_SET_COOKIE = 43 */
2587 szCookie
, /* HTTP_QUERY_COOKIE = 44 */
2588 NULL
, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2589 NULL
, /* HTTP_QUERY_REFRESH = 46 */
2590 NULL
, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2591 szAge
, /* HTTP_QUERY_AGE = 48 */
2592 szCache_Control
, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2593 szContent_Base
, /* HTTP_QUERY_CONTENT_BASE = 50 */
2594 szContent_Location
, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2595 szContent_MD5
, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2596 szContent_Range
, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2597 szETag
, /* HTTP_QUERY_ETAG = 54 */
2598 hostW
, /* HTTP_QUERY_HOST = 55 */
2599 szIf_Match
, /* HTTP_QUERY_IF_MATCH = 56 */
2600 szIf_None_Match
, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2601 szIf_Range
, /* HTTP_QUERY_IF_RANGE = 58 */
2602 szIf_Unmodified_Since
, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2603 szMax_Forwards
, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2604 szProxy_Authorization
, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2605 szRange
, /* HTTP_QUERY_RANGE = 62 */
2606 szTransfer_Encoding
, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2607 szUpgrade
, /* HTTP_QUERY_UPGRADE = 64 */
2608 szVary
, /* HTTP_QUERY_VARY = 65 */
2609 szVia
, /* HTTP_QUERY_VIA = 66 */
2610 szWarning
, /* HTTP_QUERY_WARNING = 67 */
2611 szExpect
, /* HTTP_QUERY_EXPECT = 68 */
2612 szProxy_Connection
, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2613 szUnless_Modified_Since
, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2616 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2618 /***********************************************************************
2619 * HTTP_HttpQueryInfoW (internal)
2621 static BOOL
HTTP_HttpQueryInfoW(http_request_t
*lpwhr
, DWORD dwInfoLevel
,
2622 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
2624 LPHTTPHEADERW lphttpHdr
= NULL
;
2625 BOOL bSuccess
= FALSE
;
2626 BOOL request_only
= dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
;
2627 INT requested_index
= lpdwIndex
? *lpdwIndex
: 0;
2628 DWORD level
= (dwInfoLevel
& ~HTTP_QUERY_MODIFIER_FLAGS_MASK
);
2631 /* Find requested header structure */
2634 case HTTP_QUERY_CUSTOM
:
2635 if (!lpBuffer
) return FALSE
;
2636 index
= HTTP_GetCustomHeaderIndex(lpwhr
, lpBuffer
, requested_index
, request_only
);
2639 case HTTP_QUERY_CONTENT_LENGTH
:
2640 if(lpwhr
->gzip_stream
) {
2641 INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND
);
2645 index
= HTTP_GetCustomHeaderIndex(lpwhr
, header_lookup
[level
],
2646 requested_index
,request_only
);
2649 case HTTP_QUERY_RAW_HEADERS_CRLF
:
2656 headers
= HTTP_BuildHeaderRequestString(lpwhr
, lpwhr
->lpszVerb
, lpwhr
->lpszPath
, lpwhr
->lpszVersion
);
2658 headers
= lpwhr
->lpszRawHeaders
;
2661 len
= strlenW(headers
) * sizeof(WCHAR
);
2663 if (len
+ sizeof(WCHAR
) > *lpdwBufferLength
)
2665 len
+= sizeof(WCHAR
);
2666 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER
);
2672 memcpy(lpBuffer
, headers
, len
+ sizeof(WCHAR
));
2675 len
= strlenW(szCrLf
) * sizeof(WCHAR
);
2676 memcpy(lpBuffer
, szCrLf
, sizeof(szCrLf
));
2678 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
/ sizeof(WCHAR
)));
2681 *lpdwBufferLength
= len
;
2684 HeapFree(GetProcessHeap(), 0, headers
);
2687 case HTTP_QUERY_RAW_HEADERS
:
2689 LPWSTR
* ppszRawHeaderLines
= HTTP_Tokenize(lpwhr
->lpszRawHeaders
, szCrLf
);
2691 LPWSTR pszString
= lpBuffer
;
2693 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
2694 size
+= strlenW(ppszRawHeaderLines
[i
]) + 1;
2696 if (size
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
2698 HTTP_FreeTokens(ppszRawHeaderLines
);
2699 *lpdwBufferLength
= (size
+ 1) * sizeof(WCHAR
);
2700 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER
);
2705 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
2707 DWORD len
= strlenW(ppszRawHeaderLines
[i
]);
2708 memcpy(pszString
, ppszRawHeaderLines
[i
], (len
+1)*sizeof(WCHAR
));
2712 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, size
));
2714 *lpdwBufferLength
= size
* sizeof(WCHAR
);
2715 HTTP_FreeTokens(ppszRawHeaderLines
);
2719 case HTTP_QUERY_STATUS_TEXT
:
2720 if (lpwhr
->lpszStatusText
)
2722 DWORD len
= strlenW(lpwhr
->lpszStatusText
);
2723 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
2725 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
2726 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER
);
2731 memcpy(lpBuffer
, lpwhr
->lpszStatusText
, (len
+ 1) * sizeof(WCHAR
));
2732 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
2734 *lpdwBufferLength
= len
* sizeof(WCHAR
);
2738 case HTTP_QUERY_VERSION
:
2739 if (lpwhr
->lpszVersion
)
2741 DWORD len
= strlenW(lpwhr
->lpszVersion
);
2742 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
2744 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
2745 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER
);
2750 memcpy(lpBuffer
, lpwhr
->lpszVersion
, (len
+ 1) * sizeof(WCHAR
));
2751 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
2753 *lpdwBufferLength
= len
* sizeof(WCHAR
);
2757 case HTTP_QUERY_CONTENT_ENCODING
:
2758 index
= HTTP_GetCustomHeaderIndex(lpwhr
, header_lookup
[lpwhr
->gzip_stream
? HTTP_QUERY_CONTENT_TYPE
: level
],
2759 requested_index
,request_only
);
2762 assert (LAST_TABLE_HEADER
== (HTTP_QUERY_UNLESS_MODIFIED_SINCE
+ 1));
2764 if (level
< LAST_TABLE_HEADER
&& header_lookup
[level
])
2765 index
= HTTP_GetCustomHeaderIndex(lpwhr
, header_lookup
[level
],
2766 requested_index
,request_only
);
2770 lphttpHdr
= &lpwhr
->pCustHeaders
[index
];
2772 /* Ensure header satisfies requested attributes */
2774 ((dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
) &&
2775 (~lphttpHdr
->wFlags
& HDR_ISREQUEST
)))
2777 INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND
);
2781 if (lpdwIndex
&& level
!= HTTP_QUERY_STATUS_CODE
) (*lpdwIndex
)++;
2783 /* coalesce value to requested type */
2784 if (dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
&& lpBuffer
)
2786 *(int *)lpBuffer
= atoiW(lphttpHdr
->lpszValue
);
2787 TRACE(" returning number: %d\n", *(int *)lpBuffer
);
2790 else if (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
&& lpBuffer
)
2796 tmpTime
= ConvertTimeString(lphttpHdr
->lpszValue
);
2798 tmpTM
= *gmtime(&tmpTime
);
2799 STHook
= (SYSTEMTIME
*)lpBuffer
;
2800 STHook
->wDay
= tmpTM
.tm_mday
;
2801 STHook
->wHour
= tmpTM
.tm_hour
;
2802 STHook
->wMilliseconds
= 0;
2803 STHook
->wMinute
= tmpTM
.tm_min
;
2804 STHook
->wDayOfWeek
= tmpTM
.tm_wday
;
2805 STHook
->wMonth
= tmpTM
.tm_mon
+ 1;
2806 STHook
->wSecond
= tmpTM
.tm_sec
;
2807 STHook
->wYear
= tmpTM
.tm_year
;
2810 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
2811 STHook
->wYear
, STHook
->wMonth
, STHook
->wDay
, STHook
->wDayOfWeek
,
2812 STHook
->wHour
, STHook
->wMinute
, STHook
->wSecond
, STHook
->wMilliseconds
);
2814 else if (lphttpHdr
->lpszValue
)
2816 DWORD len
= (strlenW(lphttpHdr
->lpszValue
) + 1) * sizeof(WCHAR
);
2818 if (len
> *lpdwBufferLength
)
2820 *lpdwBufferLength
= len
;
2821 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER
);
2826 memcpy(lpBuffer
, lphttpHdr
->lpszValue
, len
);
2827 TRACE(" returning string: %s\n", debugstr_w(lpBuffer
));
2829 *lpdwBufferLength
= len
- sizeof(WCHAR
);
2835 /***********************************************************************
2836 * HttpQueryInfoW (WININET.@)
2838 * Queries for information about an HTTP request
2845 BOOL WINAPI
HttpQueryInfoW(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
2846 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
2848 BOOL bSuccess
= FALSE
;
2849 http_request_t
*lpwhr
;
2851 if (TRACE_ON(wininet
)) {
2852 #define FE(x) { x, #x }
2853 static const wininet_flag_info query_flags
[] = {
2854 FE(HTTP_QUERY_MIME_VERSION
),
2855 FE(HTTP_QUERY_CONTENT_TYPE
),
2856 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING
),
2857 FE(HTTP_QUERY_CONTENT_ID
),
2858 FE(HTTP_QUERY_CONTENT_DESCRIPTION
),
2859 FE(HTTP_QUERY_CONTENT_LENGTH
),
2860 FE(HTTP_QUERY_CONTENT_LANGUAGE
),
2861 FE(HTTP_QUERY_ALLOW
),
2862 FE(HTTP_QUERY_PUBLIC
),
2863 FE(HTTP_QUERY_DATE
),
2864 FE(HTTP_QUERY_EXPIRES
),
2865 FE(HTTP_QUERY_LAST_MODIFIED
),
2866 FE(HTTP_QUERY_MESSAGE_ID
),
2868 FE(HTTP_QUERY_DERIVED_FROM
),
2869 FE(HTTP_QUERY_COST
),
2870 FE(HTTP_QUERY_LINK
),
2871 FE(HTTP_QUERY_PRAGMA
),
2872 FE(HTTP_QUERY_VERSION
),
2873 FE(HTTP_QUERY_STATUS_CODE
),
2874 FE(HTTP_QUERY_STATUS_TEXT
),
2875 FE(HTTP_QUERY_RAW_HEADERS
),
2876 FE(HTTP_QUERY_RAW_HEADERS_CRLF
),
2877 FE(HTTP_QUERY_CONNECTION
),
2878 FE(HTTP_QUERY_ACCEPT
),
2879 FE(HTTP_QUERY_ACCEPT_CHARSET
),
2880 FE(HTTP_QUERY_ACCEPT_ENCODING
),
2881 FE(HTTP_QUERY_ACCEPT_LANGUAGE
),
2882 FE(HTTP_QUERY_AUTHORIZATION
),
2883 FE(HTTP_QUERY_CONTENT_ENCODING
),
2884 FE(HTTP_QUERY_FORWARDED
),
2885 FE(HTTP_QUERY_FROM
),
2886 FE(HTTP_QUERY_IF_MODIFIED_SINCE
),
2887 FE(HTTP_QUERY_LOCATION
),
2888 FE(HTTP_QUERY_ORIG_URI
),
2889 FE(HTTP_QUERY_REFERER
),
2890 FE(HTTP_QUERY_RETRY_AFTER
),
2891 FE(HTTP_QUERY_SERVER
),
2892 FE(HTTP_QUERY_TITLE
),
2893 FE(HTTP_QUERY_USER_AGENT
),
2894 FE(HTTP_QUERY_WWW_AUTHENTICATE
),
2895 FE(HTTP_QUERY_PROXY_AUTHENTICATE
),
2896 FE(HTTP_QUERY_ACCEPT_RANGES
),
2897 FE(HTTP_QUERY_SET_COOKIE
),
2898 FE(HTTP_QUERY_COOKIE
),
2899 FE(HTTP_QUERY_REQUEST_METHOD
),
2900 FE(HTTP_QUERY_REFRESH
),
2901 FE(HTTP_QUERY_CONTENT_DISPOSITION
),
2903 FE(HTTP_QUERY_CACHE_CONTROL
),
2904 FE(HTTP_QUERY_CONTENT_BASE
),
2905 FE(HTTP_QUERY_CONTENT_LOCATION
),
2906 FE(HTTP_QUERY_CONTENT_MD5
),
2907 FE(HTTP_QUERY_CONTENT_RANGE
),
2908 FE(HTTP_QUERY_ETAG
),
2909 FE(HTTP_QUERY_HOST
),
2910 FE(HTTP_QUERY_IF_MATCH
),
2911 FE(HTTP_QUERY_IF_NONE_MATCH
),
2912 FE(HTTP_QUERY_IF_RANGE
),
2913 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE
),
2914 FE(HTTP_QUERY_MAX_FORWARDS
),
2915 FE(HTTP_QUERY_PROXY_AUTHORIZATION
),
2916 FE(HTTP_QUERY_RANGE
),
2917 FE(HTTP_QUERY_TRANSFER_ENCODING
),
2918 FE(HTTP_QUERY_UPGRADE
),
2919 FE(HTTP_QUERY_VARY
),
2921 FE(HTTP_QUERY_WARNING
),
2922 FE(HTTP_QUERY_CUSTOM
)
2924 static const wininet_flag_info modifier_flags
[] = {
2925 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS
),
2926 FE(HTTP_QUERY_FLAG_SYSTEMTIME
),
2927 FE(HTTP_QUERY_FLAG_NUMBER
),
2928 FE(HTTP_QUERY_FLAG_COALESCE
)
2931 DWORD info_mod
= dwInfoLevel
& HTTP_QUERY_MODIFIER_FLAGS_MASK
;
2932 DWORD info
= dwInfoLevel
& HTTP_QUERY_HEADER_MASK
;
2935 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest
, dwInfoLevel
, dwInfoLevel
);
2936 TRACE(" Attribute:");
2937 for (i
= 0; i
< (sizeof(query_flags
) / sizeof(query_flags
[0])); i
++) {
2938 if (query_flags
[i
].val
== info
) {
2939 TRACE(" %s", query_flags
[i
].name
);
2943 if (i
== (sizeof(query_flags
) / sizeof(query_flags
[0]))) {
2944 TRACE(" Unknown (%08x)", info
);
2947 TRACE(" Modifier:");
2948 for (i
= 0; i
< (sizeof(modifier_flags
) / sizeof(modifier_flags
[0])); i
++) {
2949 if (modifier_flags
[i
].val
& info_mod
) {
2950 TRACE(" %s", modifier_flags
[i
].name
);
2951 info_mod
&= ~ modifier_flags
[i
].val
;
2956 TRACE(" Unknown (%08x)", info_mod
);
2961 lpwhr
= (http_request_t
*) WININET_GetObject( hHttpRequest
);
2962 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
2964 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
2968 if (lpBuffer
== NULL
)
2969 *lpdwBufferLength
= 0;
2970 bSuccess
= HTTP_HttpQueryInfoW( lpwhr
, dwInfoLevel
,
2971 lpBuffer
, lpdwBufferLength
, lpdwIndex
);
2975 WININET_Release( &lpwhr
->hdr
);
2977 TRACE("%d <--\n", bSuccess
);
2981 /***********************************************************************
2982 * HttpQueryInfoA (WININET.@)
2984 * Queries for information about an HTTP request
2991 BOOL WINAPI
HttpQueryInfoA(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
2992 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
2998 if((dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
) ||
2999 (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
))
3001 return HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, lpBuffer
,
3002 lpdwBufferLength
, lpdwIndex
);
3008 len
= (*lpdwBufferLength
)*sizeof(WCHAR
);
3009 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3011 alloclen
= MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, NULL
, 0 ) * sizeof(WCHAR
);
3017 bufferW
= HeapAlloc( GetProcessHeap(), 0, alloclen
);
3018 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3019 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3020 MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, bufferW
, alloclen
/ sizeof(WCHAR
) );
3027 result
= HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, bufferW
,
3031 len
= WideCharToMultiByte( CP_ACP
,0, bufferW
, len
/ sizeof(WCHAR
) + 1,
3032 lpBuffer
, *lpdwBufferLength
, NULL
, NULL
);
3033 *lpdwBufferLength
= len
- 1;
3035 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer
));
3038 /* since the strings being returned from HttpQueryInfoW should be
3039 * only ASCII characters, it is reasonable to assume that all of
3040 * the Unicode characters can be reduced to a single byte */
3041 *lpdwBufferLength
= len
/ sizeof(WCHAR
);
3043 HeapFree(GetProcessHeap(), 0, bufferW
);
3048 /***********************************************************************
3049 * HttpSendRequestExA (WININET.@)
3051 * Sends the specified request to the HTTP server and allows chunked
3056 * Failure: FALSE, call GetLastError() for more information.
3058 BOOL WINAPI
HttpSendRequestExA(HINTERNET hRequest
,
3059 LPINTERNET_BUFFERSA lpBuffersIn
,
3060 LPINTERNET_BUFFERSA lpBuffersOut
,
3061 DWORD dwFlags
, DWORD_PTR dwContext
)
3063 INTERNET_BUFFERSW BuffersInW
;
3066 LPWSTR header
= NULL
;
3068 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
3069 lpBuffersOut
, dwFlags
, dwContext
);
3073 BuffersInW
.dwStructSize
= sizeof(LPINTERNET_BUFFERSW
);
3074 if (lpBuffersIn
->lpcszHeader
)
3076 headerlen
= MultiByteToWideChar(CP_ACP
,0,lpBuffersIn
->lpcszHeader
,
3077 lpBuffersIn
->dwHeadersLength
,0,0);
3078 header
= HeapAlloc(GetProcessHeap(),0,headerlen
*sizeof(WCHAR
));
3079 if (!(BuffersInW
.lpcszHeader
= header
))
3081 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
3084 BuffersInW
.dwHeadersLength
= MultiByteToWideChar(CP_ACP
, 0,
3085 lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
3089 BuffersInW
.lpcszHeader
= NULL
;
3090 BuffersInW
.dwHeadersTotal
= lpBuffersIn
->dwHeadersTotal
;
3091 BuffersInW
.lpvBuffer
= lpBuffersIn
->lpvBuffer
;
3092 BuffersInW
.dwBufferLength
= lpBuffersIn
->dwBufferLength
;
3093 BuffersInW
.dwBufferTotal
= lpBuffersIn
->dwBufferTotal
;
3094 BuffersInW
.Next
= NULL
;
3097 rc
= HttpSendRequestExW(hRequest
, lpBuffersIn
? &BuffersInW
: NULL
, NULL
, dwFlags
, dwContext
);
3099 HeapFree(GetProcessHeap(),0,header
);
3104 /***********************************************************************
3105 * HttpSendRequestExW (WININET.@)
3107 * Sends the specified request to the HTTP server and allows chunked
3112 * Failure: FALSE, call GetLastError() for more information.
3114 BOOL WINAPI
HttpSendRequestExW(HINTERNET hRequest
,
3115 LPINTERNET_BUFFERSW lpBuffersIn
,
3116 LPINTERNET_BUFFERSW lpBuffersOut
,
3117 DWORD dwFlags
, DWORD_PTR dwContext
)
3120 http_request_t
*lpwhr
;
3121 http_session_t
*lpwhs
;
3124 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
3125 lpBuffersOut
, dwFlags
, dwContext
);
3127 lpwhr
= (http_request_t
*) WININET_GetObject( hRequest
);
3129 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
3131 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
3135 lpwhs
= lpwhr
->lpHttpSession
;
3136 assert(lpwhs
->hdr
.htype
== WH_HHTTPSESSION
);
3137 hIC
= lpwhs
->lpAppInfo
;
3138 assert(hIC
->hdr
.htype
== WH_HINIT
);
3140 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
3142 WORKREQUEST workRequest
;
3143 struct WORKREQ_HTTPSENDREQUESTW
*req
;
3145 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
3146 workRequest
.hdr
= WININET_AddRef( &lpwhr
->hdr
);
3147 req
= &workRequest
.u
.HttpSendRequestW
;
3150 /* FIXME: this should use dwHeadersLength or may not be necessary at all */
3151 req
->lpszHeader
= heap_strdupW(lpBuffersIn
->lpcszHeader
);
3152 req
->dwHeaderLength
= lpBuffersIn
->dwHeadersLength
;
3153 req
->lpOptional
= lpBuffersIn
->lpvBuffer
;
3154 req
->dwOptionalLength
= lpBuffersIn
->dwBufferLength
;
3155 req
->dwContentLength
= lpBuffersIn
->dwBufferTotal
;
3159 req
->lpszHeader
= NULL
;
3160 req
->dwHeaderLength
= 0;
3161 req
->lpOptional
= NULL
;
3162 req
->dwOptionalLength
= 0;
3163 req
->dwContentLength
= 0;
3166 req
->bEndRequest
= FALSE
;
3168 INTERNET_AsyncCall(&workRequest
);
3170 * This is from windows.
3172 INTERNET_SetLastError(ERROR_IO_PENDING
);
3177 ret
= HTTP_HttpSendRequestW(lpwhr
, lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
3178 lpBuffersIn
->lpvBuffer
, lpBuffersIn
->dwBufferLength
,
3179 lpBuffersIn
->dwBufferTotal
, FALSE
);
3181 ret
= HTTP_HttpSendRequestW(lpwhr
, NULL
, 0, NULL
, 0, 0, FALSE
);
3186 WININET_Release( &lpwhr
->hdr
);
3192 /***********************************************************************
3193 * HttpSendRequestW (WININET.@)
3195 * Sends the specified request to the HTTP server
3202 BOOL WINAPI
HttpSendRequestW(HINTERNET hHttpRequest
, LPCWSTR lpszHeaders
,
3203 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
3205 http_request_t
*lpwhr
;
3206 http_session_t
*lpwhs
= NULL
;
3207 appinfo_t
*hIC
= NULL
;
3210 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest
,
3211 debugstr_wn(lpszHeaders
, dwHeaderLength
), dwHeaderLength
, lpOptional
, dwOptionalLength
);
3213 lpwhr
= (http_request_t
*) WININET_GetObject( hHttpRequest
);
3214 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
3216 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
3221 lpwhs
= lpwhr
->lpHttpSession
;
3222 if (NULL
== lpwhs
|| lpwhs
->hdr
.htype
!= WH_HHTTPSESSION
)
3224 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
3229 hIC
= lpwhs
->lpAppInfo
;
3230 if (NULL
== hIC
|| hIC
->hdr
.htype
!= WH_HINIT
)
3232 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
3237 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
3239 WORKREQUEST workRequest
;
3240 struct WORKREQ_HTTPSENDREQUESTW
*req
;
3242 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
3243 workRequest
.hdr
= WININET_AddRef( &lpwhr
->hdr
);
3244 req
= &workRequest
.u
.HttpSendRequestW
;
3249 if (dwHeaderLength
== ~0u) size
= (strlenW(lpszHeaders
) + 1) * sizeof(WCHAR
);
3250 else size
= dwHeaderLength
* sizeof(WCHAR
);
3252 req
->lpszHeader
= HeapAlloc(GetProcessHeap(), 0, size
);
3253 memcpy(req
->lpszHeader
, lpszHeaders
, size
);
3256 req
->lpszHeader
= 0;
3257 req
->dwHeaderLength
= dwHeaderLength
;
3258 req
->lpOptional
= lpOptional
;
3259 req
->dwOptionalLength
= dwOptionalLength
;
3260 req
->dwContentLength
= dwOptionalLength
;
3261 req
->bEndRequest
= TRUE
;
3263 INTERNET_AsyncCall(&workRequest
);
3265 * This is from windows.
3267 INTERNET_SetLastError(ERROR_IO_PENDING
);
3272 r
= HTTP_HttpSendRequestW(lpwhr
, lpszHeaders
,
3273 dwHeaderLength
, lpOptional
, dwOptionalLength
,
3274 dwOptionalLength
, TRUE
);
3278 WININET_Release( &lpwhr
->hdr
);
3282 /***********************************************************************
3283 * HttpSendRequestA (WININET.@)
3285 * Sends the specified request to the HTTP server
3292 BOOL WINAPI
HttpSendRequestA(HINTERNET hHttpRequest
, LPCSTR lpszHeaders
,
3293 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
3296 LPWSTR szHeaders
=NULL
;
3297 DWORD nLen
=dwHeaderLength
;
3298 if(lpszHeaders
!=NULL
)
3300 nLen
=MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,NULL
,0);
3301 szHeaders
=HeapAlloc(GetProcessHeap(),0,nLen
*sizeof(WCHAR
));
3302 MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,szHeaders
,nLen
);
3304 result
=HttpSendRequestW(hHttpRequest
, szHeaders
, nLen
, lpOptional
, dwOptionalLength
);
3305 HeapFree(GetProcessHeap(),0,szHeaders
);
3309 /***********************************************************************
3310 * HTTP_GetRedirectURL (internal)
3312 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*lpwhr
, LPCWSTR lpszUrl
)
3314 static WCHAR szHttp
[] = {'h','t','t','p',0};
3315 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3316 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
3317 URL_COMPONENTSW urlComponents
;
3318 DWORD url_length
= 0;
3320 LPWSTR combined_url
;
3322 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3323 urlComponents
.lpszScheme
= (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) ? szHttps
: szHttp
;
3324 urlComponents
.dwSchemeLength
= 0;
3325 urlComponents
.lpszHostName
= lpwhs
->lpszHostName
;
3326 urlComponents
.dwHostNameLength
= 0;
3327 urlComponents
.nPort
= lpwhs
->nHostPort
;
3328 urlComponents
.lpszUserName
= lpwhs
->lpszUserName
;
3329 urlComponents
.dwUserNameLength
= 0;
3330 urlComponents
.lpszPassword
= NULL
;
3331 urlComponents
.dwPasswordLength
= 0;
3332 urlComponents
.lpszUrlPath
= lpwhr
->lpszPath
;
3333 urlComponents
.dwUrlPathLength
= 0;
3334 urlComponents
.lpszExtraInfo
= NULL
;
3335 urlComponents
.dwExtraInfoLength
= 0;
3337 if (!InternetCreateUrlW(&urlComponents
, 0, NULL
, &url_length
) &&
3338 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3341 orig_url
= HeapAlloc(GetProcessHeap(), 0, url_length
);
3343 /* convert from bytes to characters */
3344 url_length
= url_length
/ sizeof(WCHAR
) - 1;
3345 if (!InternetCreateUrlW(&urlComponents
, 0, orig_url
, &url_length
))
3347 HeapFree(GetProcessHeap(), 0, orig_url
);
3352 if (!InternetCombineUrlW(orig_url
, lpszUrl
, NULL
, &url_length
, ICU_ENCODE_SPACES_ONLY
) &&
3353 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3355 HeapFree(GetProcessHeap(), 0, orig_url
);
3358 combined_url
= HeapAlloc(GetProcessHeap(), 0, url_length
* sizeof(WCHAR
));
3360 if (!InternetCombineUrlW(orig_url
, lpszUrl
, combined_url
, &url_length
, ICU_ENCODE_SPACES_ONLY
))
3362 HeapFree(GetProcessHeap(), 0, orig_url
);
3363 HeapFree(GetProcessHeap(), 0, combined_url
);
3366 HeapFree(GetProcessHeap(), 0, orig_url
);
3367 return combined_url
;
3371 /***********************************************************************
3372 * HTTP_HandleRedirect (internal)
3374 static BOOL
HTTP_HandleRedirect(http_request_t
*lpwhr
, LPCWSTR lpszUrl
)
3376 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
3377 appinfo_t
*hIC
= lpwhs
->lpAppInfo
;
3378 BOOL using_proxy
= hIC
->lpszProxy
&& hIC
->lpszProxy
[0];
3379 WCHAR path
[INTERNET_MAX_URL_LENGTH
];
3384 /* if it's an absolute path, keep the same session info */
3385 lstrcpynW(path
, lpszUrl
, INTERNET_MAX_URL_LENGTH
);
3389 URL_COMPONENTSW urlComponents
;
3390 WCHAR protocol
[32], hostName
[MAXHOSTNAME
], userName
[1024];
3391 static WCHAR szHttp
[] = {'h','t','t','p',0};
3392 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3398 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3399 urlComponents
.lpszScheme
= protocol
;
3400 urlComponents
.dwSchemeLength
= 32;
3401 urlComponents
.lpszHostName
= hostName
;
3402 urlComponents
.dwHostNameLength
= MAXHOSTNAME
;
3403 urlComponents
.lpszUserName
= userName
;
3404 urlComponents
.dwUserNameLength
= 1024;
3405 urlComponents
.lpszPassword
= NULL
;
3406 urlComponents
.dwPasswordLength
= 0;
3407 urlComponents
.lpszUrlPath
= path
;
3408 urlComponents
.dwUrlPathLength
= 2048;
3409 urlComponents
.lpszExtraInfo
= NULL
;
3410 urlComponents
.dwExtraInfoLength
= 0;
3411 if(!InternetCrackUrlW(lpszUrl
, strlenW(lpszUrl
), 0, &urlComponents
))
3414 if (!strncmpW(szHttp
, urlComponents
.lpszScheme
, strlenW(szHttp
)) &&
3415 (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
))
3417 TRACE("redirect from secure page to non-secure page\n");
3418 /* FIXME: warn about from secure redirect to non-secure page */
3419 lpwhr
->hdr
.dwFlags
&= ~INTERNET_FLAG_SECURE
;
3421 if (!strncmpW(szHttps
, urlComponents
.lpszScheme
, strlenW(szHttps
)) &&
3422 !(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
))
3424 TRACE("redirect from non-secure page to secure page\n");
3425 /* FIXME: notify about redirect to secure page */
3426 lpwhr
->hdr
.dwFlags
|= INTERNET_FLAG_SECURE
;
3429 if (urlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
3431 if (lstrlenW(protocol
)>4) /*https*/
3432 urlComponents
.nPort
= INTERNET_DEFAULT_HTTPS_PORT
;
3434 urlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
3439 * This upsets redirects to binary files on sourceforge.net
3440 * and gives an html page instead of the target file
3441 * Examination of the HTTP request sent by native wininet.dll
3442 * reveals that it doesn't send a referrer in that case.
3443 * Maybe there's a flag that enables this, or maybe a referrer
3444 * shouldn't be added in case of a redirect.
3447 /* consider the current host as the referrer */
3448 if (lpwhs
->lpszServerName
&& *lpwhs
->lpszServerName
)
3449 HTTP_ProcessHeader(lpwhr
, HTTP_REFERER
, lpwhs
->lpszServerName
,
3450 HTTP_ADDHDR_FLAG_REQ
|HTTP_ADDREQ_FLAG_REPLACE
|
3451 HTTP_ADDHDR_FLAG_ADD_IF_NEW
);
3454 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszHostName
);
3455 if (urlComponents
.nPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
3456 urlComponents
.nPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
3459 static const WCHAR fmt
[] = {'%','s',':','%','i',0};
3460 len
= lstrlenW(hostName
);
3461 len
+= 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3462 lpwhs
->lpszHostName
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
3463 sprintfW(lpwhs
->lpszHostName
, fmt
, hostName
, urlComponents
.nPort
);
3466 lpwhs
->lpszHostName
= heap_strdupW(hostName
);
3468 HTTP_ProcessHeader(lpwhr
, hostW
, lpwhs
->lpszHostName
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDREQ_FLAG_REPLACE
| HTTP_ADDHDR_FLAG_REQ
);
3470 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszUserName
);
3471 lpwhs
->lpszUserName
= NULL
;
3473 lpwhs
->lpszUserName
= heap_strdupW(userName
);
3477 if (strcmpiW(lpwhs
->lpszServerName
, hostName
) || lpwhs
->nServerPort
!= urlComponents
.nPort
)
3479 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszServerName
);
3480 lpwhs
->lpszServerName
= heap_strdupW(hostName
);
3481 lpwhs
->nServerPort
= urlComponents
.nPort
;
3483 NETCON_close(&lpwhr
->netConnection
);
3484 if (!HTTP_ResolveName(lpwhr
)) return FALSE
;
3485 if (!NETCON_init(&lpwhr
->netConnection
, lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)) return FALSE
;
3486 lpwhr
->read_pos
= lpwhr
->read_size
= 0;
3487 lpwhr
->read_chunked
= FALSE
;
3491 TRACE("Redirect through proxy\n");
3494 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszPath
);
3495 lpwhr
->lpszPath
=NULL
;
3501 rc
= UrlEscapeW(path
, NULL
, &needed
, URL_ESCAPE_SPACES_ONLY
);
3502 if (rc
!= E_POINTER
)
3503 needed
= strlenW(path
)+1;
3504 lpwhr
->lpszPath
= HeapAlloc(GetProcessHeap(), 0, needed
*sizeof(WCHAR
));
3505 rc
= UrlEscapeW(path
, lpwhr
->lpszPath
, &needed
,
3506 URL_ESCAPE_SPACES_ONLY
);
3509 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path
),rc
);
3510 strcpyW(lpwhr
->lpszPath
,path
);
3514 /* Remove custom content-type/length headers on redirects. */
3515 index
= HTTP_GetCustomHeaderIndex(lpwhr
, szContent_Type
, 0, TRUE
);
3517 HTTP_DeleteCustomHeader(lpwhr
, index
);
3518 index
= HTTP_GetCustomHeaderIndex(lpwhr
, szContent_Length
, 0, TRUE
);
3520 HTTP_DeleteCustomHeader(lpwhr
, index
);
3525 /***********************************************************************
3526 * HTTP_build_req (internal)
3528 * concatenate all the strings in the request together
3530 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
)
3535 for( t
= list
; *t
; t
++ )
3536 len
+= strlenW( *t
);
3539 str
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
3542 for( t
= list
; *t
; t
++ )
3548 static BOOL
HTTP_SecureProxyConnect(http_request_t
*lpwhr
)
3551 LPWSTR requestString
;
3557 static const WCHAR szConnect
[] = {'C','O','N','N','E','C','T',0};
3558 static const WCHAR szFormat
[] = {'%','s',':','%','d',0};
3559 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
3563 lpszPath
= HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs
->lpszHostName
) + 13)*sizeof(WCHAR
) );
3564 sprintfW( lpszPath
, szFormat
, lpwhs
->lpszHostName
, lpwhs
->nHostPort
);
3565 requestString
= HTTP_BuildHeaderRequestString( lpwhr
, szConnect
, lpszPath
, g_szHttp1_1
);
3566 HeapFree( GetProcessHeap(), 0, lpszPath
);
3568 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3569 NULL
, 0, NULL
, NULL
);
3570 len
--; /* the nul terminator isn't needed */
3571 ascii_req
= HeapAlloc( GetProcessHeap(), 0, len
);
3572 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3573 ascii_req
, len
, NULL
, NULL
);
3574 HeapFree( GetProcessHeap(), 0, requestString
);
3576 TRACE("full request -> %s\n", debugstr_an( ascii_req
, len
) );
3578 ret
= NETCON_send( &lpwhr
->netConnection
, ascii_req
, len
, 0, &cnt
);
3579 HeapFree( GetProcessHeap(), 0, ascii_req
);
3580 if (!ret
|| cnt
< 0)
3583 responseLen
= HTTP_GetResponseHeaders( lpwhr
, TRUE
);
3590 static void HTTP_InsertCookies(http_request_t
*lpwhr
)
3592 static const WCHAR szUrlForm
[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3593 LPWSTR lpszCookies
, lpszUrl
= NULL
;
3594 DWORD nCookieSize
, size
;
3595 LPHTTPHEADERW Host
= HTTP_GetHeader(lpwhr
, hostW
);
3597 size
= (strlenW(Host
->lpszValue
) + strlenW(szUrlForm
) + strlenW(lpwhr
->lpszPath
)) * sizeof(WCHAR
);
3598 if (!(lpszUrl
= HeapAlloc(GetProcessHeap(), 0, size
))) return;
3599 sprintfW( lpszUrl
, szUrlForm
, Host
->lpszValue
, lpwhr
->lpszPath
);
3601 if (InternetGetCookieW(lpszUrl
, NULL
, NULL
, &nCookieSize
))
3604 static const WCHAR szCookie
[] = {'C','o','o','k','i','e',':',' ',0};
3606 size
= sizeof(szCookie
) + nCookieSize
* sizeof(WCHAR
) + sizeof(szCrLf
);
3607 if ((lpszCookies
= HeapAlloc(GetProcessHeap(), 0, size
)))
3609 cnt
+= sprintfW(lpszCookies
, szCookie
);
3610 InternetGetCookieW(lpszUrl
, NULL
, lpszCookies
+ cnt
, &nCookieSize
);
3611 strcatW(lpszCookies
, szCrLf
);
3613 HTTP_HttpAddRequestHeadersW(lpwhr
, lpszCookies
, strlenW(lpszCookies
), HTTP_ADDREQ_FLAG_REPLACE
);
3614 HeapFree(GetProcessHeap(), 0, lpszCookies
);
3617 HeapFree(GetProcessHeap(), 0, lpszUrl
);
3620 /***********************************************************************
3621 * HTTP_HttpSendRequestW (internal)
3623 * Sends the specified request to the HTTP server
3630 BOOL WINAPI
HTTP_HttpSendRequestW(http_request_t
*lpwhr
, LPCWSTR lpszHeaders
,
3631 DWORD dwHeaderLength
, LPVOID lpOptional
, DWORD dwOptionalLength
,
3632 DWORD dwContentLength
, BOOL bEndRequest
)
3635 BOOL bSuccess
= FALSE
, redirected
= FALSE
;
3636 LPWSTR requestString
= NULL
;
3639 INTERNET_ASYNC_RESULT iar
;
3640 static const WCHAR szPost
[] = { 'P','O','S','T',0 };
3641 static const WCHAR szContentLength
[] =
3642 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3643 WCHAR contentLengthStr
[sizeof szContentLength
/2 /* includes \r\n */ + 20 /* int */ ];
3645 TRACE("--> %p\n", lpwhr
);
3647 assert(lpwhr
->hdr
.htype
== WH_HHTTPREQ
);
3649 /* if the verb is NULL default to GET */
3650 if (!lpwhr
->lpszVerb
)
3651 lpwhr
->lpszVerb
= heap_strdupW(szGET
);
3653 if (dwContentLength
|| strcmpW(lpwhr
->lpszVerb
, szGET
))
3655 sprintfW(contentLengthStr
, szContentLength
, dwContentLength
);
3656 HTTP_HttpAddRequestHeadersW(lpwhr
, contentLengthStr
, -1L, HTTP_ADDREQ_FLAG_REPLACE
);
3657 lpwhr
->dwBytesToWrite
= dwContentLength
;
3659 if (lpwhr
->lpHttpSession
->lpAppInfo
->lpszAgent
)
3661 WCHAR
*agent_header
;
3662 static const WCHAR user_agent
[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3665 len
= strlenW(lpwhr
->lpHttpSession
->lpAppInfo
->lpszAgent
) + strlenW(user_agent
);
3666 agent_header
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
3667 sprintfW(agent_header
, user_agent
, lpwhr
->lpHttpSession
->lpAppInfo
->lpszAgent
);
3669 HTTP_HttpAddRequestHeadersW(lpwhr
, agent_header
, strlenW(agent_header
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
3670 HeapFree(GetProcessHeap(), 0, agent_header
);
3672 if (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_PRAGMA_NOCACHE
)
3674 static const WCHAR pragma_nocache
[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3675 HTTP_HttpAddRequestHeadersW(lpwhr
, pragma_nocache
, strlenW(pragma_nocache
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
3677 if ((lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_CACHE_WRITE
) && !strcmpW(lpwhr
->lpszVerb
, szPost
))
3679 static const WCHAR cache_control
[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3680 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3681 HTTP_HttpAddRequestHeadersW(lpwhr
, cache_control
, strlenW(cache_control
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
3691 /* like native, just in case the caller forgot to call InternetReadFile
3692 * for all the data */
3693 HTTP_DrainContent(lpwhr
);
3694 lpwhr
->dwContentRead
= 0;
3696 if (TRACE_ON(wininet
))
3698 LPHTTPHEADERW Host
= HTTP_GetHeader(lpwhr
, hostW
);
3699 TRACE("Going to url %s %s\n", debugstr_w(Host
->lpszValue
), debugstr_w(lpwhr
->lpszPath
));
3703 if (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_KEEP_CONNECTION
)
3705 HTTP_ProcessHeader(lpwhr
, szConnection
, szKeepAlive
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
3707 HTTP_InsertAuthorization(lpwhr
, lpwhr
->pAuthInfo
, szAuthorization
);
3708 HTTP_InsertAuthorization(lpwhr
, lpwhr
->pProxyAuthInfo
, szProxy_Authorization
);
3710 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
))
3711 HTTP_InsertCookies(lpwhr
);
3713 /* add the headers the caller supplied */
3714 if( lpszHeaders
&& dwHeaderLength
)
3716 HTTP_HttpAddRequestHeadersW(lpwhr
, lpszHeaders
, dwHeaderLength
,
3717 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REPLACE
);
3720 if (lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxy
&& lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxy
[0])
3722 WCHAR
*url
= HTTP_BuildProxyRequestUrl(lpwhr
);
3723 requestString
= HTTP_BuildHeaderRequestString(lpwhr
, lpwhr
->lpszVerb
, url
, lpwhr
->lpszVersion
);
3724 HeapFree(GetProcessHeap(), 0, url
);
3727 requestString
= HTTP_BuildHeaderRequestString(lpwhr
, lpwhr
->lpszVerb
, lpwhr
->lpszPath
, lpwhr
->lpszVersion
);
3730 TRACE("Request header -> %s\n", debugstr_w(requestString
) );
3732 /* Send the request and store the results */
3733 if (!HTTP_OpenConnection(lpwhr
))
3736 /* send the request as ASCII, tack on the optional data */
3737 if (!lpOptional
|| redirected
)
3738 dwOptionalLength
= 0;
3739 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3740 NULL
, 0, NULL
, NULL
);
3741 ascii_req
= HeapAlloc( GetProcessHeap(), 0, len
+ dwOptionalLength
);
3742 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3743 ascii_req
, len
, NULL
, NULL
);
3745 memcpy( &ascii_req
[len
-1], lpOptional
, dwOptionalLength
);
3746 len
= (len
+ dwOptionalLength
- 1);
3748 TRACE("full request -> %s\n", debugstr_a(ascii_req
) );
3750 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3751 INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
3753 NETCON_send(&lpwhr
->netConnection
, ascii_req
, len
, 0, &cnt
);
3754 HeapFree( GetProcessHeap(), 0, ascii_req
);
3756 lpwhr
->dwBytesWritten
= dwOptionalLength
;
3758 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3759 INTERNET_STATUS_REQUEST_SENT
,
3760 &len
, sizeof(DWORD
));
3767 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3768 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
3773 responseLen
= HTTP_GetResponseHeaders(lpwhr
, TRUE
);
3777 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3778 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
,
3781 HTTP_ProcessCookies(lpwhr
);
3783 if (!set_content_length( lpwhr
)) HTTP_FinishedReading(lpwhr
);
3785 dwBufferSize
= sizeof(dwStatusCode
);
3786 if (!HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_STATUS_CODE
,
3787 &dwStatusCode
,&dwBufferSize
,NULL
))
3790 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
) && bSuccess
)
3792 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
3793 dwBufferSize
=sizeof(szNewLocation
);
3794 if ((dwStatusCode
==HTTP_STATUS_REDIRECT
|| dwStatusCode
==HTTP_STATUS_MOVED
) &&
3795 HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_LOCATION
,szNewLocation
,&dwBufferSize
,NULL
))
3797 if (strcmpW(lpwhr
->lpszVerb
, szGET
) && strcmpW(lpwhr
->lpszVerb
, szHEAD
))
3799 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVerb
);
3800 lpwhr
->lpszVerb
= heap_strdupW(szGET
);
3802 HTTP_DrainContent(lpwhr
);
3803 if ((new_url
= HTTP_GetRedirectURL( lpwhr
, szNewLocation
)))
3805 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
3806 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
3807 bSuccess
= HTTP_HandleRedirect(lpwhr
, new_url
);
3810 HeapFree(GetProcessHeap(), 0, requestString
);
3813 HeapFree( GetProcessHeap(), 0, new_url
);
3818 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTH
) && bSuccess
)
3820 WCHAR szAuthValue
[2048];
3822 if (dwStatusCode
== HTTP_STATUS_DENIED
)
3825 while (HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_WWW_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
))
3827 if (HTTP_DoAuthorization(lpwhr
, szAuthValue
,
3829 lpwhr
->lpHttpSession
->lpszUserName
,
3830 lpwhr
->lpHttpSession
->lpszPassword
))
3837 if (dwStatusCode
== HTTP_STATUS_PROXY_AUTH_REQ
)
3840 while (HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_PROXY_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
))
3842 if (HTTP_DoAuthorization(lpwhr
, szAuthValue
,
3843 &lpwhr
->pProxyAuthInfo
,
3844 lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxyUsername
,
3845 lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxyPassword
))
3860 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
3861 WCHAR cacheFileName
[MAX_PATH
+1];
3864 b
= HTTP_GetRequestURL(lpwhr
, url
);
3866 WARN("Could not get URL\n");
3870 b
= CreateUrlCacheEntryW(url
, lpwhr
->dwContentLength
> 0 ? lpwhr
->dwContentLength
: 0, NULL
, cacheFileName
, 0);
3872 lpwhr
->lpszCacheFile
= heap_strdupW(cacheFileName
);
3873 lpwhr
->hCacheFile
= CreateFileW(lpwhr
->lpszCacheFile
, GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
3874 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
3875 if(lpwhr
->hCacheFile
== INVALID_HANDLE_VALUE
) {
3876 WARN("Could not create file: %u\n", GetLastError());
3877 lpwhr
->hCacheFile
= NULL
;
3880 WARN("Could not create cache entry: %08x\n", GetLastError());
3886 HeapFree(GetProcessHeap(), 0, requestString
);
3888 /* TODO: send notification for P3P header */
3890 if (lpwhr
->lpHttpSession
->lpAppInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
3894 if (lpwhr
->dwBytesWritten
== lpwhr
->dwBytesToWrite
) HTTP_ReceiveRequestData(lpwhr
, TRUE
);
3897 iar
.dwResult
= (DWORD_PTR
)lpwhr
->hdr
.hInternet
;
3900 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3901 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
3902 sizeof(INTERNET_ASYNC_RESULT
));
3907 iar
.dwResult
= (DWORD_PTR
)lpwhr
->hdr
.hInternet
;
3908 iar
.dwError
= INTERNET_GetLastError();
3910 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3911 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
3912 sizeof(INTERNET_ASYNC_RESULT
));
3917 if (bSuccess
) INTERNET_SetLastError(ERROR_SUCCESS
);
3921 /***********************************************************************
3922 * HTTPSESSION_Destroy (internal)
3924 * Deallocate session handle
3927 static void HTTPSESSION_Destroy(object_header_t
*hdr
)
3929 http_session_t
*lpwhs
= (http_session_t
*) hdr
;
3931 TRACE("%p\n", lpwhs
);
3933 WININET_Release(&lpwhs
->lpAppInfo
->hdr
);
3935 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszHostName
);
3936 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszServerName
);
3937 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszPassword
);
3938 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszUserName
);
3939 HeapFree(GetProcessHeap(), 0, lpwhs
);
3942 static DWORD
HTTPSESSION_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
3945 case INTERNET_OPTION_HANDLE_TYPE
:
3946 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3948 if (*size
< sizeof(ULONG
))
3949 return ERROR_INSUFFICIENT_BUFFER
;
3951 *size
= sizeof(DWORD
);
3952 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_CONNECT_HTTP
;
3953 return ERROR_SUCCESS
;
3956 return INET_QueryOption(option
, buffer
, size
, unicode
);
3959 static DWORD
HTTPSESSION_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
3961 http_session_t
*ses
= (http_session_t
*)hdr
;
3964 case INTERNET_OPTION_USERNAME
:
3966 HeapFree(GetProcessHeap(), 0, ses
->lpszUserName
);
3967 if (!(ses
->lpszUserName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
3968 return ERROR_SUCCESS
;
3970 case INTERNET_OPTION_PASSWORD
:
3972 HeapFree(GetProcessHeap(), 0, ses
->lpszPassword
);
3973 if (!(ses
->lpszPassword
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
3974 return ERROR_SUCCESS
;
3979 return ERROR_INTERNET_INVALID_OPTION
;
3982 static const object_vtbl_t HTTPSESSIONVtbl
= {
3983 HTTPSESSION_Destroy
,
3985 HTTPSESSION_QueryOption
,
3986 HTTPSESSION_SetOption
,
3995 /***********************************************************************
3996 * HTTP_Connect (internal)
3998 * Create http session handle
4001 * HINTERNET a session handle on success
4005 HINTERNET
HTTP_Connect(appinfo_t
*hIC
, LPCWSTR lpszServerName
,
4006 INTERNET_PORT nServerPort
, LPCWSTR lpszUserName
,
4007 LPCWSTR lpszPassword
, DWORD dwFlags
, DWORD_PTR dwContext
,
4008 DWORD dwInternalFlags
)
4010 http_session_t
*lpwhs
= NULL
;
4011 HINTERNET handle
= NULL
;
4015 if (!lpszServerName
|| !lpszServerName
[0])
4017 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
4021 assert( hIC
->hdr
.htype
== WH_HINIT
);
4023 lpwhs
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(http_session_t
));
4026 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
4031 * According to my tests. The name is not resolved until a request is sent
4034 lpwhs
->hdr
.htype
= WH_HHTTPSESSION
;
4035 lpwhs
->hdr
.vtbl
= &HTTPSESSIONVtbl
;
4036 lpwhs
->hdr
.dwFlags
= dwFlags
;
4037 lpwhs
->hdr
.dwContext
= dwContext
;
4038 lpwhs
->hdr
.dwInternalFlags
= dwInternalFlags
| (hIC
->hdr
.dwInternalFlags
& INET_CALLBACKW
);
4039 lpwhs
->hdr
.refs
= 1;
4040 lpwhs
->hdr
.lpfnStatusCB
= hIC
->hdr
.lpfnStatusCB
;
4042 WININET_AddRef( &hIC
->hdr
);
4043 lpwhs
->lpAppInfo
= hIC
;
4044 list_add_head( &hIC
->hdr
.children
, &lpwhs
->hdr
.entry
);
4046 handle
= WININET_AllocHandle( &lpwhs
->hdr
);
4049 ERR("Failed to alloc handle\n");
4050 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
4054 if(hIC
->lpszProxy
&& hIC
->dwAccessType
== INTERNET_OPEN_TYPE_PROXY
) {
4055 if(strchrW(hIC
->lpszProxy
, ' '))
4056 FIXME("Several proxies not implemented.\n");
4057 if(hIC
->lpszProxyBypass
)
4058 FIXME("Proxy bypass is ignored.\n");
4060 if (lpszServerName
&& lpszServerName
[0])
4062 lpwhs
->lpszServerName
= heap_strdupW(lpszServerName
);
4063 lpwhs
->lpszHostName
= heap_strdupW(lpszServerName
);
4065 if (lpszUserName
&& lpszUserName
[0])
4066 lpwhs
->lpszUserName
= heap_strdupW(lpszUserName
);
4067 if (lpszPassword
&& lpszPassword
[0])
4068 lpwhs
->lpszPassword
= heap_strdupW(lpszPassword
);
4069 lpwhs
->nServerPort
= nServerPort
;
4070 lpwhs
->nHostPort
= nServerPort
;
4072 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4073 if (!(lpwhs
->hdr
.dwInternalFlags
& INET_OPENURL
))
4075 INTERNET_SendCallback(&hIC
->hdr
, dwContext
,
4076 INTERNET_STATUS_HANDLE_CREATED
, &handle
,
4082 WININET_Release( &lpwhs
->hdr
);
4085 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4089 TRACE("%p --> %p (%p)\n", hIC
, handle
, lpwhs
);
4094 /***********************************************************************
4095 * HTTP_OpenConnection (internal)
4097 * Connect to a web server
4104 static BOOL
HTTP_OpenConnection(http_request_t
*lpwhr
)
4106 BOOL bSuccess
= FALSE
;
4107 http_session_t
*lpwhs
;
4108 appinfo_t
*hIC
= NULL
;
4109 char szaddr
[INET6_ADDRSTRLEN
];
4115 if (lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
4117 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
4121 if (NETCON_connected(&lpwhr
->netConnection
))
4126 if (!HTTP_ResolveName(lpwhr
)) goto lend
;
4128 lpwhs
= lpwhr
->lpHttpSession
;
4130 hIC
= lpwhs
->lpAppInfo
;
4131 switch (lpwhs
->socketAddress
.ss_family
)
4134 addr
= &((struct sockaddr_in
*)&lpwhs
->socketAddress
)->sin_addr
;
4137 addr
= &((struct sockaddr_in6
*)&lpwhs
->socketAddress
)->sin6_addr
;
4140 WARN("unsupported family %d\n", lpwhs
->socketAddress
.ss_family
);
4141 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED
);
4144 inet_ntop(lpwhs
->socketAddress
.ss_family
, addr
, szaddr
, sizeof(szaddr
));
4145 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
4146 INTERNET_STATUS_CONNECTING_TO_SERVER
,
4150 if (!NETCON_create(&lpwhr
->netConnection
, lpwhs
->socketAddress
.ss_family
,
4153 WARN("Socket creation failed: %u\n", INTERNET_GetLastError());
4157 if (!NETCON_connect(&lpwhr
->netConnection
, (struct sockaddr
*)&lpwhs
->socketAddress
,
4161 if (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
4163 /* Note: we differ from Microsoft's WinINet here. they seem to have
4164 * a bug that causes no status callbacks to be sent when starting
4165 * a tunnel to a proxy server using the CONNECT verb. i believe our
4166 * behaviour to be more correct and to not cause any incompatibilities
4167 * because using a secure connection through a proxy server is a rare
4168 * case that would be hard for anyone to depend on */
4169 if (hIC
->lpszProxy
&& !HTTP_SecureProxyConnect(lpwhr
))
4172 if (!NETCON_secure_connect(&lpwhr
->netConnection
, lpwhs
->lpszHostName
))
4174 WARN("Couldn't connect securely to host\n");
4179 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
4180 INTERNET_STATUS_CONNECTED_TO_SERVER
,
4181 szaddr
, strlen(szaddr
)+1);
4186 lpwhr
->read_pos
= lpwhr
->read_size
= 0;
4187 lpwhr
->read_chunked
= FALSE
;
4189 TRACE("%d <--\n", bSuccess
);
4194 /***********************************************************************
4195 * HTTP_clear_response_headers (internal)
4197 * clear out any old response headers
4199 static void HTTP_clear_response_headers( http_request_t
*lpwhr
)
4203 for( i
=0; i
<lpwhr
->nCustHeaders
; i
++)
4205 if( !lpwhr
->pCustHeaders
[i
].lpszField
)
4207 if( !lpwhr
->pCustHeaders
[i
].lpszValue
)
4209 if ( lpwhr
->pCustHeaders
[i
].wFlags
& HDR_ISREQUEST
)
4211 HTTP_DeleteCustomHeader( lpwhr
, i
);
4216 /***********************************************************************
4217 * HTTP_GetResponseHeaders (internal)
4219 * Read server response
4226 static INT
HTTP_GetResponseHeaders(http_request_t
*lpwhr
, BOOL clear
)
4229 WCHAR buffer
[MAX_REPLY_LEN
];
4230 DWORD buflen
= MAX_REPLY_LEN
;
4231 BOOL bSuccess
= FALSE
;
4233 char bufferA
[MAX_REPLY_LEN
];
4234 LPWSTR status_code
= NULL
, status_text
= NULL
;
4235 DWORD cchMaxRawHeaders
= 1024;
4236 LPWSTR lpszRawHeaders
= HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
4238 DWORD cchRawHeaders
= 0;
4239 BOOL codeHundred
= FALSE
;
4243 /* clear old response headers (eg. from a redirect response) */
4244 if (clear
) HTTP_clear_response_headers( lpwhr
);
4246 if (!NETCON_connected(&lpwhr
->netConnection
))
4250 static const WCHAR szHundred
[] = {'1','0','0',0};
4252 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4254 buflen
= MAX_REPLY_LEN
;
4255 if (!read_line(lpwhr
, bufferA
, &buflen
))
4258 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
4259 /* check is this a status code line? */
4260 if (!strncmpW(buffer
, g_szHttp1_0
, 4))
4262 /* split the version from the status code */
4263 status_code
= strchrW( buffer
, ' ' );
4268 /* split the status code from the status text */
4269 status_text
= strchrW( status_code
, ' ' );
4274 TRACE("version [%s] status code [%s] status text [%s]\n",
4275 debugstr_w(buffer
), debugstr_w(status_code
), debugstr_w(status_text
) );
4277 codeHundred
= (!strcmpW(status_code
, szHundred
));
4279 else if (!codeHundred
)
4281 FIXME("Non status line at head of response (%s)\n",debugstr_w(buffer
));
4284 } while (codeHundred
);
4286 /* Add status code */
4287 HTTP_ProcessHeader(lpwhr
, szStatus
, status_code
,
4288 HTTP_ADDHDR_FLAG_REPLACE
);
4290 HeapFree(GetProcessHeap(),0,lpwhr
->lpszVersion
);
4291 HeapFree(GetProcessHeap(),0,lpwhr
->lpszStatusText
);
4293 lpwhr
->lpszVersion
= heap_strdupW(buffer
);
4294 lpwhr
->lpszStatusText
= heap_strdupW(status_text
);
4296 /* Restore the spaces */
4297 *(status_code
-1) = ' ';
4298 *(status_text
-1) = ' ';
4300 /* regenerate raw headers */
4301 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
4302 cchMaxRawHeaders
*= 2;
4303 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
4304 if (temp
== NULL
) goto lend
;
4305 lpszRawHeaders
= temp
;
4306 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
4307 cchRawHeaders
+= (buflen
-1);
4308 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
4309 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
4310 lpszRawHeaders
[cchRawHeaders
] = '\0';
4312 /* Parse each response line */
4315 buflen
= MAX_REPLY_LEN
;
4316 if (read_line(lpwhr
, bufferA
, &buflen
))
4318 LPWSTR
* pFieldAndValue
;
4320 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA
));
4322 if (!bufferA
[0]) break;
4323 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
4325 pFieldAndValue
= HTTP_InterpretHttpHeader(buffer
);
4328 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
4329 cchMaxRawHeaders
*= 2;
4330 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
4331 if (temp
== NULL
) goto lend
;
4332 lpszRawHeaders
= temp
;
4333 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
4334 cchRawHeaders
+= (buflen
-1);
4335 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
4336 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
4337 lpszRawHeaders
[cchRawHeaders
] = '\0';
4339 HTTP_ProcessHeader(lpwhr
, pFieldAndValue
[0], pFieldAndValue
[1],
4340 HTTP_ADDREQ_FLAG_ADD
);
4342 HTTP_FreeTokens(pFieldAndValue
);
4353 /* make sure the response header is terminated with an empty line. Some apps really
4354 truly care about that empty line being there for some reason. Just add it to the
4356 if (cchRawHeaders
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
4358 cchMaxRawHeaders
= cchRawHeaders
+ strlenW(szCrLf
);
4359 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
4360 if (temp
== NULL
) goto lend
;
4361 lpszRawHeaders
= temp
;
4364 memcpy(&lpszRawHeaders
[cchRawHeaders
], szCrLf
, sizeof(szCrLf
));
4366 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszRawHeaders
);
4367 lpwhr
->lpszRawHeaders
= lpszRawHeaders
;
4368 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders
));
4378 HeapFree(GetProcessHeap(), 0, lpszRawHeaders
);
4384 static void strip_spaces(LPWSTR start
)
4389 while (*str
== ' ' && *str
!= '\0')
4393 memmove(start
, str
, sizeof(WCHAR
) * (strlenW(str
) + 1));
4395 end
= start
+ strlenW(start
) - 1;
4396 while (end
>= start
&& *end
== ' ')
4404 /***********************************************************************
4405 * HTTP_InterpretHttpHeader (internal)
4407 * Parse server response
4411 * Pointer to array of field, value, NULL on success.
4414 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
)
4416 LPWSTR
* pTokenPair
;
4420 pTokenPair
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*pTokenPair
)*3);
4422 pszColon
= strchrW(buffer
, ':');
4423 /* must have two tokens */
4426 HTTP_FreeTokens(pTokenPair
);
4428 TRACE("No ':' in line: %s\n", debugstr_w(buffer
));
4432 pTokenPair
[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon
- buffer
+ 1) * sizeof(WCHAR
));
4435 HTTP_FreeTokens(pTokenPair
);
4438 memcpy(pTokenPair
[0], buffer
, (pszColon
- buffer
) * sizeof(WCHAR
));
4439 pTokenPair
[0][pszColon
- buffer
] = '\0';
4443 len
= strlenW(pszColon
);
4444 pTokenPair
[1] = HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
));
4447 HTTP_FreeTokens(pTokenPair
);
4450 memcpy(pTokenPair
[1], pszColon
, (len
+ 1) * sizeof(WCHAR
));
4452 strip_spaces(pTokenPair
[0]);
4453 strip_spaces(pTokenPair
[1]);
4455 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair
[0]), debugstr_w(pTokenPair
[1]));
4459 /***********************************************************************
4460 * HTTP_ProcessHeader (internal)
4462 * Stuff header into header tables according to <dwModifier>
4466 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4468 static BOOL
HTTP_ProcessHeader(http_request_t
*lpwhr
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
)
4470 LPHTTPHEADERW lphttpHdr
= NULL
;
4471 BOOL bSuccess
= FALSE
;
4473 BOOL request_only
= dwModifier
& HTTP_ADDHDR_FLAG_REQ
;
4475 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field
), debugstr_w(value
), dwModifier
);
4477 /* REPLACE wins out over ADD */
4478 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
4479 dwModifier
&= ~HTTP_ADDHDR_FLAG_ADD
;
4481 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD
)
4484 index
= HTTP_GetCustomHeaderIndex(lpwhr
, field
, 0, request_only
);
4488 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD_IF_NEW
)
4492 lphttpHdr
= &lpwhr
->pCustHeaders
[index
];
4498 hdr
.lpszField
= (LPWSTR
)field
;
4499 hdr
.lpszValue
= (LPWSTR
)value
;
4500 hdr
.wFlags
= hdr
.wCount
= 0;
4502 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
4503 hdr
.wFlags
|= HDR_ISREQUEST
;
4505 return HTTP_InsertCustomHeader(lpwhr
, &hdr
);
4507 /* no value to delete */
4510 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
4511 lphttpHdr
->wFlags
|= HDR_ISREQUEST
;
4513 lphttpHdr
->wFlags
&= ~HDR_ISREQUEST
;
4515 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
4517 HTTP_DeleteCustomHeader( lpwhr
, index
);
4523 hdr
.lpszField
= (LPWSTR
)field
;
4524 hdr
.lpszValue
= (LPWSTR
)value
;
4525 hdr
.wFlags
= hdr
.wCount
= 0;
4527 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
4528 hdr
.wFlags
|= HDR_ISREQUEST
;
4530 return HTTP_InsertCustomHeader(lpwhr
, &hdr
);
4535 else if (dwModifier
& COALESCEFLAGS
)
4540 INT origlen
= strlenW(lphttpHdr
->lpszValue
);
4541 INT valuelen
= strlenW(value
);
4543 if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
)
4546 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
4548 else if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON
)
4551 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
4554 len
= origlen
+ valuelen
+ ((ch
> 0) ? 2 : 0);
4556 lpsztmp
= HeapReAlloc(GetProcessHeap(), 0, lphttpHdr
->lpszValue
, (len
+1)*sizeof(WCHAR
));
4559 lphttpHdr
->lpszValue
= lpsztmp
;
4560 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4563 lphttpHdr
->lpszValue
[origlen
] = ch
;
4565 lphttpHdr
->lpszValue
[origlen
] = ' ';
4569 memcpy(&lphttpHdr
->lpszValue
[origlen
], value
, valuelen
*sizeof(WCHAR
));
4570 lphttpHdr
->lpszValue
[len
] = '\0';
4575 WARN("HeapReAlloc (%d bytes) failed\n",len
+1);
4576 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
4579 TRACE("<-- %d\n",bSuccess
);
4584 /***********************************************************************
4585 * HTTP_FinishedReading (internal)
4587 * Called when all content from server has been read by client.
4590 static BOOL
HTTP_FinishedReading(http_request_t
*lpwhr
)
4592 WCHAR szVersion
[10];
4593 WCHAR szConnectionResponse
[20];
4594 DWORD dwBufferSize
= sizeof(szVersion
);
4595 BOOL keepalive
= FALSE
;
4599 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
4600 * the connection is keep-alive by default */
4601 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_VERSION
, szVersion
,
4602 &dwBufferSize
, NULL
) &&
4603 !strcmpiW(szVersion
, g_szHttp1_1
))
4608 dwBufferSize
= sizeof(szConnectionResponse
);
4609 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_PROXY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) ||
4610 HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
))
4612 keepalive
= !strcmpiW(szConnectionResponse
, szKeepAlive
);
4617 HTTPREQ_CloseConnection(&lpwhr
->hdr
);
4620 /* FIXME: store data in the URL cache here */
4626 /***********************************************************************
4627 * HTTP_GetCustomHeaderIndex (internal)
4629 * Return index of custom header from header array
4632 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*lpwhr
, LPCWSTR lpszField
,
4633 int requested_index
, BOOL request_only
)
4637 TRACE("%s\n", debugstr_w(lpszField
));
4639 for (index
= 0; index
< lpwhr
->nCustHeaders
; index
++)
4641 if (strcmpiW(lpwhr
->pCustHeaders
[index
].lpszField
, lpszField
))
4644 if (request_only
&& !(lpwhr
->pCustHeaders
[index
].wFlags
& HDR_ISREQUEST
))
4647 if (!request_only
&& (lpwhr
->pCustHeaders
[index
].wFlags
& HDR_ISREQUEST
))
4650 if (requested_index
== 0)
4655 if (index
>= lpwhr
->nCustHeaders
)
4658 TRACE("Return: %d\n", index
);
4663 /***********************************************************************
4664 * HTTP_InsertCustomHeader (internal)
4666 * Insert header into array
4669 static BOOL
HTTP_InsertCustomHeader(http_request_t
*lpwhr
, LPHTTPHEADERW lpHdr
)
4672 LPHTTPHEADERW lph
= NULL
;
4675 TRACE("--> %s: %s\n", debugstr_w(lpHdr
->lpszField
), debugstr_w(lpHdr
->lpszValue
));
4676 count
= lpwhr
->nCustHeaders
+ 1;
4678 lph
= HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, lpwhr
->pCustHeaders
, sizeof(HTTPHEADERW
) * count
);
4680 lph
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(HTTPHEADERW
) * count
);
4684 lpwhr
->pCustHeaders
= lph
;
4685 lpwhr
->pCustHeaders
[count
-1].lpszField
= heap_strdupW(lpHdr
->lpszField
);
4686 lpwhr
->pCustHeaders
[count
-1].lpszValue
= heap_strdupW(lpHdr
->lpszValue
);
4687 lpwhr
->pCustHeaders
[count
-1].wFlags
= lpHdr
->wFlags
;
4688 lpwhr
->pCustHeaders
[count
-1].wCount
= lpHdr
->wCount
;
4689 lpwhr
->nCustHeaders
++;
4694 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
4701 /***********************************************************************
4702 * HTTP_DeleteCustomHeader (internal)
4704 * Delete header from array
4705 * If this function is called, the indexs may change.
4707 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*lpwhr
, DWORD index
)
4709 if( lpwhr
->nCustHeaders
<= 0 )
4711 if( index
>= lpwhr
->nCustHeaders
)
4713 lpwhr
->nCustHeaders
--;
4715 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[index
].lpszField
);
4716 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[index
].lpszValue
);
4718 memmove( &lpwhr
->pCustHeaders
[index
], &lpwhr
->pCustHeaders
[index
+1],
4719 (lpwhr
->nCustHeaders
- index
)* sizeof(HTTPHEADERW
) );
4720 memset( &lpwhr
->pCustHeaders
[lpwhr
->nCustHeaders
], 0, sizeof(HTTPHEADERW
) );
4726 /***********************************************************************
4727 * HTTP_VerifyValidHeader (internal)
4729 * Verify the given header is not invalid for the given http request
4732 static BOOL
HTTP_VerifyValidHeader(http_request_t
*lpwhr
, LPCWSTR field
)
4734 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4735 if (!strcmpW(lpwhr
->lpszVersion
, g_szHttp1_0
) && !strcmpiW(field
, szAccept_Encoding
))
4741 /***********************************************************************
4742 * IsHostInProxyBypassList (@)
4747 BOOL WINAPI
IsHostInProxyBypassList(DWORD flags
, LPCSTR szHost
, DWORD length
)
4749 FIXME("STUB: flags=%d host=%s length=%d\n",flags
,szHost
,length
);