2 * Wininet - Http Implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2002 CodeWeavers Inc.
6 * Copyright 2002 TransGaming Technologies Inc.
7 * Copyright 2004 Mike McCormack for CodeWeavers
8 * Copyright 2005 Aric Stewart for CodeWeavers
9 * Copyright 2006 Robert Shearman for CodeWeavers
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "wine/port.h"
32 #if defined(__MINGW32__) || defined (_MSC_VER)
36 #include <sys/types.h>
37 #ifdef HAVE_SYS_SOCKET_H
38 # include <sys/socket.h>
40 #ifdef HAVE_ARPA_INET_H
41 # include <arpa/inet.h>
59 #define NO_SHLWAPI_STREAM
60 #define NO_SHLWAPI_REG
61 #define NO_SHLWAPI_STRFCNS
62 #define NO_SHLWAPI_GDI
68 #include "wine/debug.h"
69 #include "wine/exception.h"
70 #include "wine/unicode.h"
72 WINE_DEFAULT_DEBUG_CHANNEL(wininet
);
74 static const WCHAR g_szHttp1_0
[] = {'H','T','T','P','/','1','.','0',0};
75 static const WCHAR g_szHttp1_1
[] = {'H','T','T','P','/','1','.','1',0};
76 static const WCHAR hostW
[] = { 'H','o','s','t',0 };
77 static const WCHAR szAuthorization
[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
78 static const WCHAR szProxy_Authorization
[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
79 static const WCHAR szStatus
[] = { 'S','t','a','t','u','s',0 };
80 static const WCHAR szKeepAlive
[] = {'K','e','e','p','-','A','l','i','v','e',0};
81 static const WCHAR szGET
[] = { 'G','E','T', 0 };
82 static const WCHAR szHEAD
[] = { 'H','E','A','D', 0 };
83 static const WCHAR szCrLf
[] = {'\r','\n', 0};
85 static const WCHAR szAccept
[] = { 'A','c','c','e','p','t',0 };
86 static const WCHAR szAccept_Charset
[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
87 static const WCHAR szAccept_Encoding
[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
88 static const WCHAR szAccept_Language
[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
89 static const WCHAR szAccept_Ranges
[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
90 static const WCHAR szAge
[] = { 'A','g','e',0 };
91 static const WCHAR szAllow
[] = { 'A','l','l','o','w',0 };
92 static const WCHAR szCache_Control
[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
93 static const WCHAR szConnection
[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
94 static const WCHAR szContent_Base
[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
95 static const WCHAR szContent_Encoding
[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
96 static const WCHAR szContent_ID
[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
97 static const WCHAR szContent_Language
[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
98 static const WCHAR szContent_Length
[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
99 static const WCHAR szContent_Location
[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
100 static const WCHAR szContent_MD5
[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
101 static const WCHAR szContent_Range
[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
102 static const WCHAR szContent_Transfer_Encoding
[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
103 static const WCHAR szContent_Type
[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
104 static const WCHAR szCookie
[] = { 'C','o','o','k','i','e',0 };
105 static const WCHAR szDate
[] = { 'D','a','t','e',0 };
106 static const WCHAR szFrom
[] = { 'F','r','o','m',0 };
107 static const WCHAR szETag
[] = { 'E','T','a','g',0 };
108 static const WCHAR szExpect
[] = { 'E','x','p','e','c','t',0 };
109 static const WCHAR szExpires
[] = { 'E','x','p','i','r','e','s',0 };
110 static const WCHAR szIf_Match
[] = { 'I','f','-','M','a','t','c','h',0 };
111 static const WCHAR szIf_Modified_Since
[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
112 static const WCHAR szIf_None_Match
[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
113 static const WCHAR szIf_Range
[] = { 'I','f','-','R','a','n','g','e',0 };
114 static const WCHAR szIf_Unmodified_Since
[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
115 static const WCHAR szLast_Modified
[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
116 static const WCHAR szLocation
[] = { 'L','o','c','a','t','i','o','n',0 };
117 static const WCHAR szMax_Forwards
[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
118 static const WCHAR szMime_Version
[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
119 static const WCHAR szPragma
[] = { 'P','r','a','g','m','a',0 };
120 static const WCHAR szProxy_Authenticate
[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
121 static const WCHAR szProxy_Connection
[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
122 static const WCHAR szPublic
[] = { 'P','u','b','l','i','c',0 };
123 static const WCHAR szRange
[] = { 'R','a','n','g','e',0 };
124 static const WCHAR szReferer
[] = { 'R','e','f','e','r','e','r',0 };
125 static const WCHAR szRetry_After
[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
126 static const WCHAR szServer
[] = { 'S','e','r','v','e','r',0 };
127 static const WCHAR szSet_Cookie
[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
128 static const WCHAR szTransfer_Encoding
[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
129 static const WCHAR szUnless_Modified_Since
[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
130 static const WCHAR szUpgrade
[] = { 'U','p','g','r','a','d','e',0 };
131 static const WCHAR szURI
[] = { 'U','R','I',0 };
132 static const WCHAR szUser_Agent
[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
133 static const WCHAR szVary
[] = { 'V','a','r','y',0 };
134 static const WCHAR szVia
[] = { 'V','i','a',0 };
135 static const WCHAR szWarning
[] = { 'W','a','r','n','i','n','g',0 };
136 static const WCHAR szWWW_Authenticate
[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
138 #define MAXHOSTNAME 100
139 #define MAX_FIELD_VALUE_LEN 256
140 #define MAX_FIELD_LEN 256
142 #define HTTP_REFERER szReferer
143 #define HTTP_ACCEPT szAccept
144 #define HTTP_USERAGENT szUser_Agent
146 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
147 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
148 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
149 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
150 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
151 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
152 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
154 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
165 unsigned int auth_data_len
;
166 BOOL finished
; /* finished authenticating */
170 struct gzip_stream_t
{
180 static BOOL
HTTP_OpenConnection(http_request_t
*req
);
181 static BOOL
HTTP_GetResponseHeaders(http_request_t
*req
, BOOL clear
);
182 static BOOL
HTTP_ProcessHeader(http_request_t
*req
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
);
183 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
);
184 static BOOL
HTTP_InsertCustomHeader(http_request_t
*req
, LPHTTPHEADERW lpHdr
);
185 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*req
, LPCWSTR lpszField
, INT index
, BOOL Request
);
186 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*req
, DWORD index
);
187 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
);
188 static BOOL
HTTP_HttpQueryInfoW(http_request_t
*, DWORD
, LPVOID
, LPDWORD
, LPDWORD
);
189 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*req
, LPCWSTR lpszUrl
);
190 static BOOL
HTTP_HandleRedirect(http_request_t
*req
, LPCWSTR lpszUrl
);
191 static UINT
HTTP_DecodeBase64(LPCWSTR base64
, LPSTR bin
);
192 static BOOL
HTTP_VerifyValidHeader(http_request_t
*req
, LPCWSTR field
);
193 static void HTTP_DrainContent(http_request_t
*req
);
194 static BOOL
HTTP_FinishedReading(http_request_t
*req
);
196 static LPHTTPHEADERW
HTTP_GetHeader(http_request_t
*req
, LPCWSTR head
)
199 HeaderIndex
= HTTP_GetCustomHeaderIndex(req
, head
, 0, TRUE
);
200 if (HeaderIndex
== -1)
203 return &req
->pCustHeaders
[HeaderIndex
];
208 static voidpf
wininet_zalloc(voidpf opaque
, uInt items
, uInt size
)
210 return HeapAlloc(GetProcessHeap(), 0, items
*size
);
213 static void wininet_zfree(voidpf opaque
, voidpf address
)
215 HeapFree(GetProcessHeap(), 0, address
);
218 static void init_gzip_stream(http_request_t
*req
)
220 gzip_stream_t
*gzip_stream
;
223 gzip_stream
= HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t
));
224 gzip_stream
->zstream
.zalloc
= wininet_zalloc
;
225 gzip_stream
->zstream
.zfree
= wininet_zfree
;
226 gzip_stream
->zstream
.opaque
= NULL
;
227 gzip_stream
->zstream
.next_in
= NULL
;
228 gzip_stream
->zstream
.avail_in
= 0;
229 gzip_stream
->zstream
.next_out
= NULL
;
230 gzip_stream
->zstream
.avail_out
= 0;
231 gzip_stream
->buf_pos
= 0;
232 gzip_stream
->buf_size
= 0;
233 gzip_stream
->end_of_data
= FALSE
;
235 zres
= inflateInit2(&gzip_stream
->zstream
, 0x1f);
237 ERR("inflateInit failed: %d\n", zres
);
238 HeapFree(GetProcessHeap(), 0, gzip_stream
);
242 req
->gzip_stream
= gzip_stream
;
244 index
= HTTP_GetCustomHeaderIndex(req
, szContent_Length
, 0, FALSE
);
246 HTTP_DeleteCustomHeader(req
, index
);
251 static void init_gzip_stream(http_request_t
*req
)
253 ERR("gzip stream not supported, missing zlib.\n");
258 /* set the request content length based on the headers */
259 static DWORD
set_content_length( http_request_t
*lpwhr
)
261 static const WCHAR szChunked
[] = {'c','h','u','n','k','e','d',0};
265 size
= sizeof(lpwhr
->dwContentLength
);
266 if (!HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_CONTENT_LENGTH
,
267 &lpwhr
->dwContentLength
, &size
, NULL
))
268 lpwhr
->dwContentLength
= ~0u;
270 size
= sizeof(encoding
);
271 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_TRANSFER_ENCODING
, encoding
, &size
, NULL
) &&
272 !strcmpiW(encoding
, szChunked
))
274 lpwhr
->dwContentLength
= ~0u;
275 lpwhr
->read_chunked
= TRUE
;
278 if(lpwhr
->decoding
) {
281 static const WCHAR gzipW
[] = {'g','z','i','p',0};
283 encoding_idx
= HTTP_GetCustomHeaderIndex(lpwhr
, szContent_Encoding
, 0, FALSE
);
284 if(encoding_idx
!= -1 && !strcmpiW(lpwhr
->pCustHeaders
[encoding_idx
].lpszValue
, gzipW
))
285 init_gzip_stream(lpwhr
);
288 return lpwhr
->dwContentLength
;
291 /***********************************************************************
292 * HTTP_Tokenize (internal)
294 * Tokenize a string, allocating memory for the tokens.
296 static LPWSTR
* HTTP_Tokenize(LPCWSTR string
, LPCWSTR token_string
)
298 LPWSTR
* token_array
;
305 /* empty string has no tokens */
309 for (i
= 0; string
[i
]; i
++)
311 if (!strncmpW(string
+i
, token_string
, strlenW(token_string
)))
315 /* we want to skip over separators, but not the null terminator */
316 for (j
= 0; j
< strlenW(token_string
) - 1; j
++)
324 /* add 1 for terminating NULL */
325 token_array
= HeapAlloc(GetProcessHeap(), 0, (tokens
+1) * sizeof(*token_array
));
326 token_array
[tokens
] = NULL
;
329 for (i
= 0; i
< tokens
; i
++)
332 next_token
= strstrW(string
, token_string
);
333 if (!next_token
) next_token
= string
+strlenW(string
);
334 len
= next_token
- string
;
335 token_array
[i
] = HeapAlloc(GetProcessHeap(), 0, (len
+1)*sizeof(WCHAR
));
336 memcpy(token_array
[i
], string
, len
*sizeof(WCHAR
));
337 token_array
[i
][len
] = '\0';
338 string
= next_token
+strlenW(token_string
);
343 /***********************************************************************
344 * HTTP_FreeTokens (internal)
346 * Frees memory returned from HTTP_Tokenize.
348 static void HTTP_FreeTokens(LPWSTR
* token_array
)
351 for (i
= 0; token_array
[i
]; i
++)
352 HeapFree(GetProcessHeap(), 0, token_array
[i
]);
353 HeapFree(GetProcessHeap(), 0, token_array
);
356 /* **********************************************************************
358 * Helper functions for the HttpSendRequest(Ex) functions
361 static void AsyncHttpSendRequestProc(WORKREQUEST
*workRequest
)
363 struct WORKREQ_HTTPSENDREQUESTW
const *req
= &workRequest
->u
.HttpSendRequestW
;
364 http_request_t
*lpwhr
= (http_request_t
*) workRequest
->hdr
;
366 TRACE("%p\n", lpwhr
);
368 HTTP_HttpSendRequestW(lpwhr
, req
->lpszHeader
,
369 req
->dwHeaderLength
, req
->lpOptional
, req
->dwOptionalLength
,
370 req
->dwContentLength
, req
->bEndRequest
);
372 HeapFree(GetProcessHeap(), 0, req
->lpszHeader
);
375 static void HTTP_FixURL(http_request_t
*lpwhr
)
377 static const WCHAR szSlash
[] = { '/',0 };
378 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/', 0 };
380 /* If we don't have a path we set it to root */
381 if (NULL
== lpwhr
->lpszPath
)
382 lpwhr
->lpszPath
= heap_strdupW(szSlash
);
383 else /* remove \r and \n*/
385 int nLen
= strlenW(lpwhr
->lpszPath
);
386 while ((nLen
>0 ) && ((lpwhr
->lpszPath
[nLen
-1] == '\r')||(lpwhr
->lpszPath
[nLen
-1] == '\n')))
389 lpwhr
->lpszPath
[nLen
]='\0';
391 /* Replace '\' with '/' */
394 if (lpwhr
->lpszPath
[nLen
] == '\\') lpwhr
->lpszPath
[nLen
]='/';
398 if(CSTR_EQUAL
!= CompareStringW( LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
399 lpwhr
->lpszPath
, strlenW(lpwhr
->lpszPath
), szHttp
, strlenW(szHttp
) )
400 && lpwhr
->lpszPath
[0] != '/') /* not an absolute path ?? --> fix it !! */
402 WCHAR
*fixurl
= HeapAlloc(GetProcessHeap(), 0,
403 (strlenW(lpwhr
->lpszPath
) + 2)*sizeof(WCHAR
));
405 strcpyW(fixurl
+ 1, lpwhr
->lpszPath
);
406 HeapFree( GetProcessHeap(), 0, lpwhr
->lpszPath
);
407 lpwhr
->lpszPath
= fixurl
;
411 static LPWSTR
HTTP_BuildHeaderRequestString( http_request_t
*lpwhr
, LPCWSTR verb
, LPCWSTR path
, LPCWSTR version
)
413 LPWSTR requestString
;
419 static const WCHAR szSpace
[] = { ' ',0 };
420 static const WCHAR szColon
[] = { ':',' ',0 };
421 static const WCHAR sztwocrlf
[] = {'\r','\n','\r','\n', 0};
423 /* allocate space for an array of all the string pointers to be added */
424 len
= (lpwhr
->nCustHeaders
)*4 + 10;
425 req
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(LPCWSTR
) );
427 /* add the verb, path and HTTP version string */
435 /* Append custom request headers */
436 for (i
= 0; i
< lpwhr
->nCustHeaders
; i
++)
438 if (lpwhr
->pCustHeaders
[i
].wFlags
& HDR_ISREQUEST
)
441 req
[n
++] = lpwhr
->pCustHeaders
[i
].lpszField
;
443 req
[n
++] = lpwhr
->pCustHeaders
[i
].lpszValue
;
445 TRACE("Adding custom header %s (%s)\n",
446 debugstr_w(lpwhr
->pCustHeaders
[i
].lpszField
),
447 debugstr_w(lpwhr
->pCustHeaders
[i
].lpszValue
));
452 ERR("oops. buffer overrun\n");
455 requestString
= HTTP_build_req( req
, 4 );
456 HeapFree( GetProcessHeap(), 0, req
);
459 * Set (header) termination string for request
460 * Make sure there's exactly two new lines at the end of the request
462 p
= &requestString
[strlenW(requestString
)-1];
463 while ( (*p
== '\n') || (*p
== '\r') )
465 strcpyW( p
+1, sztwocrlf
);
467 return requestString
;
470 static void HTTP_ProcessCookies( http_request_t
*lpwhr
)
474 LPHTTPHEADERW setCookieHeader
;
476 while((HeaderIndex
= HTTP_GetCustomHeaderIndex(lpwhr
, szSet_Cookie
, numCookies
, FALSE
)) != -1)
478 setCookieHeader
= &lpwhr
->pCustHeaders
[HeaderIndex
];
480 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
) && setCookieHeader
->lpszValue
)
483 static const WCHAR szFmt
[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
487 Host
= HTTP_GetHeader(lpwhr
, hostW
);
488 len
= lstrlenW(Host
->lpszValue
) + 9 + lstrlenW(lpwhr
->lpszPath
);
489 buf_url
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
490 sprintfW(buf_url
, szFmt
, Host
->lpszValue
, lpwhr
->lpszPath
);
491 InternetSetCookieW(buf_url
, NULL
, setCookieHeader
->lpszValue
);
493 HeapFree(GetProcessHeap(), 0, buf_url
);
499 static inline BOOL
is_basic_auth_value( LPCWSTR pszAuthValue
)
501 static const WCHAR szBasic
[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
502 return !strncmpiW(pszAuthValue
, szBasic
, ARRAYSIZE(szBasic
)) &&
503 ((pszAuthValue
[ARRAYSIZE(szBasic
)] == ' ') || !pszAuthValue
[ARRAYSIZE(szBasic
)]);
506 static BOOL
HTTP_DoAuthorization( http_request_t
*lpwhr
, LPCWSTR pszAuthValue
,
507 struct HttpAuthInfo
**ppAuthInfo
,
508 LPWSTR domain_and_username
, LPWSTR password
)
510 SECURITY_STATUS sec_status
;
511 struct HttpAuthInfo
*pAuthInfo
= *ppAuthInfo
;
514 TRACE("%s\n", debugstr_w(pszAuthValue
));
521 pAuthInfo
= HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo
));
525 SecInvalidateHandle(&pAuthInfo
->cred
);
526 SecInvalidateHandle(&pAuthInfo
->ctx
);
527 memset(&pAuthInfo
->exp
, 0, sizeof(pAuthInfo
->exp
));
529 pAuthInfo
->auth_data
= NULL
;
530 pAuthInfo
->auth_data_len
= 0;
531 pAuthInfo
->finished
= FALSE
;
533 if (is_basic_auth_value(pszAuthValue
))
535 static const WCHAR szBasic
[] = {'B','a','s','i','c',0};
536 pAuthInfo
->scheme
= heap_strdupW(szBasic
);
537 if (!pAuthInfo
->scheme
)
539 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
546 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity
;
548 pAuthInfo
->scheme
= heap_strdupW(pszAuthValue
);
549 if (!pAuthInfo
->scheme
)
551 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
555 if (domain_and_username
)
557 WCHAR
*user
= strchrW(domain_and_username
, '\\');
558 WCHAR
*domain
= domain_and_username
;
560 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
562 pAuthData
= &nt_auth_identity
;
567 user
= domain_and_username
;
571 nt_auth_identity
.Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
572 nt_auth_identity
.User
= user
;
573 nt_auth_identity
.UserLength
= strlenW(nt_auth_identity
.User
);
574 nt_auth_identity
.Domain
= domain
;
575 nt_auth_identity
.DomainLength
= domain
? user
- domain
- 1 : 0;
576 nt_auth_identity
.Password
= password
;
577 nt_auth_identity
.PasswordLength
= strlenW(nt_auth_identity
.Password
);
580 /* use default credentials */
583 sec_status
= AcquireCredentialsHandleW(NULL
, pAuthInfo
->scheme
,
584 SECPKG_CRED_OUTBOUND
, NULL
,
586 NULL
, &pAuthInfo
->cred
,
588 if (sec_status
== SEC_E_OK
)
590 PSecPkgInfoW sec_pkg_info
;
591 sec_status
= QuerySecurityPackageInfoW(pAuthInfo
->scheme
, &sec_pkg_info
);
592 if (sec_status
== SEC_E_OK
)
594 pAuthInfo
->max_token
= sec_pkg_info
->cbMaxToken
;
595 FreeContextBuffer(sec_pkg_info
);
598 if (sec_status
!= SEC_E_OK
)
600 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
601 debugstr_w(pAuthInfo
->scheme
), sec_status
);
602 HeapFree(GetProcessHeap(), 0, pAuthInfo
->scheme
);
603 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
607 *ppAuthInfo
= pAuthInfo
;
609 else if (pAuthInfo
->finished
)
612 if ((strlenW(pszAuthValue
) < strlenW(pAuthInfo
->scheme
)) ||
613 strncmpiW(pszAuthValue
, pAuthInfo
->scheme
, strlenW(pAuthInfo
->scheme
)))
615 ERR("authentication scheme changed from %s to %s\n",
616 debugstr_w(pAuthInfo
->scheme
), debugstr_w(pszAuthValue
));
620 if (is_basic_auth_value(pszAuthValue
))
626 TRACE("basic authentication\n");
628 /* we don't cache credentials for basic authentication, so we can't
629 * retrieve them if the application didn't pass us any credentials */
630 if (!domain_and_username
) return FALSE
;
632 userlen
= WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, lstrlenW(domain_and_username
), NULL
, 0, NULL
, NULL
);
633 passlen
= WideCharToMultiByte(CP_UTF8
, 0, password
, lstrlenW(password
), NULL
, 0, NULL
, NULL
);
635 /* length includes a nul terminator, which will be re-used for the ':' */
636 auth_data
= HeapAlloc(GetProcessHeap(), 0, userlen
+ 1 + passlen
);
640 WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, -1, auth_data
, userlen
, NULL
, NULL
);
641 auth_data
[userlen
] = ':';
642 WideCharToMultiByte(CP_UTF8
, 0, password
, -1, &auth_data
[userlen
+1], passlen
, NULL
, NULL
);
644 pAuthInfo
->auth_data
= auth_data
;
645 pAuthInfo
->auth_data_len
= userlen
+ 1 + passlen
;
646 pAuthInfo
->finished
= TRUE
;
653 SecBufferDesc out_desc
, in_desc
;
655 unsigned char *buffer
;
656 ULONG context_req
= ISC_REQ_CONNECTION
| ISC_REQ_USE_DCE_STYLE
|
657 ISC_REQ_MUTUAL_AUTH
| ISC_REQ_DELEGATE
;
659 in
.BufferType
= SECBUFFER_TOKEN
;
663 in_desc
.ulVersion
= 0;
664 in_desc
.cBuffers
= 1;
665 in_desc
.pBuffers
= &in
;
667 pszAuthData
= pszAuthValue
+ strlenW(pAuthInfo
->scheme
);
668 if (*pszAuthData
== ' ')
671 in
.cbBuffer
= HTTP_DecodeBase64(pszAuthData
, NULL
);
672 in
.pvBuffer
= HeapAlloc(GetProcessHeap(), 0, in
.cbBuffer
);
673 HTTP_DecodeBase64(pszAuthData
, in
.pvBuffer
);
676 buffer
= HeapAlloc(GetProcessHeap(), 0, pAuthInfo
->max_token
);
678 out
.BufferType
= SECBUFFER_TOKEN
;
679 out
.cbBuffer
= pAuthInfo
->max_token
;
680 out
.pvBuffer
= buffer
;
682 out_desc
.ulVersion
= 0;
683 out_desc
.cBuffers
= 1;
684 out_desc
.pBuffers
= &out
;
686 sec_status
= InitializeSecurityContextW(first
? &pAuthInfo
->cred
: NULL
,
687 first
? NULL
: &pAuthInfo
->ctx
,
688 first
? lpwhr
->lpHttpSession
->lpszServerName
: NULL
,
689 context_req
, 0, SECURITY_NETWORK_DREP
,
690 in
.pvBuffer
? &in_desc
: NULL
,
691 0, &pAuthInfo
->ctx
, &out_desc
,
692 &pAuthInfo
->attr
, &pAuthInfo
->exp
);
693 if (sec_status
== SEC_E_OK
)
695 pAuthInfo
->finished
= TRUE
;
696 pAuthInfo
->auth_data
= out
.pvBuffer
;
697 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
698 TRACE("sending last auth packet\n");
700 else if (sec_status
== SEC_I_CONTINUE_NEEDED
)
702 pAuthInfo
->auth_data
= out
.pvBuffer
;
703 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
704 TRACE("sending next auth packet\n");
708 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status
);
709 pAuthInfo
->finished
= TRUE
;
710 HeapFree(GetProcessHeap(), 0, out
.pvBuffer
);
718 /***********************************************************************
719 * HTTP_HttpAddRequestHeadersW (internal)
721 static BOOL
HTTP_HttpAddRequestHeadersW(http_request_t
*lpwhr
,
722 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
727 BOOL bSuccess
= FALSE
;
730 TRACE("copying header: %s\n", debugstr_wn(lpszHeader
, dwHeaderLength
));
732 if( dwHeaderLength
== ~0U )
733 len
= strlenW(lpszHeader
);
735 len
= dwHeaderLength
;
736 buffer
= HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR
)*(len
+1) );
737 lstrcpynW( buffer
, lpszHeader
, len
+ 1);
743 LPWSTR
* pFieldAndValue
;
747 while (*lpszEnd
!= '\0')
749 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
754 if (*lpszStart
== '\0')
757 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
760 lpszEnd
++; /* Jump over newline */
762 TRACE("interpreting header %s\n", debugstr_w(lpszStart
));
763 if (*lpszStart
== '\0')
765 /* Skip 0-length headers */
770 pFieldAndValue
= HTTP_InterpretHttpHeader(lpszStart
);
773 bSuccess
= HTTP_VerifyValidHeader(lpwhr
, pFieldAndValue
[0]);
775 bSuccess
= HTTP_ProcessHeader(lpwhr
, pFieldAndValue
[0],
776 pFieldAndValue
[1], dwModifier
| HTTP_ADDHDR_FLAG_REQ
);
777 HTTP_FreeTokens(pFieldAndValue
);
783 HeapFree(GetProcessHeap(), 0, buffer
);
788 /***********************************************************************
789 * HttpAddRequestHeadersW (WININET.@)
791 * Adds one or more HTTP header to the request handler
794 * On Windows if dwHeaderLength includes the trailing '\0', then
795 * HttpAddRequestHeadersW() adds it too. However this results in an
796 * invalid Http header which is rejected by some servers so we probably
797 * don't need to match Windows on that point.
804 BOOL WINAPI
HttpAddRequestHeadersW(HINTERNET hHttpRequest
,
805 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
807 BOOL bSuccess
= FALSE
;
808 http_request_t
*lpwhr
;
810 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_wn(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
815 lpwhr
= (http_request_t
*) WININET_GetObject( hHttpRequest
);
816 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
818 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
821 bSuccess
= HTTP_HttpAddRequestHeadersW( lpwhr
, lpszHeader
, dwHeaderLength
, dwModifier
);
824 WININET_Release( &lpwhr
->hdr
);
829 /***********************************************************************
830 * HttpAddRequestHeadersA (WININET.@)
832 * Adds one or more HTTP header to the request handler
839 BOOL WINAPI
HttpAddRequestHeadersA(HINTERNET hHttpRequest
,
840 LPCSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
846 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_an(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
848 len
= MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, NULL
, 0 );
849 hdr
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
850 MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, hdr
, len
);
851 if( dwHeaderLength
!= ~0U )
852 dwHeaderLength
= len
;
854 r
= HttpAddRequestHeadersW( hHttpRequest
, hdr
, dwHeaderLength
, dwModifier
);
856 HeapFree( GetProcessHeap(), 0, hdr
);
861 /***********************************************************************
862 * HttpEndRequestA (WININET.@)
864 * Ends an HTTP request that was started by HttpSendRequestEx
871 BOOL WINAPI
HttpEndRequestA(HINTERNET hRequest
,
872 LPINTERNET_BUFFERSA lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
874 TRACE("(%p, %p, %08x, %08lx)\n", hRequest
, lpBuffersOut
, dwFlags
, dwContext
);
878 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
882 return HttpEndRequestW(hRequest
, NULL
, dwFlags
, dwContext
);
885 static BOOL
HTTP_HttpEndRequestW(http_request_t
*lpwhr
, DWORD dwFlags
, DWORD_PTR dwContext
)
890 INTERNET_ASYNC_RESULT iar
;
892 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
893 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
895 responseLen
= HTTP_GetResponseHeaders(lpwhr
, TRUE
);
899 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
900 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
, sizeof(DWORD
));
902 /* process cookies here. Is this right? */
903 HTTP_ProcessCookies(lpwhr
);
905 if (!set_content_length( lpwhr
)) HTTP_FinishedReading(lpwhr
);
907 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
))
909 DWORD dwCode
,dwCodeLength
= sizeof(DWORD
);
910 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_STATUS_CODE
, &dwCode
, &dwCodeLength
, NULL
) &&
911 (dwCode
== 302 || dwCode
== 301 || dwCode
== 303))
913 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
914 dwBufferSize
=sizeof(szNewLocation
);
915 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_LOCATION
, szNewLocation
, &dwBufferSize
, NULL
))
917 if (strcmpW(lpwhr
->lpszVerb
, szGET
) && strcmpW(lpwhr
->lpszVerb
, szHEAD
))
919 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVerb
);
920 lpwhr
->lpszVerb
= heap_strdupW(szGET
);
922 HTTP_DrainContent(lpwhr
);
923 if ((new_url
= HTTP_GetRedirectURL( lpwhr
, szNewLocation
)))
925 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
926 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
927 rc
= HTTP_HandleRedirect(lpwhr
, new_url
);
929 rc
= HTTP_HttpSendRequestW(lpwhr
, NULL
, 0, NULL
, 0, 0, TRUE
);
930 HeapFree( GetProcessHeap(), 0, new_url
);
936 iar
.dwResult
= (DWORD_PTR
)lpwhr
->hdr
.hInternet
;
937 iar
.dwError
= rc
? 0 : INTERNET_GetLastError();
939 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
940 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
941 sizeof(INTERNET_ASYNC_RESULT
));
945 static void AsyncHttpEndRequestProc(WORKREQUEST
*work
)
947 struct WORKREQ_HTTPENDREQUESTW
const *req
= &work
->u
.HttpEndRequestW
;
948 http_request_t
*lpwhr
= (http_request_t
*)work
->hdr
;
950 TRACE("%p\n", lpwhr
);
952 HTTP_HttpEndRequestW(lpwhr
, req
->dwFlags
, req
->dwContext
);
955 /***********************************************************************
956 * HttpEndRequestW (WININET.@)
958 * Ends an HTTP request that was started by HttpSendRequestEx
965 BOOL WINAPI
HttpEndRequestW(HINTERNET hRequest
,
966 LPINTERNET_BUFFERSW lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
969 http_request_t
*lpwhr
;
975 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
979 lpwhr
= (http_request_t
*) WININET_GetObject( hRequest
);
981 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
983 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
985 WININET_Release( &lpwhr
->hdr
);
988 lpwhr
->hdr
.dwFlags
|= dwFlags
;
990 if (lpwhr
->lpHttpSession
->lpAppInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
993 struct WORKREQ_HTTPENDREQUESTW
*request
;
995 work
.asyncproc
= AsyncHttpEndRequestProc
;
996 work
.hdr
= WININET_AddRef( &lpwhr
->hdr
);
998 request
= &work
.u
.HttpEndRequestW
;
999 request
->dwFlags
= dwFlags
;
1000 request
->dwContext
= dwContext
;
1002 INTERNET_AsyncCall(&work
);
1003 INTERNET_SetLastError(ERROR_IO_PENDING
);
1006 rc
= HTTP_HttpEndRequestW(lpwhr
, dwFlags
, dwContext
);
1008 WININET_Release( &lpwhr
->hdr
);
1009 TRACE("%i <--\n",rc
);
1013 /***********************************************************************
1014 * HttpOpenRequestW (WININET.@)
1016 * Open a HTTP request handle
1019 * HINTERNET a HTTP request handle on success
1023 HINTERNET WINAPI
HttpOpenRequestW(HINTERNET hHttpSession
,
1024 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
1025 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
1026 DWORD dwFlags
, DWORD_PTR dwContext
)
1028 http_session_t
*lpwhs
;
1029 HINTERNET handle
= NULL
;
1031 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
1032 debugstr_w(lpszVerb
), debugstr_w(lpszObjectName
),
1033 debugstr_w(lpszVersion
), debugstr_w(lpszReferrer
), lpszAcceptTypes
,
1034 dwFlags
, dwContext
);
1035 if(lpszAcceptTypes
!=NULL
)
1038 for(i
=0;lpszAcceptTypes
[i
]!=NULL
;i
++)
1039 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes
[i
]));
1042 lpwhs
= (http_session_t
*) WININET_GetObject( hHttpSession
);
1043 if (NULL
== lpwhs
|| lpwhs
->hdr
.htype
!= WH_HHTTPSESSION
)
1045 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
1050 * My tests seem to show that the windows version does not
1051 * become asynchronous until after this point. And anyhow
1052 * if this call was asynchronous then how would you get the
1053 * necessary HINTERNET pointer returned by this function.
1056 handle
= HTTP_HttpOpenRequestW(lpwhs
, lpszVerb
, lpszObjectName
,
1057 lpszVersion
, lpszReferrer
, lpszAcceptTypes
,
1058 dwFlags
, dwContext
);
1061 WININET_Release( &lpwhs
->hdr
);
1062 TRACE("returning %p\n", handle
);
1067 /***********************************************************************
1068 * HttpOpenRequestA (WININET.@)
1070 * Open a HTTP request handle
1073 * HINTERNET a HTTP request handle on success
1077 HINTERNET WINAPI
HttpOpenRequestA(HINTERNET hHttpSession
,
1078 LPCSTR lpszVerb
, LPCSTR lpszObjectName
, LPCSTR lpszVersion
,
1079 LPCSTR lpszReferrer
, LPCSTR
*lpszAcceptTypes
,
1080 DWORD dwFlags
, DWORD_PTR dwContext
)
1082 LPWSTR szVerb
= NULL
, szObjectName
= NULL
;
1083 LPWSTR szVersion
= NULL
, szReferrer
= NULL
, *szAcceptTypes
= NULL
;
1084 INT acceptTypesCount
;
1085 HINTERNET rc
= FALSE
;
1088 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
1089 debugstr_a(lpszVerb
), debugstr_a(lpszObjectName
),
1090 debugstr_a(lpszVersion
), debugstr_a(lpszReferrer
), lpszAcceptTypes
,
1091 dwFlags
, dwContext
);
1095 szVerb
= heap_strdupAtoW(lpszVerb
);
1102 szObjectName
= heap_strdupAtoW(lpszObjectName
);
1103 if ( !szObjectName
)
1109 szVersion
= heap_strdupAtoW(lpszVersion
);
1116 szReferrer
= heap_strdupAtoW(lpszReferrer
);
1121 if (lpszAcceptTypes
)
1123 acceptTypesCount
= 0;
1124 types
= lpszAcceptTypes
;
1129 /* find out how many there are */
1130 if (*types
&& **types
)
1132 TRACE("accept type: %s\n", debugstr_a(*types
));
1138 WARN("invalid accept type pointer\n");
1143 szAcceptTypes
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
*) * (acceptTypesCount
+1));
1144 if (!szAcceptTypes
) goto end
;
1146 acceptTypesCount
= 0;
1147 types
= lpszAcceptTypes
;
1152 if (*types
&& **types
)
1153 szAcceptTypes
[acceptTypesCount
++] = heap_strdupAtoW(*types
);
1157 /* ignore invalid pointer */
1162 szAcceptTypes
[acceptTypesCount
] = NULL
;
1165 rc
= HttpOpenRequestW(hHttpSession
, szVerb
, szObjectName
,
1166 szVersion
, szReferrer
,
1167 (LPCWSTR
*)szAcceptTypes
, dwFlags
, dwContext
);
1172 acceptTypesCount
= 0;
1173 while (szAcceptTypes
[acceptTypesCount
])
1175 HeapFree(GetProcessHeap(), 0, szAcceptTypes
[acceptTypesCount
]);
1178 HeapFree(GetProcessHeap(), 0, szAcceptTypes
);
1180 HeapFree(GetProcessHeap(), 0, szReferrer
);
1181 HeapFree(GetProcessHeap(), 0, szVersion
);
1182 HeapFree(GetProcessHeap(), 0, szObjectName
);
1183 HeapFree(GetProcessHeap(), 0, szVerb
);
1188 /***********************************************************************
1191 static UINT
HTTP_EncodeBase64( LPCSTR bin
, unsigned int len
, LPWSTR base64
)
1194 static const CHAR HTTP_Base64Enc
[] =
1195 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1199 /* first 6 bits, all from bin[0] */
1200 base64
[n
++] = HTTP_Base64Enc
[(bin
[0] & 0xfc) >> 2];
1201 x
= (bin
[0] & 3) << 4;
1203 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1206 base64
[n
++] = HTTP_Base64Enc
[x
];
1211 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[1]&0xf0) >> 4 ) ];
1212 x
= ( bin
[1] & 0x0f ) << 2;
1214 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1217 base64
[n
++] = HTTP_Base64Enc
[x
];
1221 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[2]&0xc0 ) >> 6 ) ];
1223 /* last 6 bits, all from bin [2] */
1224 base64
[n
++] = HTTP_Base64Enc
[ bin
[2] & 0x3f ];
1232 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1233 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1234 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1235 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1236 static const signed char HTTP_Base64Dec
[256] =
1238 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1239 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1240 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1241 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1242 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1243 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1244 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1245 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1246 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1247 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1248 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1249 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1250 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1251 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1252 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1253 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1254 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1255 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1256 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1257 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1258 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1259 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1260 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1261 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1262 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1263 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1267 /***********************************************************************
1270 static UINT
HTTP_DecodeBase64( LPCWSTR base64
, LPSTR bin
)
1278 if (base64
[0] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1279 ((in
[0] = HTTP_Base64Dec
[base64
[0]]) == -1) ||
1280 base64
[1] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1281 ((in
[1] = HTTP_Base64Dec
[base64
[1]]) == -1))
1283 WARN("invalid base64: %s\n", debugstr_w(base64
));
1287 bin
[n
] = (unsigned char) (in
[0] << 2 | in
[1] >> 4);
1290 if ((base64
[2] == '=') && (base64
[3] == '='))
1292 if (base64
[2] > ARRAYSIZE(HTTP_Base64Dec
) ||
1293 ((in
[2] = HTTP_Base64Dec
[base64
[2]]) == -1))
1295 WARN("invalid base64: %s\n", debugstr_w(&base64
[2]));
1299 bin
[n
] = (unsigned char) (in
[1] << 4 | in
[2] >> 2);
1302 if (base64
[3] == '=')
1304 if (base64
[3] > ARRAYSIZE(HTTP_Base64Dec
) ||
1305 ((in
[3] = HTTP_Base64Dec
[base64
[3]]) == -1))
1307 WARN("invalid base64: %s\n", debugstr_w(&base64
[3]));
1311 bin
[n
] = (unsigned char) (((in
[2] << 6) & 0xc0) | in
[3]);
1320 /***********************************************************************
1321 * HTTP_InsertAuthorization
1323 * Insert or delete the authorization field in the request header.
1325 static BOOL
HTTP_InsertAuthorization( http_request_t
*lpwhr
, struct HttpAuthInfo
*pAuthInfo
, LPCWSTR header
)
1329 static const WCHAR wszSpace
[] = {' ',0};
1330 static const WCHAR wszBasic
[] = {'B','a','s','i','c',0};
1332 WCHAR
*authorization
= NULL
;
1334 if (pAuthInfo
->auth_data_len
)
1336 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1337 len
= strlenW(pAuthInfo
->scheme
)+1+((pAuthInfo
->auth_data_len
+2)*4)/3;
1338 authorization
= HeapAlloc(GetProcessHeap(), 0, (len
+1)*sizeof(WCHAR
));
1342 strcpyW(authorization
, pAuthInfo
->scheme
);
1343 strcatW(authorization
, wszSpace
);
1344 HTTP_EncodeBase64(pAuthInfo
->auth_data
,
1345 pAuthInfo
->auth_data_len
,
1346 authorization
+strlenW(authorization
));
1348 /* clear the data as it isn't valid now that it has been sent to the
1349 * server, unless it's Basic authentication which doesn't do
1350 * connection tracking */
1351 if (strcmpiW(pAuthInfo
->scheme
, wszBasic
))
1353 HeapFree(GetProcessHeap(), 0, pAuthInfo
->auth_data
);
1354 pAuthInfo
->auth_data
= NULL
;
1355 pAuthInfo
->auth_data_len
= 0;
1359 TRACE("Inserting authorization: %s\n", debugstr_w(authorization
));
1361 HTTP_ProcessHeader(lpwhr
, header
, authorization
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
1363 HeapFree(GetProcessHeap(), 0, authorization
);
1368 static WCHAR
*HTTP_BuildProxyRequestUrl(http_request_t
*req
)
1370 WCHAR new_location
[INTERNET_MAX_URL_LENGTH
], *url
;
1373 size
= sizeof(new_location
);
1374 if (HTTP_HttpQueryInfoW(req
, HTTP_QUERY_LOCATION
, new_location
, &size
, NULL
))
1376 if (!(url
= HeapAlloc( GetProcessHeap(), 0, size
+ sizeof(WCHAR
) ))) return NULL
;
1377 strcpyW( url
, new_location
);
1381 static const WCHAR slash
[] = { '/',0 };
1382 static const WCHAR format
[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1383 static const WCHAR formatSSL
[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1384 http_session_t
*session
= req
->lpHttpSession
;
1386 size
= 16; /* "https://" + sizeof(port#) + ":/\0" */
1387 size
+= strlenW( session
->lpszHostName
) + strlenW( req
->lpszPath
);
1389 if (!(url
= HeapAlloc( GetProcessHeap(), 0, size
* sizeof(WCHAR
) ))) return NULL
;
1391 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1392 sprintfW( url
, formatSSL
, session
->lpszHostName
, session
->nHostPort
);
1394 sprintfW( url
, format
, session
->lpszHostName
, session
->nHostPort
);
1395 if (req
->lpszPath
[0] != '/') strcatW( url
, slash
);
1396 strcatW( url
, req
->lpszPath
);
1398 TRACE("url=%s\n", debugstr_w(url
));
1402 /***********************************************************************
1403 * HTTP_DealWithProxy
1405 static BOOL
HTTP_DealWithProxy(appinfo_t
*hIC
, http_session_t
*lpwhs
, http_request_t
*lpwhr
)
1407 WCHAR buf
[MAXHOSTNAME
];
1408 WCHAR proxy
[MAXHOSTNAME
+ 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1409 static WCHAR szNul
[] = { 0 };
1410 URL_COMPONENTSW UrlComponents
;
1411 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/',0 };
1412 static const WCHAR szFormat
[] = { 'h','t','t','p',':','/','/','%','s',0 };
1414 memset( &UrlComponents
, 0, sizeof UrlComponents
);
1415 UrlComponents
.dwStructSize
= sizeof UrlComponents
;
1416 UrlComponents
.lpszHostName
= buf
;
1417 UrlComponents
.dwHostNameLength
= MAXHOSTNAME
;
1419 if( CSTR_EQUAL
!= CompareStringW(LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
1420 hIC
->lpszProxy
,strlenW(szHttp
),szHttp
,strlenW(szHttp
)) )
1421 sprintfW(proxy
, szFormat
, hIC
->lpszProxy
);
1423 strcpyW(proxy
, hIC
->lpszProxy
);
1424 if( !InternetCrackUrlW(proxy
, 0, 0, &UrlComponents
) )
1426 if( UrlComponents
.dwHostNameLength
== 0 )
1429 if( !lpwhr
->lpszPath
)
1430 lpwhr
->lpszPath
= szNul
;
1432 if(UrlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
1433 UrlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
1435 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszServerName
);
1436 lpwhs
->lpszServerName
= heap_strdupW(UrlComponents
.lpszHostName
);
1437 lpwhs
->nServerPort
= UrlComponents
.nPort
;
1439 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs
->lpszServerName
), lpwhs
->nServerPort
);
1443 #ifndef INET6_ADDRSTRLEN
1444 #define INET6_ADDRSTRLEN 46
1447 static BOOL
HTTP_ResolveName(http_request_t
*lpwhr
)
1449 char szaddr
[INET6_ADDRSTRLEN
];
1450 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
1453 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1454 INTERNET_STATUS_RESOLVING_NAME
,
1455 lpwhs
->lpszServerName
,
1456 strlenW(lpwhs
->lpszServerName
)+1);
1458 lpwhs
->sa_len
= sizeof(lpwhs
->socketAddress
);
1459 if (!GetAddress(lpwhs
->lpszServerName
, lpwhs
->nServerPort
,
1460 (struct sockaddr
*)&lpwhs
->socketAddress
, &lpwhs
->sa_len
))
1462 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED
);
1466 switch (lpwhs
->socketAddress
.ss_family
)
1469 addr
= &((struct sockaddr_in
*)&lpwhs
->socketAddress
)->sin_addr
;
1472 addr
= &((struct sockaddr_in6
*)&lpwhs
->socketAddress
)->sin6_addr
;
1475 WARN("unsupported family %d\n", lpwhs
->socketAddress
.ss_family
);
1476 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED
);
1479 inet_ntop(lpwhs
->socketAddress
.ss_family
, addr
, szaddr
, sizeof(szaddr
));
1480 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1481 INTERNET_STATUS_NAME_RESOLVED
,
1482 szaddr
, strlen(szaddr
)+1);
1484 TRACE("resolved %s to %s\n", debugstr_w(lpwhs
->lpszServerName
), szaddr
);
1489 /***********************************************************************
1490 * HTTPREQ_Destroy (internal)
1492 * Deallocate request handle
1495 static void HTTPREQ_Destroy(object_header_t
*hdr
)
1497 http_request_t
*lpwhr
= (http_request_t
*) hdr
;
1502 if(lpwhr
->hCacheFile
)
1503 CloseHandle(lpwhr
->hCacheFile
);
1505 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszCacheFile
);
1507 DeleteCriticalSection( &lpwhr
->read_section
);
1508 WININET_Release(&lpwhr
->lpHttpSession
->hdr
);
1510 if (lpwhr
->pAuthInfo
)
1512 if (SecIsValidHandle(&lpwhr
->pAuthInfo
->ctx
))
1513 DeleteSecurityContext(&lpwhr
->pAuthInfo
->ctx
);
1514 if (SecIsValidHandle(&lpwhr
->pAuthInfo
->cred
))
1515 FreeCredentialsHandle(&lpwhr
->pAuthInfo
->cred
);
1517 HeapFree(GetProcessHeap(), 0, lpwhr
->pAuthInfo
->auth_data
);
1518 HeapFree(GetProcessHeap(), 0, lpwhr
->pAuthInfo
->scheme
);
1519 HeapFree(GetProcessHeap(), 0, lpwhr
->pAuthInfo
);
1520 lpwhr
->pAuthInfo
= NULL
;
1523 if (lpwhr
->pProxyAuthInfo
)
1525 if (SecIsValidHandle(&lpwhr
->pProxyAuthInfo
->ctx
))
1526 DeleteSecurityContext(&lpwhr
->pProxyAuthInfo
->ctx
);
1527 if (SecIsValidHandle(&lpwhr
->pProxyAuthInfo
->cred
))
1528 FreeCredentialsHandle(&lpwhr
->pProxyAuthInfo
->cred
);
1530 HeapFree(GetProcessHeap(), 0, lpwhr
->pProxyAuthInfo
->auth_data
);
1531 HeapFree(GetProcessHeap(), 0, lpwhr
->pProxyAuthInfo
->scheme
);
1532 HeapFree(GetProcessHeap(), 0, lpwhr
->pProxyAuthInfo
);
1533 lpwhr
->pProxyAuthInfo
= NULL
;
1536 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszPath
);
1537 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVerb
);
1538 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszRawHeaders
);
1539 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVersion
);
1540 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszStatusText
);
1542 for (i
= 0; i
< lpwhr
->nCustHeaders
; i
++)
1544 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[i
].lpszField
);
1545 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[i
].lpszValue
);
1548 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
);
1549 HeapFree(GetProcessHeap(), 0, lpwhr
);
1552 static void HTTPREQ_CloseConnection(object_header_t
*hdr
)
1554 http_request_t
*lpwhr
= (http_request_t
*) hdr
;
1556 TRACE("%p\n",lpwhr
);
1559 if(lpwhr
->gzip_stream
) {
1560 inflateEnd(&lpwhr
->gzip_stream
->zstream
);
1561 HeapFree(GetProcessHeap(), 0, lpwhr
->gzip_stream
);
1562 lpwhr
->gzip_stream
= NULL
;
1566 if (!NETCON_connected(&lpwhr
->netConnection
))
1569 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1570 INTERNET_STATUS_CLOSING_CONNECTION
, 0, 0);
1572 NETCON_close(&lpwhr
->netConnection
);
1574 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1575 INTERNET_STATUS_CONNECTION_CLOSED
, 0, 0);
1578 static BOOL
HTTP_GetRequestURL(http_request_t
*req
, LPWSTR buf
)
1580 LPHTTPHEADERW host_header
;
1582 static const WCHAR formatW
[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1584 host_header
= HTTP_GetHeader(req
, hostW
);
1588 sprintfW(buf
, formatW
, host_header
->lpszValue
, req
->lpszPath
); /* FIXME */
1592 static DWORD
HTTPREQ_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
1594 http_request_t
*req
= (http_request_t
*)hdr
;
1597 case INTERNET_OPTION_SECURITY_FLAGS
:
1599 http_session_t
*lpwhs
;
1600 lpwhs
= req
->lpHttpSession
;
1602 if (*size
< sizeof(ULONG
))
1603 return ERROR_INSUFFICIENT_BUFFER
;
1605 *size
= sizeof(DWORD
);
1606 if (lpwhs
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1607 *(DWORD
*)buffer
= SECURITY_FLAG_SECURE
;
1609 *(DWORD
*)buffer
= 0;
1610 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD
*)buffer
);
1611 return ERROR_SUCCESS
;
1614 case INTERNET_OPTION_HANDLE_TYPE
:
1615 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1617 if (*size
< sizeof(ULONG
))
1618 return ERROR_INSUFFICIENT_BUFFER
;
1620 *size
= sizeof(DWORD
);
1621 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_HTTP_REQUEST
;
1622 return ERROR_SUCCESS
;
1624 case INTERNET_OPTION_URL
: {
1625 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1630 static const WCHAR httpW
[] = {'h','t','t','p',':','/','/',0};
1632 TRACE("INTERNET_OPTION_URL\n");
1634 host
= HTTP_GetHeader(req
, hostW
);
1635 strcpyW(url
, httpW
);
1636 strcatW(url
, host
->lpszValue
);
1637 if (NULL
!= (pch
= strchrW(url
+ strlenW(httpW
), ':')))
1639 strcatW(url
, req
->lpszPath
);
1641 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url
));
1644 len
= (strlenW(url
)+1) * sizeof(WCHAR
);
1646 return ERROR_INSUFFICIENT_BUFFER
;
1649 strcpyW(buffer
, url
);
1650 return ERROR_SUCCESS
;
1652 len
= WideCharToMultiByte(CP_ACP
, 0, url
, -1, buffer
, *size
, NULL
, NULL
);
1654 return ERROR_INSUFFICIENT_BUFFER
;
1657 return ERROR_SUCCESS
;
1661 case INTERNET_OPTION_CACHE_TIMESTAMPS
: {
1662 INTERNET_CACHE_ENTRY_INFOW
*info
;
1663 INTERNET_CACHE_TIMESTAMPS
*ts
= buffer
;
1664 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1665 DWORD nbytes
, error
;
1668 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1670 if (*size
< sizeof(*ts
))
1672 *size
= sizeof(*ts
);
1673 return ERROR_INSUFFICIENT_BUFFER
;
1676 HTTP_GetRequestURL(req
, url
);
1677 ret
= GetUrlCacheEntryInfoW(url
, NULL
, &nbytes
);
1678 error
= GetLastError();
1679 if (!ret
&& error
== ERROR_INSUFFICIENT_BUFFER
)
1681 if (!(info
= HeapAlloc(GetProcessHeap(), 0, nbytes
)))
1682 return ERROR_OUTOFMEMORY
;
1684 GetUrlCacheEntryInfoW(url
, info
, &nbytes
);
1686 ts
->ftExpires
= info
->ExpireTime
;
1687 ts
->ftLastModified
= info
->LastModifiedTime
;
1689 HeapFree(GetProcessHeap(), 0, info
);
1690 *size
= sizeof(*ts
);
1691 return ERROR_SUCCESS
;
1696 case INTERNET_OPTION_DATAFILE_NAME
: {
1699 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1701 if(!req
->lpszCacheFile
) {
1703 return ERROR_INTERNET_ITEM_NOT_FOUND
;
1707 req_size
= (lstrlenW(req
->lpszCacheFile
)+1) * sizeof(WCHAR
);
1708 if(*size
< req_size
)
1709 return ERROR_INSUFFICIENT_BUFFER
;
1712 memcpy(buffer
, req
->lpszCacheFile
, *size
);
1713 return ERROR_SUCCESS
;
1715 req_size
= WideCharToMultiByte(CP_ACP
, 0, req
->lpszCacheFile
, -1, NULL
, 0, NULL
, NULL
);
1716 if (req_size
> *size
)
1717 return ERROR_INSUFFICIENT_BUFFER
;
1719 *size
= WideCharToMultiByte(CP_ACP
, 0, req
->lpszCacheFile
,
1720 -1, buffer
, *size
, NULL
, NULL
);
1721 return ERROR_SUCCESS
;
1725 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT
: {
1726 PCCERT_CONTEXT context
;
1728 if(*size
< sizeof(INTERNET_CERTIFICATE_INFOW
)) {
1729 *size
= sizeof(INTERNET_CERTIFICATE_INFOW
);
1730 return ERROR_INSUFFICIENT_BUFFER
;
1733 context
= (PCCERT_CONTEXT
)NETCON_GetCert(&(req
->netConnection
));
1735 INTERNET_CERTIFICATE_INFOW
*info
= (INTERNET_CERTIFICATE_INFOW
*)buffer
;
1738 memset(info
, 0, sizeof(INTERNET_CERTIFICATE_INFOW
));
1739 info
->ftExpiry
= context
->pCertInfo
->NotAfter
;
1740 info
->ftStart
= context
->pCertInfo
->NotBefore
;
1742 len
= CertNameToStrW(context
->dwCertEncodingType
,
1743 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1744 info
->lpszSubjectInfo
= LocalAlloc(0, len
*sizeof(WCHAR
));
1745 if(info
->lpszSubjectInfo
)
1746 CertNameToStrW(context
->dwCertEncodingType
,
1747 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
,
1748 info
->lpszSubjectInfo
, len
);
1749 len
= CertNameToStrW(context
->dwCertEncodingType
,
1750 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1751 info
->lpszIssuerInfo
= LocalAlloc(0, len
*sizeof(WCHAR
));
1752 if (info
->lpszIssuerInfo
)
1753 CertNameToStrW(context
->dwCertEncodingType
,
1754 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
,
1755 info
->lpszIssuerInfo
, len
);
1757 INTERNET_CERTIFICATE_INFOA
*infoA
= (INTERNET_CERTIFICATE_INFOA
*)info
;
1759 len
= CertNameToStrA(context
->dwCertEncodingType
,
1760 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1761 infoA
->lpszSubjectInfo
= LocalAlloc(0, len
);
1762 if(infoA
->lpszSubjectInfo
)
1763 CertNameToStrA(context
->dwCertEncodingType
,
1764 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
,
1765 infoA
->lpszSubjectInfo
, len
);
1766 len
= CertNameToStrA(context
->dwCertEncodingType
,
1767 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1768 infoA
->lpszIssuerInfo
= LocalAlloc(0, len
);
1769 if(infoA
->lpszIssuerInfo
)
1770 CertNameToStrA(context
->dwCertEncodingType
,
1771 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
,
1772 infoA
->lpszIssuerInfo
, len
);
1776 * Contrary to MSDN, these do not appear to be set.
1778 * lpszSignatureAlgName
1779 * lpszEncryptionAlgName
1782 CertFreeCertificateContext(context
);
1783 return ERROR_SUCCESS
;
1788 return INET_QueryOption(option
, buffer
, size
, unicode
);
1791 static DWORD
HTTPREQ_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
1793 http_request_t
*req
= (http_request_t
*)hdr
;
1796 case INTERNET_OPTION_SEND_TIMEOUT
:
1797 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
1798 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1800 if (size
!= sizeof(DWORD
))
1801 return ERROR_INVALID_PARAMETER
;
1803 return NETCON_set_timeout(&req
->netConnection
, option
== INTERNET_OPTION_SEND_TIMEOUT
,
1806 case INTERNET_OPTION_USERNAME
:
1807 HeapFree(GetProcessHeap(), 0, req
->lpHttpSession
->lpszUserName
);
1808 if (!(req
->lpHttpSession
->lpszUserName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
1809 return ERROR_SUCCESS
;
1811 case INTERNET_OPTION_PASSWORD
:
1812 HeapFree(GetProcessHeap(), 0, req
->lpHttpSession
->lpszPassword
);
1813 if (!(req
->lpHttpSession
->lpszPassword
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
1814 return ERROR_SUCCESS
;
1815 case INTERNET_OPTION_HTTP_DECODING
:
1816 if(size
!= sizeof(BOOL
))
1817 return ERROR_INVALID_PARAMETER
;
1818 req
->decoding
= *(BOOL
*)buffer
;
1819 return ERROR_SUCCESS
;
1822 return ERROR_INTERNET_INVALID_OPTION
;
1825 /* read some more data into the read buffer (the read section must be held) */
1826 static BOOL
read_more_data( http_request_t
*req
, int maxlen
)
1832 /* move existing data to the start of the buffer */
1834 memmove( req
->read_buf
, req
->read_buf
+ req
->read_pos
, req
->read_size
);
1838 if (maxlen
== -1) maxlen
= sizeof(req
->read_buf
);
1840 if(!NETCON_recv( &req
->netConnection
, req
->read_buf
+ req
->read_size
,
1841 maxlen
- req
->read_size
, 0, &len
))
1844 req
->read_size
+= len
;
1848 /* remove some amount of data from the read buffer (the read section must be held) */
1849 static void remove_data( http_request_t
*req
, int count
)
1851 if (!(req
->read_size
-= count
)) req
->read_pos
= 0;
1852 else req
->read_pos
+= count
;
1855 static BOOL
read_line( http_request_t
*req
, LPSTR buffer
, DWORD
*len
)
1857 int count
, bytes_read
, pos
= 0;
1859 EnterCriticalSection( &req
->read_section
);
1862 BYTE
*eol
= memchr( req
->read_buf
+ req
->read_pos
, '\n', req
->read_size
);
1866 count
= eol
- (req
->read_buf
+ req
->read_pos
);
1867 bytes_read
= count
+ 1;
1869 else count
= bytes_read
= req
->read_size
;
1871 count
= min( count
, *len
- pos
);
1872 memcpy( buffer
+ pos
, req
->read_buf
+ req
->read_pos
, count
);
1874 remove_data( req
, bytes_read
);
1877 if (!read_more_data( req
, -1 ) || !req
->read_size
)
1880 TRACE( "returning empty string\n" );
1881 LeaveCriticalSection( &req
->read_section
);
1885 LeaveCriticalSection( &req
->read_section
);
1889 if (pos
&& buffer
[pos
- 1] == '\r') pos
--;
1892 buffer
[*len
- 1] = 0;
1893 TRACE( "returning %s\n", debugstr_a(buffer
));
1897 /* discard data contents until we reach end of line (the read section must be held) */
1898 static BOOL
discard_eol( http_request_t
*req
)
1902 BYTE
*eol
= memchr( req
->read_buf
+ req
->read_pos
, '\n', req
->read_size
);
1905 remove_data( req
, (eol
+ 1) - (req
->read_buf
+ req
->read_pos
) );
1908 req
->read_pos
= req
->read_size
= 0; /* discard everything */
1909 if (!read_more_data( req
, -1 )) return FALSE
;
1910 } while (req
->read_size
);
1914 /* read the size of the next chunk (the read section must be held) */
1915 static BOOL
start_next_chunk( http_request_t
*req
)
1917 DWORD chunk_size
= 0;
1919 if (!req
->dwContentLength
) return TRUE
;
1920 if (req
->dwContentLength
== req
->dwContentRead
)
1922 /* read terminator for the previous chunk */
1923 if (!discard_eol( req
)) return FALSE
;
1924 req
->dwContentLength
= ~0u;
1925 req
->dwContentRead
= 0;
1929 while (req
->read_size
)
1931 char ch
= req
->read_buf
[req
->read_pos
];
1932 if (ch
>= '0' && ch
<= '9') chunk_size
= chunk_size
* 16 + ch
- '0';
1933 else if (ch
>= 'a' && ch
<= 'f') chunk_size
= chunk_size
* 16 + ch
- 'a' + 10;
1934 else if (ch
>= 'A' && ch
<= 'F') chunk_size
= chunk_size
* 16 + ch
- 'A' + 10;
1935 else if (ch
== ';' || ch
== '\r' || ch
== '\n')
1937 TRACE( "reading %u byte chunk\n", chunk_size
);
1938 req
->dwContentLength
= chunk_size
;
1939 req
->dwContentRead
= 0;
1940 if (!discard_eol( req
)) return FALSE
;
1943 remove_data( req
, 1 );
1945 if (!read_more_data( req
, -1 )) return FALSE
;
1946 if (!req
->read_size
)
1948 req
->dwContentLength
= req
->dwContentRead
= 0;
1954 /* check if we have reached the end of the data to read (the read section must be held) */
1955 static BOOL
end_of_read_data( http_request_t
*req
)
1957 if (req
->gzip_stream
) return req
->gzip_stream
->end_of_data
&& !req
->gzip_stream
->buf_size
;
1958 if (req
->read_chunked
) return (req
->dwContentLength
== 0);
1959 if (req
->dwContentLength
== ~0u) return FALSE
;
1960 return (req
->dwContentLength
== req
->dwContentRead
);
1963 /* fetch some more data into the read buffer (the read section must be held) */
1964 static BOOL
refill_buffer( http_request_t
*req
)
1966 int len
= sizeof(req
->read_buf
);
1968 if (req
->read_chunked
&& (req
->dwContentLength
== ~0u || req
->dwContentLength
== req
->dwContentRead
))
1970 if (!start_next_chunk( req
)) return FALSE
;
1973 if (req
->dwContentLength
!= ~0u) len
= min( len
, req
->dwContentLength
- req
->dwContentRead
);
1974 if (len
<= req
->read_size
) return TRUE
;
1976 if (!read_more_data( req
, len
)) return FALSE
;
1977 if (!req
->read_size
) req
->dwContentLength
= req
->dwContentRead
= 0;
1981 static DWORD
read_gzip_data(http_request_t
*req
, BYTE
*buf
, int size
, BOOL sync
, int *read_ret
)
1983 DWORD ret
= ERROR_SUCCESS
;
1987 z_stream
*zstream
= &req
->gzip_stream
->zstream
;
1990 while(read
< size
&& !req
->gzip_stream
->end_of_data
) {
1991 if(!req
->read_size
) {
1992 if(!sync
|| !refill_buffer(req
))
1996 zstream
->next_in
= req
->read_buf
+req
->read_pos
;
1997 zstream
->avail_in
= req
->read_size
;
1998 zstream
->next_out
= buf
+read
;
1999 zstream
->avail_out
= size
-read
;
2000 zres
= inflate(zstream
, Z_FULL_FLUSH
);
2001 read
= size
- zstream
->avail_out
;
2002 remove_data(req
, req
->read_size
-zstream
->avail_in
);
2003 if(zres
== Z_STREAM_END
) {
2004 TRACE("end of data\n");
2005 req
->gzip_stream
->end_of_data
= TRUE
;
2006 }else if(zres
!= Z_OK
) {
2007 WARN("inflate failed %d\n", zres
);
2009 ret
= ERROR_INTERNET_DECODING_FAILED
;
2019 static void refill_gzip_buffer(http_request_t
*req
)
2024 if(!req
->gzip_stream
|| !req
->read_size
|| req
->gzip_stream
->buf_size
== sizeof(req
->gzip_stream
->buf
))
2027 if(req
->gzip_stream
->buf_pos
) {
2028 if(req
->gzip_stream
->buf_size
)
2029 memmove(req
->gzip_stream
->buf
, req
->gzip_stream
->buf
+ req
->gzip_stream
->buf_pos
, req
->gzip_stream
->buf_size
);
2030 req
->gzip_stream
->buf_pos
= 0;
2033 res
= read_gzip_data(req
, req
->gzip_stream
->buf
+ req
->gzip_stream
->buf_size
,
2034 sizeof(req
->gzip_stream
->buf
) - req
->gzip_stream
->buf_size
, FALSE
, &len
);
2035 if(res
== ERROR_SUCCESS
)
2036 req
->gzip_stream
->buf_size
+= len
;
2039 /* return the size of data available to be read immediately (the read section must be held) */
2040 static DWORD
get_avail_data( http_request_t
*req
)
2042 if (req
->gzip_stream
) {
2043 refill_gzip_buffer(req
);
2044 return req
->gzip_stream
->buf_size
;
2046 if (req
->read_chunked
&& (req
->dwContentLength
== ~0u || req
->dwContentLength
== req
->dwContentRead
))
2048 return min( req
->read_size
, req
->dwContentLength
- req
->dwContentRead
);
2051 static void HTTP_ReceiveRequestData(http_request_t
*req
, BOOL first_notif
)
2053 INTERNET_ASYNC_RESULT iar
;
2057 EnterCriticalSection( &req
->read_section
);
2058 if (refill_buffer( req
)) {
2059 iar
.dwResult
= (DWORD_PTR
)req
->hdr
.hInternet
;
2060 iar
.dwError
= first_notif
? 0 : get_avail_data(req
);
2063 iar
.dwError
= INTERNET_GetLastError();
2065 LeaveCriticalSection( &req
->read_section
);
2067 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2068 sizeof(INTERNET_ASYNC_RESULT
));
2071 /* read data from the http connection (the read section must be held) */
2072 static DWORD
HTTPREQ_Read(http_request_t
*req
, void *buffer
, DWORD size
, DWORD
*read
, BOOL sync
)
2074 BOOL finished_reading
= FALSE
;
2075 int len
, bytes_read
= 0;
2076 DWORD ret
= ERROR_SUCCESS
;
2078 EnterCriticalSection( &req
->read_section
);
2080 if (req
->read_chunked
&& (req
->dwContentLength
== ~0u || req
->dwContentLength
== req
->dwContentRead
))
2082 if (!start_next_chunk( req
)) goto done
;
2085 if(req
->gzip_stream
) {
2086 if(req
->gzip_stream
->buf_size
) {
2087 bytes_read
= min(req
->gzip_stream
->buf_size
, size
);
2088 memcpy(buffer
, req
->gzip_stream
->buf
+ req
->gzip_stream
->buf_pos
, bytes_read
);
2089 req
->gzip_stream
->buf_pos
+= bytes_read
;
2090 req
->gzip_stream
->buf_size
-= bytes_read
;
2091 }else if(!req
->read_size
&& !req
->gzip_stream
->end_of_data
) {
2095 if(size
> bytes_read
) {
2096 ret
= read_gzip_data(req
, (BYTE
*)buffer
+bytes_read
, size
-bytes_read
, sync
, &len
);
2097 if(ret
== ERROR_SUCCESS
)
2101 finished_reading
= req
->gzip_stream
->end_of_data
&& !req
->gzip_stream
->buf_size
;
2103 if (req
->dwContentLength
!= ~0u) size
= min( size
, req
->dwContentLength
- req
->dwContentRead
);
2105 if (req
->read_size
) {
2106 bytes_read
= min( req
->read_size
, size
);
2107 memcpy( buffer
, req
->read_buf
+ req
->read_pos
, bytes_read
);
2108 remove_data( req
, bytes_read
);
2111 if (size
> bytes_read
&& (!bytes_read
|| sync
)) {
2112 if (NETCON_recv( &req
->netConnection
, (char *)buffer
+ bytes_read
, size
- bytes_read
,
2113 sync
? MSG_WAITALL
: 0, &len
))
2115 /* always return success, even if the network layer returns an error */
2118 finished_reading
= !bytes_read
&& req
->dwContentRead
== req
->dwContentLength
;
2121 req
->dwContentRead
+= bytes_read
;
2124 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read
, req
->dwContentRead
, req
->dwContentLength
);
2125 LeaveCriticalSection( &req
->read_section
);
2127 if(ret
== ERROR_SUCCESS
&& req
->lpszCacheFile
) {
2129 DWORD dwBytesWritten
;
2131 res
= WriteFile(req
->hCacheFile
, buffer
, bytes_read
, &dwBytesWritten
, NULL
);
2133 WARN("WriteFile failed: %u\n", GetLastError());
2136 if(finished_reading
)
2137 HTTP_FinishedReading(req
);
2143 static DWORD
HTTPREQ_ReadFile(object_header_t
*hdr
, void *buffer
, DWORD size
, DWORD
*read
)
2145 http_request_t
*req
= (http_request_t
*)hdr
;
2146 return HTTPREQ_Read(req
, buffer
, size
, read
, TRUE
);
2149 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST
*workRequest
)
2151 struct WORKREQ_INTERNETREADFILEEXA
const *data
= &workRequest
->u
.InternetReadFileExA
;
2152 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2153 INTERNET_ASYNC_RESULT iar
;
2156 TRACE("INTERNETREADFILEEXA %p\n", workRequest
->hdr
);
2158 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2159 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2161 iar
.dwResult
= res
== ERROR_SUCCESS
;
2164 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2165 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2166 sizeof(INTERNET_ASYNC_RESULT
));
2169 static DWORD
HTTPREQ_ReadFileExA(object_header_t
*hdr
, INTERNET_BUFFERSA
*buffers
,
2170 DWORD flags
, DWORD_PTR context
)
2172 http_request_t
*req
= (http_request_t
*)hdr
;
2175 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2176 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2178 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2179 return ERROR_INVALID_PARAMETER
;
2181 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2183 if ((hdr
->dwFlags
& INTERNET_FLAG_ASYNC
) && !get_avail_data(req
))
2185 WORKREQUEST workRequest
;
2187 if (TryEnterCriticalSection( &req
->read_section
))
2189 if (get_avail_data(req
))
2191 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2192 &buffers
->dwBufferLength
, FALSE
);
2193 LeaveCriticalSection( &req
->read_section
);
2196 LeaveCriticalSection( &req
->read_section
);
2199 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExAProc
;
2200 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2201 workRequest
.u
.InternetReadFileExA
.lpBuffersOut
= buffers
;
2203 INTERNET_AsyncCall(&workRequest
);
2205 return ERROR_IO_PENDING
;
2208 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
, &buffers
->dwBufferLength
,
2209 !(flags
& IRF_NO_WAIT
));
2212 if (res
== ERROR_SUCCESS
) {
2213 DWORD size
= buffers
->dwBufferLength
;
2214 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2215 &size
, sizeof(size
));
2221 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST
*workRequest
)
2223 struct WORKREQ_INTERNETREADFILEEXW
const *data
= &workRequest
->u
.InternetReadFileExW
;
2224 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2225 INTERNET_ASYNC_RESULT iar
;
2228 TRACE("INTERNETREADFILEEXW %p\n", workRequest
->hdr
);
2230 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2231 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2233 iar
.dwResult
= res
== ERROR_SUCCESS
;
2236 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2237 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2238 sizeof(INTERNET_ASYNC_RESULT
));
2241 static DWORD
HTTPREQ_ReadFileExW(object_header_t
*hdr
, INTERNET_BUFFERSW
*buffers
,
2242 DWORD flags
, DWORD_PTR context
)
2245 http_request_t
*req
= (http_request_t
*)hdr
;
2248 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2249 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2251 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2252 return ERROR_INVALID_PARAMETER
;
2254 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2256 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2258 WORKREQUEST workRequest
;
2260 if (TryEnterCriticalSection( &req
->read_section
))
2262 if (get_avail_data(req
))
2264 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2265 &buffers
->dwBufferLength
, FALSE
);
2266 LeaveCriticalSection( &req
->read_section
);
2269 LeaveCriticalSection( &req
->read_section
);
2272 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExWProc
;
2273 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2274 workRequest
.u
.InternetReadFileExW
.lpBuffersOut
= buffers
;
2276 INTERNET_AsyncCall(&workRequest
);
2278 return ERROR_IO_PENDING
;
2281 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
, &buffers
->dwBufferLength
,
2282 !(flags
& IRF_NO_WAIT
));
2285 if (res
== ERROR_SUCCESS
) {
2286 DWORD size
= buffers
->dwBufferLength
;
2287 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2288 &size
, sizeof(size
));
2294 static BOOL
HTTPREQ_WriteFile(object_header_t
*hdr
, const void *buffer
, DWORD size
, DWORD
*written
)
2297 http_request_t
*lpwhr
= (http_request_t
*)hdr
;
2299 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
2302 if ((ret
= NETCON_send(&lpwhr
->netConnection
, buffer
, size
, 0, (LPINT
)written
)))
2303 lpwhr
->dwBytesWritten
+= *written
;
2305 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_SENT
, written
, sizeof(DWORD
));
2309 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST
*workRequest
)
2311 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2313 HTTP_ReceiveRequestData(req
, FALSE
);
2316 static DWORD
HTTPREQ_QueryDataAvailable(object_header_t
*hdr
, DWORD
*available
, DWORD flags
, DWORD_PTR ctx
)
2318 http_request_t
*req
= (http_request_t
*)hdr
;
2320 TRACE("(%p %p %x %lx)\n", req
, available
, flags
, ctx
);
2322 if (req
->lpHttpSession
->lpAppInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
2324 WORKREQUEST workRequest
;
2326 /* never wait, if we can't enter the section we queue an async request right away */
2327 if (TryEnterCriticalSection( &req
->read_section
))
2329 if ((*available
= get_avail_data( req
))) goto done
;
2330 if (end_of_read_data( req
)) goto done
;
2331 LeaveCriticalSection( &req
->read_section
);
2334 workRequest
.asyncproc
= HTTPREQ_AsyncQueryDataAvailableProc
;
2335 workRequest
.hdr
= WININET_AddRef( &req
->hdr
);
2337 INTERNET_AsyncCall(&workRequest
);
2339 return ERROR_IO_PENDING
;
2342 EnterCriticalSection( &req
->read_section
);
2344 if (!(*available
= get_avail_data( req
)) && !end_of_read_data( req
))
2346 refill_buffer( req
);
2347 *available
= get_avail_data( req
);
2351 if (*available
== sizeof(req
->read_buf
) && !req
->gzip_stream
) /* check if we have even more pending in the socket */
2354 if (NETCON_query_data_available(&req
->netConnection
, &extra
))
2355 *available
= min( *available
+ extra
, req
->dwContentLength
- req
->dwContentRead
);
2357 LeaveCriticalSection( &req
->read_section
);
2359 TRACE( "returning %u\n", *available
);
2360 return ERROR_SUCCESS
;
2363 static const object_vtbl_t HTTPREQVtbl
= {
2365 HTTPREQ_CloseConnection
,
2366 HTTPREQ_QueryOption
,
2369 HTTPREQ_ReadFileExA
,
2370 HTTPREQ_ReadFileExW
,
2372 HTTPREQ_QueryDataAvailable
,
2376 /***********************************************************************
2377 * HTTP_HttpOpenRequestW (internal)
2379 * Open a HTTP request handle
2382 * HINTERNET a HTTP request handle on success
2386 HINTERNET WINAPI
HTTP_HttpOpenRequestW(http_session_t
*lpwhs
,
2387 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
2388 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
2389 DWORD dwFlags
, DWORD_PTR dwContext
)
2391 appinfo_t
*hIC
= NULL
;
2392 http_request_t
*lpwhr
;
2393 LPWSTR lpszHostName
= NULL
;
2394 HINTERNET handle
= NULL
;
2395 static const WCHAR szHostForm
[] = {'%','s',':','%','u',0};
2400 assert( lpwhs
->hdr
.htype
== WH_HHTTPSESSION
);
2401 hIC
= lpwhs
->lpAppInfo
;
2403 lpwhr
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(http_request_t
));
2406 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
2409 lpwhr
->hdr
.htype
= WH_HHTTPREQ
;
2410 lpwhr
->hdr
.vtbl
= &HTTPREQVtbl
;
2411 lpwhr
->hdr
.dwFlags
= dwFlags
;
2412 lpwhr
->hdr
.dwContext
= dwContext
;
2413 lpwhr
->hdr
.refs
= 1;
2414 lpwhr
->hdr
.lpfnStatusCB
= lpwhs
->hdr
.lpfnStatusCB
;
2415 lpwhr
->hdr
.dwInternalFlags
= lpwhs
->hdr
.dwInternalFlags
& INET_CALLBACKW
;
2416 lpwhr
->dwContentLength
= ~0u;
2417 InitializeCriticalSection( &lpwhr
->read_section
);
2419 WININET_AddRef( &lpwhs
->hdr
);
2420 lpwhr
->lpHttpSession
= lpwhs
;
2421 list_add_head( &lpwhs
->hdr
.children
, &lpwhr
->hdr
.entry
);
2423 lpszHostName
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
) *
2424 (strlenW(lpwhs
->lpszHostName
) + 7 /* length of ":65535" + 1 */));
2425 if (NULL
== lpszHostName
)
2427 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
2431 handle
= WININET_AllocHandle( &lpwhr
->hdr
);
2434 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
2438 if (!NETCON_init(&lpwhr
->netConnection
, dwFlags
& INTERNET_FLAG_SECURE
))
2440 InternetCloseHandle( handle
);
2445 if (lpszObjectName
&& *lpszObjectName
) {
2449 rc
= UrlEscapeW(lpszObjectName
, NULL
, &len
, URL_ESCAPE_SPACES_ONLY
);
2450 if (rc
!= E_POINTER
)
2451 len
= strlenW(lpszObjectName
)+1;
2452 lpwhr
->lpszPath
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
2453 rc
= UrlEscapeW(lpszObjectName
, lpwhr
->lpszPath
, &len
,
2454 URL_ESCAPE_SPACES_ONLY
);
2457 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName
),rc
);
2458 strcpyW(lpwhr
->lpszPath
,lpszObjectName
);
2461 static const WCHAR slashW
[] = {'/',0};
2463 lpwhr
->lpszPath
= heap_strdupW(slashW
);
2466 if (lpszReferrer
&& *lpszReferrer
)
2467 HTTP_ProcessHeader(lpwhr
, HTTP_REFERER
, lpszReferrer
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2469 if (lpszAcceptTypes
)
2472 for (i
= 0; lpszAcceptTypes
[i
]; i
++)
2474 if (!*lpszAcceptTypes
[i
]) continue;
2475 HTTP_ProcessHeader(lpwhr
, HTTP_ACCEPT
, lpszAcceptTypes
[i
],
2476 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
|
2477 HTTP_ADDHDR_FLAG_REQ
|
2478 (i
== 0 ? HTTP_ADDHDR_FLAG_REPLACE
: 0));
2482 lpwhr
->lpszVerb
= heap_strdupW(lpszVerb
&& *lpszVerb
? lpszVerb
: szGET
);
2483 lpwhr
->lpszVersion
= heap_strdupW(lpszVersion
? lpszVersion
: g_szHttp1_1
);
2485 if (lpwhs
->nHostPort
!= INTERNET_INVALID_PORT_NUMBER
&&
2486 lpwhs
->nHostPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
2487 lpwhs
->nHostPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
2489 sprintfW(lpszHostName
, szHostForm
, lpwhs
->lpszHostName
, lpwhs
->nHostPort
);
2490 HTTP_ProcessHeader(lpwhr
, hostW
, lpszHostName
,
2491 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2494 HTTP_ProcessHeader(lpwhr
, hostW
, lpwhs
->lpszHostName
,
2495 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2497 if (lpwhs
->nServerPort
== INTERNET_INVALID_PORT_NUMBER
)
2498 lpwhs
->nServerPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
2499 INTERNET_DEFAULT_HTTPS_PORT
:
2500 INTERNET_DEFAULT_HTTP_PORT
);
2502 if (lpwhs
->nHostPort
== INTERNET_INVALID_PORT_NUMBER
)
2503 lpwhs
->nHostPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
2504 INTERNET_DEFAULT_HTTPS_PORT
:
2505 INTERNET_DEFAULT_HTTP_PORT
);
2507 if (NULL
!= hIC
->lpszProxy
&& hIC
->lpszProxy
[0] != 0)
2508 HTTP_DealWithProxy( hIC
, lpwhs
, lpwhr
);
2510 INTERNET_SendCallback(&lpwhs
->hdr
, dwContext
,
2511 INTERNET_STATUS_HANDLE_CREATED
, &handle
,
2515 HeapFree(GetProcessHeap(), 0, lpszHostName
);
2517 WININET_Release( &lpwhr
->hdr
);
2519 TRACE("<-- %p (%p)\n", handle
, lpwhr
);
2523 /* read any content returned by the server so that the connection can be
2525 static void HTTP_DrainContent(http_request_t
*req
)
2529 if (!NETCON_connected(&req
->netConnection
)) return;
2531 if (req
->dwContentLength
== -1)
2533 NETCON_close(&req
->netConnection
);
2536 if (!strcmpW(req
->lpszVerb
, szHEAD
)) return;
2541 if (HTTPREQ_Read(req
, buffer
, sizeof(buffer
), &bytes_read
, TRUE
) != ERROR_SUCCESS
)
2543 } while (bytes_read
);
2546 static const LPCWSTR header_lookup
[] = {
2547 szMime_Version
, /* HTTP_QUERY_MIME_VERSION = 0 */
2548 szContent_Type
, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2549 szContent_Transfer_Encoding
,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2550 szContent_ID
, /* HTTP_QUERY_CONTENT_ID = 3 */
2551 NULL
, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2552 szContent_Length
, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2553 szContent_Language
, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2554 szAllow
, /* HTTP_QUERY_ALLOW = 7 */
2555 szPublic
, /* HTTP_QUERY_PUBLIC = 8 */
2556 szDate
, /* HTTP_QUERY_DATE = 9 */
2557 szExpires
, /* HTTP_QUERY_EXPIRES = 10 */
2558 szLast_Modified
, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2559 NULL
, /* HTTP_QUERY_MESSAGE_ID = 12 */
2560 szURI
, /* HTTP_QUERY_URI = 13 */
2561 szFrom
, /* HTTP_QUERY_DERIVED_FROM = 14 */
2562 NULL
, /* HTTP_QUERY_COST = 15 */
2563 NULL
, /* HTTP_QUERY_LINK = 16 */
2564 szPragma
, /* HTTP_QUERY_PRAGMA = 17 */
2565 NULL
, /* HTTP_QUERY_VERSION = 18 */
2566 szStatus
, /* HTTP_QUERY_STATUS_CODE = 19 */
2567 NULL
, /* HTTP_QUERY_STATUS_TEXT = 20 */
2568 NULL
, /* HTTP_QUERY_RAW_HEADERS = 21 */
2569 NULL
, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2570 szConnection
, /* HTTP_QUERY_CONNECTION = 23 */
2571 szAccept
, /* HTTP_QUERY_ACCEPT = 24 */
2572 szAccept_Charset
, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2573 szAccept_Encoding
, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2574 szAccept_Language
, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2575 szAuthorization
, /* HTTP_QUERY_AUTHORIZATION = 28 */
2576 szContent_Encoding
, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2577 NULL
, /* HTTP_QUERY_FORWARDED = 30 */
2578 NULL
, /* HTTP_QUERY_FROM = 31 */
2579 szIf_Modified_Since
, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2580 szLocation
, /* HTTP_QUERY_LOCATION = 33 */
2581 NULL
, /* HTTP_QUERY_ORIG_URI = 34 */
2582 szReferer
, /* HTTP_QUERY_REFERER = 35 */
2583 szRetry_After
, /* HTTP_QUERY_RETRY_AFTER = 36 */
2584 szServer
, /* HTTP_QUERY_SERVER = 37 */
2585 NULL
, /* HTTP_TITLE = 38 */
2586 szUser_Agent
, /* HTTP_QUERY_USER_AGENT = 39 */
2587 szWWW_Authenticate
, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2588 szProxy_Authenticate
, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2589 szAccept_Ranges
, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2590 szSet_Cookie
, /* HTTP_QUERY_SET_COOKIE = 43 */
2591 szCookie
, /* HTTP_QUERY_COOKIE = 44 */
2592 NULL
, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2593 NULL
, /* HTTP_QUERY_REFRESH = 46 */
2594 NULL
, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2595 szAge
, /* HTTP_QUERY_AGE = 48 */
2596 szCache_Control
, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2597 szContent_Base
, /* HTTP_QUERY_CONTENT_BASE = 50 */
2598 szContent_Location
, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2599 szContent_MD5
, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2600 szContent_Range
, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2601 szETag
, /* HTTP_QUERY_ETAG = 54 */
2602 hostW
, /* HTTP_QUERY_HOST = 55 */
2603 szIf_Match
, /* HTTP_QUERY_IF_MATCH = 56 */
2604 szIf_None_Match
, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2605 szIf_Range
, /* HTTP_QUERY_IF_RANGE = 58 */
2606 szIf_Unmodified_Since
, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2607 szMax_Forwards
, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2608 szProxy_Authorization
, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2609 szRange
, /* HTTP_QUERY_RANGE = 62 */
2610 szTransfer_Encoding
, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2611 szUpgrade
, /* HTTP_QUERY_UPGRADE = 64 */
2612 szVary
, /* HTTP_QUERY_VARY = 65 */
2613 szVia
, /* HTTP_QUERY_VIA = 66 */
2614 szWarning
, /* HTTP_QUERY_WARNING = 67 */
2615 szExpect
, /* HTTP_QUERY_EXPECT = 68 */
2616 szProxy_Connection
, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2617 szUnless_Modified_Since
, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2620 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2622 /***********************************************************************
2623 * HTTP_HttpQueryInfoW (internal)
2625 static BOOL
HTTP_HttpQueryInfoW(http_request_t
*lpwhr
, DWORD dwInfoLevel
,
2626 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
2628 LPHTTPHEADERW lphttpHdr
= NULL
;
2629 BOOL bSuccess
= FALSE
;
2630 BOOL request_only
= dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
;
2631 INT requested_index
= lpdwIndex
? *lpdwIndex
: 0;
2632 DWORD level
= (dwInfoLevel
& ~HTTP_QUERY_MODIFIER_FLAGS_MASK
);
2635 /* Find requested header structure */
2638 case HTTP_QUERY_CUSTOM
:
2639 if (!lpBuffer
) return FALSE
;
2640 index
= HTTP_GetCustomHeaderIndex(lpwhr
, lpBuffer
, requested_index
, request_only
);
2642 case HTTP_QUERY_RAW_HEADERS_CRLF
:
2649 headers
= HTTP_BuildHeaderRequestString(lpwhr
, lpwhr
->lpszVerb
, lpwhr
->lpszPath
, lpwhr
->lpszVersion
);
2651 headers
= lpwhr
->lpszRawHeaders
;
2654 len
= strlenW(headers
) * sizeof(WCHAR
);
2656 if (len
+ sizeof(WCHAR
) > *lpdwBufferLength
)
2658 len
+= sizeof(WCHAR
);
2659 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER
);
2665 memcpy(lpBuffer
, headers
, len
+ sizeof(WCHAR
));
2668 len
= strlenW(szCrLf
) * sizeof(WCHAR
);
2669 memcpy(lpBuffer
, szCrLf
, sizeof(szCrLf
));
2671 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
/ sizeof(WCHAR
)));
2674 *lpdwBufferLength
= len
;
2677 HeapFree(GetProcessHeap(), 0, headers
);
2680 case HTTP_QUERY_RAW_HEADERS
:
2682 LPWSTR
* ppszRawHeaderLines
= HTTP_Tokenize(lpwhr
->lpszRawHeaders
, szCrLf
);
2684 LPWSTR pszString
= lpBuffer
;
2686 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
2687 size
+= strlenW(ppszRawHeaderLines
[i
]) + 1;
2689 if (size
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
2691 HTTP_FreeTokens(ppszRawHeaderLines
);
2692 *lpdwBufferLength
= (size
+ 1) * sizeof(WCHAR
);
2693 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER
);
2698 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
2700 DWORD len
= strlenW(ppszRawHeaderLines
[i
]);
2701 memcpy(pszString
, ppszRawHeaderLines
[i
], (len
+1)*sizeof(WCHAR
));
2705 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, size
));
2707 *lpdwBufferLength
= size
* sizeof(WCHAR
);
2708 HTTP_FreeTokens(ppszRawHeaderLines
);
2712 case HTTP_QUERY_STATUS_TEXT
:
2713 if (lpwhr
->lpszStatusText
)
2715 DWORD len
= strlenW(lpwhr
->lpszStatusText
);
2716 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
2718 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
2719 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER
);
2724 memcpy(lpBuffer
, lpwhr
->lpszStatusText
, (len
+ 1) * sizeof(WCHAR
));
2725 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
2727 *lpdwBufferLength
= len
* sizeof(WCHAR
);
2731 case HTTP_QUERY_VERSION
:
2732 if (lpwhr
->lpszVersion
)
2734 DWORD len
= strlenW(lpwhr
->lpszVersion
);
2735 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
2737 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
2738 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER
);
2743 memcpy(lpBuffer
, lpwhr
->lpszVersion
, (len
+ 1) * sizeof(WCHAR
));
2744 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
2746 *lpdwBufferLength
= len
* sizeof(WCHAR
);
2750 case HTTP_QUERY_CONTENT_ENCODING
:
2751 index
= HTTP_GetCustomHeaderIndex(lpwhr
, header_lookup
[lpwhr
->gzip_stream
? HTTP_QUERY_CONTENT_TYPE
: level
],
2752 requested_index
,request_only
);
2755 assert (LAST_TABLE_HEADER
== (HTTP_QUERY_UNLESS_MODIFIED_SINCE
+ 1));
2757 if (level
< LAST_TABLE_HEADER
&& header_lookup
[level
])
2758 index
= HTTP_GetCustomHeaderIndex(lpwhr
, header_lookup
[level
],
2759 requested_index
,request_only
);
2763 lphttpHdr
= &lpwhr
->pCustHeaders
[index
];
2765 /* Ensure header satisfies requested attributes */
2767 ((dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
) &&
2768 (~lphttpHdr
->wFlags
& HDR_ISREQUEST
)))
2770 INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND
);
2774 if (lpdwIndex
&& level
!= HTTP_QUERY_STATUS_CODE
) (*lpdwIndex
)++;
2776 /* coalesce value to requested type */
2777 if (dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
&& lpBuffer
)
2779 *(int *)lpBuffer
= atoiW(lphttpHdr
->lpszValue
);
2780 TRACE(" returning number: %d\n", *(int *)lpBuffer
);
2783 else if (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
&& lpBuffer
)
2789 tmpTime
= ConvertTimeString(lphttpHdr
->lpszValue
);
2791 tmpTM
= *gmtime(&tmpTime
);
2792 STHook
= (SYSTEMTIME
*)lpBuffer
;
2793 STHook
->wDay
= tmpTM
.tm_mday
;
2794 STHook
->wHour
= tmpTM
.tm_hour
;
2795 STHook
->wMilliseconds
= 0;
2796 STHook
->wMinute
= tmpTM
.tm_min
;
2797 STHook
->wDayOfWeek
= tmpTM
.tm_wday
;
2798 STHook
->wMonth
= tmpTM
.tm_mon
+ 1;
2799 STHook
->wSecond
= tmpTM
.tm_sec
;
2800 STHook
->wYear
= tmpTM
.tm_year
;
2803 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
2804 STHook
->wYear
, STHook
->wMonth
, STHook
->wDay
, STHook
->wDayOfWeek
,
2805 STHook
->wHour
, STHook
->wMinute
, STHook
->wSecond
, STHook
->wMilliseconds
);
2807 else if (lphttpHdr
->lpszValue
)
2809 DWORD len
= (strlenW(lphttpHdr
->lpszValue
) + 1) * sizeof(WCHAR
);
2811 if (len
> *lpdwBufferLength
)
2813 *lpdwBufferLength
= len
;
2814 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER
);
2819 memcpy(lpBuffer
, lphttpHdr
->lpszValue
, len
);
2820 TRACE(" returning string: %s\n", debugstr_w(lpBuffer
));
2822 *lpdwBufferLength
= len
- sizeof(WCHAR
);
2828 /***********************************************************************
2829 * HttpQueryInfoW (WININET.@)
2831 * Queries for information about an HTTP request
2838 BOOL WINAPI
HttpQueryInfoW(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
2839 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
2841 BOOL bSuccess
= FALSE
;
2842 http_request_t
*lpwhr
;
2844 if (TRACE_ON(wininet
)) {
2845 #define FE(x) { x, #x }
2846 static const wininet_flag_info query_flags
[] = {
2847 FE(HTTP_QUERY_MIME_VERSION
),
2848 FE(HTTP_QUERY_CONTENT_TYPE
),
2849 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING
),
2850 FE(HTTP_QUERY_CONTENT_ID
),
2851 FE(HTTP_QUERY_CONTENT_DESCRIPTION
),
2852 FE(HTTP_QUERY_CONTENT_LENGTH
),
2853 FE(HTTP_QUERY_CONTENT_LANGUAGE
),
2854 FE(HTTP_QUERY_ALLOW
),
2855 FE(HTTP_QUERY_PUBLIC
),
2856 FE(HTTP_QUERY_DATE
),
2857 FE(HTTP_QUERY_EXPIRES
),
2858 FE(HTTP_QUERY_LAST_MODIFIED
),
2859 FE(HTTP_QUERY_MESSAGE_ID
),
2861 FE(HTTP_QUERY_DERIVED_FROM
),
2862 FE(HTTP_QUERY_COST
),
2863 FE(HTTP_QUERY_LINK
),
2864 FE(HTTP_QUERY_PRAGMA
),
2865 FE(HTTP_QUERY_VERSION
),
2866 FE(HTTP_QUERY_STATUS_CODE
),
2867 FE(HTTP_QUERY_STATUS_TEXT
),
2868 FE(HTTP_QUERY_RAW_HEADERS
),
2869 FE(HTTP_QUERY_RAW_HEADERS_CRLF
),
2870 FE(HTTP_QUERY_CONNECTION
),
2871 FE(HTTP_QUERY_ACCEPT
),
2872 FE(HTTP_QUERY_ACCEPT_CHARSET
),
2873 FE(HTTP_QUERY_ACCEPT_ENCODING
),
2874 FE(HTTP_QUERY_ACCEPT_LANGUAGE
),
2875 FE(HTTP_QUERY_AUTHORIZATION
),
2876 FE(HTTP_QUERY_CONTENT_ENCODING
),
2877 FE(HTTP_QUERY_FORWARDED
),
2878 FE(HTTP_QUERY_FROM
),
2879 FE(HTTP_QUERY_IF_MODIFIED_SINCE
),
2880 FE(HTTP_QUERY_LOCATION
),
2881 FE(HTTP_QUERY_ORIG_URI
),
2882 FE(HTTP_QUERY_REFERER
),
2883 FE(HTTP_QUERY_RETRY_AFTER
),
2884 FE(HTTP_QUERY_SERVER
),
2885 FE(HTTP_QUERY_TITLE
),
2886 FE(HTTP_QUERY_USER_AGENT
),
2887 FE(HTTP_QUERY_WWW_AUTHENTICATE
),
2888 FE(HTTP_QUERY_PROXY_AUTHENTICATE
),
2889 FE(HTTP_QUERY_ACCEPT_RANGES
),
2890 FE(HTTP_QUERY_SET_COOKIE
),
2891 FE(HTTP_QUERY_COOKIE
),
2892 FE(HTTP_QUERY_REQUEST_METHOD
),
2893 FE(HTTP_QUERY_REFRESH
),
2894 FE(HTTP_QUERY_CONTENT_DISPOSITION
),
2896 FE(HTTP_QUERY_CACHE_CONTROL
),
2897 FE(HTTP_QUERY_CONTENT_BASE
),
2898 FE(HTTP_QUERY_CONTENT_LOCATION
),
2899 FE(HTTP_QUERY_CONTENT_MD5
),
2900 FE(HTTP_QUERY_CONTENT_RANGE
),
2901 FE(HTTP_QUERY_ETAG
),
2902 FE(HTTP_QUERY_HOST
),
2903 FE(HTTP_QUERY_IF_MATCH
),
2904 FE(HTTP_QUERY_IF_NONE_MATCH
),
2905 FE(HTTP_QUERY_IF_RANGE
),
2906 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE
),
2907 FE(HTTP_QUERY_MAX_FORWARDS
),
2908 FE(HTTP_QUERY_PROXY_AUTHORIZATION
),
2909 FE(HTTP_QUERY_RANGE
),
2910 FE(HTTP_QUERY_TRANSFER_ENCODING
),
2911 FE(HTTP_QUERY_UPGRADE
),
2912 FE(HTTP_QUERY_VARY
),
2914 FE(HTTP_QUERY_WARNING
),
2915 FE(HTTP_QUERY_CUSTOM
)
2917 static const wininet_flag_info modifier_flags
[] = {
2918 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS
),
2919 FE(HTTP_QUERY_FLAG_SYSTEMTIME
),
2920 FE(HTTP_QUERY_FLAG_NUMBER
),
2921 FE(HTTP_QUERY_FLAG_COALESCE
)
2924 DWORD info_mod
= dwInfoLevel
& HTTP_QUERY_MODIFIER_FLAGS_MASK
;
2925 DWORD info
= dwInfoLevel
& HTTP_QUERY_HEADER_MASK
;
2928 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest
, dwInfoLevel
, dwInfoLevel
);
2929 TRACE(" Attribute:");
2930 for (i
= 0; i
< (sizeof(query_flags
) / sizeof(query_flags
[0])); i
++) {
2931 if (query_flags
[i
].val
== info
) {
2932 TRACE(" %s", query_flags
[i
].name
);
2936 if (i
== (sizeof(query_flags
) / sizeof(query_flags
[0]))) {
2937 TRACE(" Unknown (%08x)", info
);
2940 TRACE(" Modifier:");
2941 for (i
= 0; i
< (sizeof(modifier_flags
) / sizeof(modifier_flags
[0])); i
++) {
2942 if (modifier_flags
[i
].val
& info_mod
) {
2943 TRACE(" %s", modifier_flags
[i
].name
);
2944 info_mod
&= ~ modifier_flags
[i
].val
;
2949 TRACE(" Unknown (%08x)", info_mod
);
2954 lpwhr
= (http_request_t
*) WININET_GetObject( hHttpRequest
);
2955 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
2957 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
2961 if (lpBuffer
== NULL
)
2962 *lpdwBufferLength
= 0;
2963 bSuccess
= HTTP_HttpQueryInfoW( lpwhr
, dwInfoLevel
,
2964 lpBuffer
, lpdwBufferLength
, lpdwIndex
);
2968 WININET_Release( &lpwhr
->hdr
);
2970 TRACE("%d <--\n", bSuccess
);
2974 /***********************************************************************
2975 * HttpQueryInfoA (WININET.@)
2977 * Queries for information about an HTTP request
2984 BOOL WINAPI
HttpQueryInfoA(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
2985 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
2991 if((dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
) ||
2992 (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
))
2994 return HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, lpBuffer
,
2995 lpdwBufferLength
, lpdwIndex
);
3001 len
= (*lpdwBufferLength
)*sizeof(WCHAR
);
3002 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3004 alloclen
= MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, NULL
, 0 ) * sizeof(WCHAR
);
3010 bufferW
= HeapAlloc( GetProcessHeap(), 0, alloclen
);
3011 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3012 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3013 MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, bufferW
, alloclen
/ sizeof(WCHAR
) );
3020 result
= HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, bufferW
,
3024 len
= WideCharToMultiByte( CP_ACP
,0, bufferW
, len
/ sizeof(WCHAR
) + 1,
3025 lpBuffer
, *lpdwBufferLength
, NULL
, NULL
);
3026 *lpdwBufferLength
= len
- 1;
3028 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer
));
3031 /* since the strings being returned from HttpQueryInfoW should be
3032 * only ASCII characters, it is reasonable to assume that all of
3033 * the Unicode characters can be reduced to a single byte */
3034 *lpdwBufferLength
= len
/ sizeof(WCHAR
);
3036 HeapFree(GetProcessHeap(), 0, bufferW
);
3041 /***********************************************************************
3042 * HttpSendRequestExA (WININET.@)
3044 * Sends the specified request to the HTTP server and allows chunked
3049 * Failure: FALSE, call GetLastError() for more information.
3051 BOOL WINAPI
HttpSendRequestExA(HINTERNET hRequest
,
3052 LPINTERNET_BUFFERSA lpBuffersIn
,
3053 LPINTERNET_BUFFERSA lpBuffersOut
,
3054 DWORD dwFlags
, DWORD_PTR dwContext
)
3056 INTERNET_BUFFERSW BuffersInW
;
3059 LPWSTR header
= NULL
;
3061 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
3062 lpBuffersOut
, dwFlags
, dwContext
);
3066 BuffersInW
.dwStructSize
= sizeof(LPINTERNET_BUFFERSW
);
3067 if (lpBuffersIn
->lpcszHeader
)
3069 headerlen
= MultiByteToWideChar(CP_ACP
,0,lpBuffersIn
->lpcszHeader
,
3070 lpBuffersIn
->dwHeadersLength
,0,0);
3071 header
= HeapAlloc(GetProcessHeap(),0,headerlen
*sizeof(WCHAR
));
3072 if (!(BuffersInW
.lpcszHeader
= header
))
3074 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
3077 BuffersInW
.dwHeadersLength
= MultiByteToWideChar(CP_ACP
, 0,
3078 lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
3082 BuffersInW
.lpcszHeader
= NULL
;
3083 BuffersInW
.dwHeadersTotal
= lpBuffersIn
->dwHeadersTotal
;
3084 BuffersInW
.lpvBuffer
= lpBuffersIn
->lpvBuffer
;
3085 BuffersInW
.dwBufferLength
= lpBuffersIn
->dwBufferLength
;
3086 BuffersInW
.dwBufferTotal
= lpBuffersIn
->dwBufferTotal
;
3087 BuffersInW
.Next
= NULL
;
3090 rc
= HttpSendRequestExW(hRequest
, lpBuffersIn
? &BuffersInW
: NULL
, NULL
, dwFlags
, dwContext
);
3092 HeapFree(GetProcessHeap(),0,header
);
3097 /***********************************************************************
3098 * HttpSendRequestExW (WININET.@)
3100 * Sends the specified request to the HTTP server and allows chunked
3105 * Failure: FALSE, call GetLastError() for more information.
3107 BOOL WINAPI
HttpSendRequestExW(HINTERNET hRequest
,
3108 LPINTERNET_BUFFERSW lpBuffersIn
,
3109 LPINTERNET_BUFFERSW lpBuffersOut
,
3110 DWORD dwFlags
, DWORD_PTR dwContext
)
3113 http_request_t
*lpwhr
;
3114 http_session_t
*lpwhs
;
3117 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
3118 lpBuffersOut
, dwFlags
, dwContext
);
3120 lpwhr
= (http_request_t
*) WININET_GetObject( hRequest
);
3122 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
3124 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
3128 lpwhs
= lpwhr
->lpHttpSession
;
3129 assert(lpwhs
->hdr
.htype
== WH_HHTTPSESSION
);
3130 hIC
= lpwhs
->lpAppInfo
;
3131 assert(hIC
->hdr
.htype
== WH_HINIT
);
3133 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
3135 WORKREQUEST workRequest
;
3136 struct WORKREQ_HTTPSENDREQUESTW
*req
;
3138 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
3139 workRequest
.hdr
= WININET_AddRef( &lpwhr
->hdr
);
3140 req
= &workRequest
.u
.HttpSendRequestW
;
3143 /* FIXME: this should use dwHeadersLength or may not be necessary at all */
3144 req
->lpszHeader
= heap_strdupW(lpBuffersIn
->lpcszHeader
);
3145 req
->dwHeaderLength
= lpBuffersIn
->dwHeadersLength
;
3146 req
->lpOptional
= lpBuffersIn
->lpvBuffer
;
3147 req
->dwOptionalLength
= lpBuffersIn
->dwBufferLength
;
3148 req
->dwContentLength
= lpBuffersIn
->dwBufferTotal
;
3152 req
->lpszHeader
= NULL
;
3153 req
->dwHeaderLength
= 0;
3154 req
->lpOptional
= NULL
;
3155 req
->dwOptionalLength
= 0;
3156 req
->dwContentLength
= 0;
3159 req
->bEndRequest
= FALSE
;
3161 INTERNET_AsyncCall(&workRequest
);
3163 * This is from windows.
3165 INTERNET_SetLastError(ERROR_IO_PENDING
);
3170 ret
= HTTP_HttpSendRequestW(lpwhr
, lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
3171 lpBuffersIn
->lpvBuffer
, lpBuffersIn
->dwBufferLength
,
3172 lpBuffersIn
->dwBufferTotal
, FALSE
);
3174 ret
= HTTP_HttpSendRequestW(lpwhr
, NULL
, 0, NULL
, 0, 0, FALSE
);
3179 WININET_Release( &lpwhr
->hdr
);
3185 /***********************************************************************
3186 * HttpSendRequestW (WININET.@)
3188 * Sends the specified request to the HTTP server
3195 BOOL WINAPI
HttpSendRequestW(HINTERNET hHttpRequest
, LPCWSTR lpszHeaders
,
3196 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
3198 http_request_t
*lpwhr
;
3199 http_session_t
*lpwhs
= NULL
;
3200 appinfo_t
*hIC
= NULL
;
3203 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest
,
3204 debugstr_wn(lpszHeaders
, dwHeaderLength
), dwHeaderLength
, lpOptional
, dwOptionalLength
);
3206 lpwhr
= (http_request_t
*) WININET_GetObject( hHttpRequest
);
3207 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
3209 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
3214 lpwhs
= lpwhr
->lpHttpSession
;
3215 if (NULL
== lpwhs
|| lpwhs
->hdr
.htype
!= WH_HHTTPSESSION
)
3217 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
3222 hIC
= lpwhs
->lpAppInfo
;
3223 if (NULL
== hIC
|| hIC
->hdr
.htype
!= WH_HINIT
)
3225 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
3230 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
3232 WORKREQUEST workRequest
;
3233 struct WORKREQ_HTTPSENDREQUESTW
*req
;
3235 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
3236 workRequest
.hdr
= WININET_AddRef( &lpwhr
->hdr
);
3237 req
= &workRequest
.u
.HttpSendRequestW
;
3242 if (dwHeaderLength
== ~0u) size
= (strlenW(lpszHeaders
) + 1) * sizeof(WCHAR
);
3243 else size
= dwHeaderLength
* sizeof(WCHAR
);
3245 req
->lpszHeader
= HeapAlloc(GetProcessHeap(), 0, size
);
3246 memcpy(req
->lpszHeader
, lpszHeaders
, size
);
3249 req
->lpszHeader
= 0;
3250 req
->dwHeaderLength
= dwHeaderLength
;
3251 req
->lpOptional
= lpOptional
;
3252 req
->dwOptionalLength
= dwOptionalLength
;
3253 req
->dwContentLength
= dwOptionalLength
;
3254 req
->bEndRequest
= TRUE
;
3256 INTERNET_AsyncCall(&workRequest
);
3258 * This is from windows.
3260 INTERNET_SetLastError(ERROR_IO_PENDING
);
3265 r
= HTTP_HttpSendRequestW(lpwhr
, lpszHeaders
,
3266 dwHeaderLength
, lpOptional
, dwOptionalLength
,
3267 dwOptionalLength
, TRUE
);
3271 WININET_Release( &lpwhr
->hdr
);
3275 /***********************************************************************
3276 * HttpSendRequestA (WININET.@)
3278 * Sends the specified request to the HTTP server
3285 BOOL WINAPI
HttpSendRequestA(HINTERNET hHttpRequest
, LPCSTR lpszHeaders
,
3286 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
3289 LPWSTR szHeaders
=NULL
;
3290 DWORD nLen
=dwHeaderLength
;
3291 if(lpszHeaders
!=NULL
)
3293 nLen
=MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,NULL
,0);
3294 szHeaders
=HeapAlloc(GetProcessHeap(),0,nLen
*sizeof(WCHAR
));
3295 MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,szHeaders
,nLen
);
3297 result
=HttpSendRequestW(hHttpRequest
, szHeaders
, nLen
, lpOptional
, dwOptionalLength
);
3298 HeapFree(GetProcessHeap(),0,szHeaders
);
3302 /***********************************************************************
3303 * HTTP_GetRedirectURL (internal)
3305 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*lpwhr
, LPCWSTR lpszUrl
)
3307 static WCHAR szHttp
[] = {'h','t','t','p',0};
3308 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3309 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
3310 URL_COMPONENTSW urlComponents
;
3311 DWORD url_length
= 0;
3313 LPWSTR combined_url
;
3315 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3316 urlComponents
.lpszScheme
= (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) ? szHttps
: szHttp
;
3317 urlComponents
.dwSchemeLength
= 0;
3318 urlComponents
.lpszHostName
= lpwhs
->lpszHostName
;
3319 urlComponents
.dwHostNameLength
= 0;
3320 urlComponents
.nPort
= lpwhs
->nHostPort
;
3321 urlComponents
.lpszUserName
= lpwhs
->lpszUserName
;
3322 urlComponents
.dwUserNameLength
= 0;
3323 urlComponents
.lpszPassword
= NULL
;
3324 urlComponents
.dwPasswordLength
= 0;
3325 urlComponents
.lpszUrlPath
= lpwhr
->lpszPath
;
3326 urlComponents
.dwUrlPathLength
= 0;
3327 urlComponents
.lpszExtraInfo
= NULL
;
3328 urlComponents
.dwExtraInfoLength
= 0;
3330 if (!InternetCreateUrlW(&urlComponents
, 0, NULL
, &url_length
) &&
3331 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3334 orig_url
= HeapAlloc(GetProcessHeap(), 0, url_length
);
3336 /* convert from bytes to characters */
3337 url_length
= url_length
/ sizeof(WCHAR
) - 1;
3338 if (!InternetCreateUrlW(&urlComponents
, 0, orig_url
, &url_length
))
3340 HeapFree(GetProcessHeap(), 0, orig_url
);
3345 if (!InternetCombineUrlW(orig_url
, lpszUrl
, NULL
, &url_length
, ICU_ENCODE_SPACES_ONLY
) &&
3346 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3348 HeapFree(GetProcessHeap(), 0, orig_url
);
3351 combined_url
= HeapAlloc(GetProcessHeap(), 0, url_length
* sizeof(WCHAR
));
3353 if (!InternetCombineUrlW(orig_url
, lpszUrl
, combined_url
, &url_length
, ICU_ENCODE_SPACES_ONLY
))
3355 HeapFree(GetProcessHeap(), 0, orig_url
);
3356 HeapFree(GetProcessHeap(), 0, combined_url
);
3359 HeapFree(GetProcessHeap(), 0, orig_url
);
3360 return combined_url
;
3364 /***********************************************************************
3365 * HTTP_HandleRedirect (internal)
3367 static BOOL
HTTP_HandleRedirect(http_request_t
*lpwhr
, LPCWSTR lpszUrl
)
3369 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
3370 appinfo_t
*hIC
= lpwhs
->lpAppInfo
;
3371 BOOL using_proxy
= hIC
->lpszProxy
&& hIC
->lpszProxy
[0];
3372 WCHAR path
[INTERNET_MAX_URL_LENGTH
];
3377 /* if it's an absolute path, keep the same session info */
3378 lstrcpynW(path
, lpszUrl
, INTERNET_MAX_URL_LENGTH
);
3382 URL_COMPONENTSW urlComponents
;
3383 WCHAR protocol
[32], hostName
[MAXHOSTNAME
], userName
[1024];
3384 static WCHAR szHttp
[] = {'h','t','t','p',0};
3385 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3391 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3392 urlComponents
.lpszScheme
= protocol
;
3393 urlComponents
.dwSchemeLength
= 32;
3394 urlComponents
.lpszHostName
= hostName
;
3395 urlComponents
.dwHostNameLength
= MAXHOSTNAME
;
3396 urlComponents
.lpszUserName
= userName
;
3397 urlComponents
.dwUserNameLength
= 1024;
3398 urlComponents
.lpszPassword
= NULL
;
3399 urlComponents
.dwPasswordLength
= 0;
3400 urlComponents
.lpszUrlPath
= path
;
3401 urlComponents
.dwUrlPathLength
= 2048;
3402 urlComponents
.lpszExtraInfo
= NULL
;
3403 urlComponents
.dwExtraInfoLength
= 0;
3404 if(!InternetCrackUrlW(lpszUrl
, strlenW(lpszUrl
), 0, &urlComponents
))
3407 if (!strncmpW(szHttp
, urlComponents
.lpszScheme
, strlenW(szHttp
)) &&
3408 (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
))
3410 TRACE("redirect from secure page to non-secure page\n");
3411 /* FIXME: warn about from secure redirect to non-secure page */
3412 lpwhr
->hdr
.dwFlags
&= ~INTERNET_FLAG_SECURE
;
3414 if (!strncmpW(szHttps
, urlComponents
.lpszScheme
, strlenW(szHttps
)) &&
3415 !(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
))
3417 TRACE("redirect from non-secure page to secure page\n");
3418 /* FIXME: notify about redirect to secure page */
3419 lpwhr
->hdr
.dwFlags
|= INTERNET_FLAG_SECURE
;
3422 if (urlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
3424 if (lstrlenW(protocol
)>4) /*https*/
3425 urlComponents
.nPort
= INTERNET_DEFAULT_HTTPS_PORT
;
3427 urlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
3432 * This upsets redirects to binary files on sourceforge.net
3433 * and gives an html page instead of the target file
3434 * Examination of the HTTP request sent by native wininet.dll
3435 * reveals that it doesn't send a referrer in that case.
3436 * Maybe there's a flag that enables this, or maybe a referrer
3437 * shouldn't be added in case of a redirect.
3440 /* consider the current host as the referrer */
3441 if (lpwhs
->lpszServerName
&& *lpwhs
->lpszServerName
)
3442 HTTP_ProcessHeader(lpwhr
, HTTP_REFERER
, lpwhs
->lpszServerName
,
3443 HTTP_ADDHDR_FLAG_REQ
|HTTP_ADDREQ_FLAG_REPLACE
|
3444 HTTP_ADDHDR_FLAG_ADD_IF_NEW
);
3447 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszHostName
);
3448 if (urlComponents
.nPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
3449 urlComponents
.nPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
3452 static const WCHAR fmt
[] = {'%','s',':','%','i',0};
3453 len
= lstrlenW(hostName
);
3454 len
+= 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3455 lpwhs
->lpszHostName
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
3456 sprintfW(lpwhs
->lpszHostName
, fmt
, hostName
, urlComponents
.nPort
);
3459 lpwhs
->lpszHostName
= heap_strdupW(hostName
);
3461 HTTP_ProcessHeader(lpwhr
, hostW
, lpwhs
->lpszHostName
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDREQ_FLAG_REPLACE
| HTTP_ADDHDR_FLAG_REQ
);
3463 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszUserName
);
3464 lpwhs
->lpszUserName
= NULL
;
3466 lpwhs
->lpszUserName
= heap_strdupW(userName
);
3470 if (strcmpiW(lpwhs
->lpszServerName
, hostName
) || lpwhs
->nServerPort
!= urlComponents
.nPort
)
3472 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszServerName
);
3473 lpwhs
->lpszServerName
= heap_strdupW(hostName
);
3474 lpwhs
->nServerPort
= urlComponents
.nPort
;
3476 NETCON_close(&lpwhr
->netConnection
);
3477 if (!HTTP_ResolveName(lpwhr
)) return FALSE
;
3478 if (!NETCON_init(&lpwhr
->netConnection
, lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)) return FALSE
;
3479 lpwhr
->read_pos
= lpwhr
->read_size
= 0;
3480 lpwhr
->read_chunked
= FALSE
;
3484 TRACE("Redirect through proxy\n");
3487 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszPath
);
3488 lpwhr
->lpszPath
=NULL
;
3494 rc
= UrlEscapeW(path
, NULL
, &needed
, URL_ESCAPE_SPACES_ONLY
);
3495 if (rc
!= E_POINTER
)
3496 needed
= strlenW(path
)+1;
3497 lpwhr
->lpszPath
= HeapAlloc(GetProcessHeap(), 0, needed
*sizeof(WCHAR
));
3498 rc
= UrlEscapeW(path
, lpwhr
->lpszPath
, &needed
,
3499 URL_ESCAPE_SPACES_ONLY
);
3502 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path
),rc
);
3503 strcpyW(lpwhr
->lpszPath
,path
);
3507 /* Remove custom content-type/length headers on redirects. */
3508 index
= HTTP_GetCustomHeaderIndex(lpwhr
, szContent_Type
, 0, TRUE
);
3510 HTTP_DeleteCustomHeader(lpwhr
, index
);
3511 index
= HTTP_GetCustomHeaderIndex(lpwhr
, szContent_Length
, 0, TRUE
);
3513 HTTP_DeleteCustomHeader(lpwhr
, index
);
3518 /***********************************************************************
3519 * HTTP_build_req (internal)
3521 * concatenate all the strings in the request together
3523 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
)
3528 for( t
= list
; *t
; t
++ )
3529 len
+= strlenW( *t
);
3532 str
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
3535 for( t
= list
; *t
; t
++ )
3541 static BOOL
HTTP_SecureProxyConnect(http_request_t
*lpwhr
)
3544 LPWSTR requestString
;
3550 static const WCHAR szConnect
[] = {'C','O','N','N','E','C','T',0};
3551 static const WCHAR szFormat
[] = {'%','s',':','%','d',0};
3552 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
3556 lpszPath
= HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs
->lpszHostName
) + 13)*sizeof(WCHAR
) );
3557 sprintfW( lpszPath
, szFormat
, lpwhs
->lpszHostName
, lpwhs
->nHostPort
);
3558 requestString
= HTTP_BuildHeaderRequestString( lpwhr
, szConnect
, lpszPath
, g_szHttp1_1
);
3559 HeapFree( GetProcessHeap(), 0, lpszPath
);
3561 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3562 NULL
, 0, NULL
, NULL
);
3563 len
--; /* the nul terminator isn't needed */
3564 ascii_req
= HeapAlloc( GetProcessHeap(), 0, len
);
3565 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3566 ascii_req
, len
, NULL
, NULL
);
3567 HeapFree( GetProcessHeap(), 0, requestString
);
3569 TRACE("full request -> %s\n", debugstr_an( ascii_req
, len
) );
3571 ret
= NETCON_send( &lpwhr
->netConnection
, ascii_req
, len
, 0, &cnt
);
3572 HeapFree( GetProcessHeap(), 0, ascii_req
);
3573 if (!ret
|| cnt
< 0)
3576 responseLen
= HTTP_GetResponseHeaders( lpwhr
, TRUE
);
3583 static void HTTP_InsertCookies(http_request_t
*lpwhr
)
3585 static const WCHAR szUrlForm
[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3586 LPWSTR lpszCookies
, lpszUrl
= NULL
;
3587 DWORD nCookieSize
, size
;
3588 LPHTTPHEADERW Host
= HTTP_GetHeader(lpwhr
, hostW
);
3590 size
= (strlenW(Host
->lpszValue
) + strlenW(szUrlForm
) + strlenW(lpwhr
->lpszPath
)) * sizeof(WCHAR
);
3591 if (!(lpszUrl
= HeapAlloc(GetProcessHeap(), 0, size
))) return;
3592 sprintfW( lpszUrl
, szUrlForm
, Host
->lpszValue
, lpwhr
->lpszPath
);
3594 if (InternetGetCookieW(lpszUrl
, NULL
, NULL
, &nCookieSize
))
3597 static const WCHAR szCookie
[] = {'C','o','o','k','i','e',':',' ',0};
3599 size
= sizeof(szCookie
) + nCookieSize
* sizeof(WCHAR
) + sizeof(szCrLf
);
3600 if ((lpszCookies
= HeapAlloc(GetProcessHeap(), 0, size
)))
3602 cnt
+= sprintfW(lpszCookies
, szCookie
);
3603 InternetGetCookieW(lpszUrl
, NULL
, lpszCookies
+ cnt
, &nCookieSize
);
3604 strcatW(lpszCookies
, szCrLf
);
3606 HTTP_HttpAddRequestHeadersW(lpwhr
, lpszCookies
, strlenW(lpszCookies
), HTTP_ADDREQ_FLAG_REPLACE
);
3607 HeapFree(GetProcessHeap(), 0, lpszCookies
);
3610 HeapFree(GetProcessHeap(), 0, lpszUrl
);
3613 /***********************************************************************
3614 * HTTP_HttpSendRequestW (internal)
3616 * Sends the specified request to the HTTP server
3623 BOOL WINAPI
HTTP_HttpSendRequestW(http_request_t
*lpwhr
, LPCWSTR lpszHeaders
,
3624 DWORD dwHeaderLength
, LPVOID lpOptional
, DWORD dwOptionalLength
,
3625 DWORD dwContentLength
, BOOL bEndRequest
)
3628 BOOL bSuccess
= FALSE
, redirected
= FALSE
;
3629 LPWSTR requestString
= NULL
;
3632 INTERNET_ASYNC_RESULT iar
;
3633 static const WCHAR szPost
[] = { 'P','O','S','T',0 };
3634 static const WCHAR szContentLength
[] =
3635 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3636 WCHAR contentLengthStr
[sizeof szContentLength
/2 /* includes \r\n */ + 20 /* int */ ];
3638 TRACE("--> %p\n", lpwhr
);
3640 assert(lpwhr
->hdr
.htype
== WH_HHTTPREQ
);
3642 /* if the verb is NULL default to GET */
3643 if (!lpwhr
->lpszVerb
)
3644 lpwhr
->lpszVerb
= heap_strdupW(szGET
);
3646 if (dwContentLength
|| strcmpW(lpwhr
->lpszVerb
, szGET
))
3648 sprintfW(contentLengthStr
, szContentLength
, dwContentLength
);
3649 HTTP_HttpAddRequestHeadersW(lpwhr
, contentLengthStr
, -1L, HTTP_ADDREQ_FLAG_REPLACE
);
3650 lpwhr
->dwBytesToWrite
= dwContentLength
;
3652 if (lpwhr
->lpHttpSession
->lpAppInfo
->lpszAgent
)
3654 WCHAR
*agent_header
;
3655 static const WCHAR user_agent
[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3658 len
= strlenW(lpwhr
->lpHttpSession
->lpAppInfo
->lpszAgent
) + strlenW(user_agent
);
3659 agent_header
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
3660 sprintfW(agent_header
, user_agent
, lpwhr
->lpHttpSession
->lpAppInfo
->lpszAgent
);
3662 HTTP_HttpAddRequestHeadersW(lpwhr
, agent_header
, strlenW(agent_header
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
3663 HeapFree(GetProcessHeap(), 0, agent_header
);
3665 if (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_PRAGMA_NOCACHE
)
3667 static const WCHAR pragma_nocache
[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3668 HTTP_HttpAddRequestHeadersW(lpwhr
, pragma_nocache
, strlenW(pragma_nocache
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
3670 if ((lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_CACHE_WRITE
) && !strcmpW(lpwhr
->lpszVerb
, szPost
))
3672 static const WCHAR cache_control
[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3673 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3674 HTTP_HttpAddRequestHeadersW(lpwhr
, cache_control
, strlenW(cache_control
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
3684 /* like native, just in case the caller forgot to call InternetReadFile
3685 * for all the data */
3686 HTTP_DrainContent(lpwhr
);
3687 lpwhr
->dwContentRead
= 0;
3689 if (TRACE_ON(wininet
))
3691 LPHTTPHEADERW Host
= HTTP_GetHeader(lpwhr
, hostW
);
3692 TRACE("Going to url %s %s\n", debugstr_w(Host
->lpszValue
), debugstr_w(lpwhr
->lpszPath
));
3696 if (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_KEEP_CONNECTION
)
3698 HTTP_ProcessHeader(lpwhr
, szConnection
, szKeepAlive
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
3700 HTTP_InsertAuthorization(lpwhr
, lpwhr
->pAuthInfo
, szAuthorization
);
3701 HTTP_InsertAuthorization(lpwhr
, lpwhr
->pProxyAuthInfo
, szProxy_Authorization
);
3703 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
))
3704 HTTP_InsertCookies(lpwhr
);
3706 /* add the headers the caller supplied */
3707 if( lpszHeaders
&& dwHeaderLength
)
3709 HTTP_HttpAddRequestHeadersW(lpwhr
, lpszHeaders
, dwHeaderLength
,
3710 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REPLACE
);
3713 if (lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxy
&& lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxy
[0])
3715 WCHAR
*url
= HTTP_BuildProxyRequestUrl(lpwhr
);
3716 requestString
= HTTP_BuildHeaderRequestString(lpwhr
, lpwhr
->lpszVerb
, url
, lpwhr
->lpszVersion
);
3717 HeapFree(GetProcessHeap(), 0, url
);
3720 requestString
= HTTP_BuildHeaderRequestString(lpwhr
, lpwhr
->lpszVerb
, lpwhr
->lpszPath
, lpwhr
->lpszVersion
);
3723 TRACE("Request header -> %s\n", debugstr_w(requestString
) );
3725 /* Send the request and store the results */
3726 if (!HTTP_OpenConnection(lpwhr
))
3729 /* send the request as ASCII, tack on the optional data */
3730 if (!lpOptional
|| redirected
)
3731 dwOptionalLength
= 0;
3732 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3733 NULL
, 0, NULL
, NULL
);
3734 ascii_req
= HeapAlloc( GetProcessHeap(), 0, len
+ dwOptionalLength
);
3735 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3736 ascii_req
, len
, NULL
, NULL
);
3738 memcpy( &ascii_req
[len
-1], lpOptional
, dwOptionalLength
);
3739 len
= (len
+ dwOptionalLength
- 1);
3741 TRACE("full request -> %s\n", debugstr_a(ascii_req
) );
3743 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3744 INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
3746 NETCON_send(&lpwhr
->netConnection
, ascii_req
, len
, 0, &cnt
);
3747 HeapFree( GetProcessHeap(), 0, ascii_req
);
3749 lpwhr
->dwBytesWritten
= dwOptionalLength
;
3751 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3752 INTERNET_STATUS_REQUEST_SENT
,
3753 &len
, sizeof(DWORD
));
3760 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3761 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
3766 responseLen
= HTTP_GetResponseHeaders(lpwhr
, TRUE
);
3770 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3771 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
,
3774 HTTP_ProcessCookies(lpwhr
);
3776 if (!set_content_length( lpwhr
)) HTTP_FinishedReading(lpwhr
);
3778 dwBufferSize
= sizeof(dwStatusCode
);
3779 if (!HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_STATUS_CODE
,
3780 &dwStatusCode
,&dwBufferSize
,NULL
))
3783 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
) && bSuccess
)
3785 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
3786 dwBufferSize
=sizeof(szNewLocation
);
3787 if ((dwStatusCode
==HTTP_STATUS_REDIRECT
|| dwStatusCode
==HTTP_STATUS_MOVED
) &&
3788 HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_LOCATION
,szNewLocation
,&dwBufferSize
,NULL
))
3790 if (strcmpW(lpwhr
->lpszVerb
, szGET
) && strcmpW(lpwhr
->lpszVerb
, szHEAD
))
3792 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVerb
);
3793 lpwhr
->lpszVerb
= heap_strdupW(szGET
);
3795 HTTP_DrainContent(lpwhr
);
3796 if ((new_url
= HTTP_GetRedirectURL( lpwhr
, szNewLocation
)))
3798 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
3799 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
3800 bSuccess
= HTTP_HandleRedirect(lpwhr
, new_url
);
3803 HeapFree(GetProcessHeap(), 0, requestString
);
3806 HeapFree( GetProcessHeap(), 0, new_url
);
3811 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTH
) && bSuccess
)
3813 WCHAR szAuthValue
[2048];
3815 if (dwStatusCode
== HTTP_STATUS_DENIED
)
3818 while (HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_WWW_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
))
3820 if (HTTP_DoAuthorization(lpwhr
, szAuthValue
,
3822 lpwhr
->lpHttpSession
->lpszUserName
,
3823 lpwhr
->lpHttpSession
->lpszPassword
))
3830 if (dwStatusCode
== HTTP_STATUS_PROXY_AUTH_REQ
)
3833 while (HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_PROXY_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
))
3835 if (HTTP_DoAuthorization(lpwhr
, szAuthValue
,
3836 &lpwhr
->pProxyAuthInfo
,
3837 lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxyUsername
,
3838 lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxyPassword
))
3853 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
3854 WCHAR cacheFileName
[MAX_PATH
+1];
3857 b
= HTTP_GetRequestURL(lpwhr
, url
);
3859 WARN("Could not get URL\n");
3863 b
= CreateUrlCacheEntryW(url
, lpwhr
->dwContentLength
> 0 ? lpwhr
->dwContentLength
: 0, NULL
, cacheFileName
, 0);
3865 lpwhr
->lpszCacheFile
= heap_strdupW(cacheFileName
);
3866 lpwhr
->hCacheFile
= CreateFileW(lpwhr
->lpszCacheFile
, GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
3867 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
3868 if(lpwhr
->hCacheFile
== INVALID_HANDLE_VALUE
) {
3869 WARN("Could not create file: %u\n", GetLastError());
3870 lpwhr
->hCacheFile
= NULL
;
3873 WARN("Could not create cache entry: %08x\n", GetLastError());
3879 HeapFree(GetProcessHeap(), 0, requestString
);
3881 /* TODO: send notification for P3P header */
3883 if (lpwhr
->lpHttpSession
->lpAppInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
3887 if (lpwhr
->dwBytesWritten
== lpwhr
->dwBytesToWrite
) HTTP_ReceiveRequestData(lpwhr
, TRUE
);
3890 iar
.dwResult
= (DWORD_PTR
)lpwhr
->hdr
.hInternet
;
3893 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3894 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
3895 sizeof(INTERNET_ASYNC_RESULT
));
3900 iar
.dwResult
= (DWORD_PTR
)lpwhr
->hdr
.hInternet
;
3901 iar
.dwError
= INTERNET_GetLastError();
3903 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3904 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
3905 sizeof(INTERNET_ASYNC_RESULT
));
3910 if (bSuccess
) INTERNET_SetLastError(ERROR_SUCCESS
);
3914 /***********************************************************************
3915 * HTTPSESSION_Destroy (internal)
3917 * Deallocate session handle
3920 static void HTTPSESSION_Destroy(object_header_t
*hdr
)
3922 http_session_t
*lpwhs
= (http_session_t
*) hdr
;
3924 TRACE("%p\n", lpwhs
);
3926 WININET_Release(&lpwhs
->lpAppInfo
->hdr
);
3928 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszHostName
);
3929 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszServerName
);
3930 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszPassword
);
3931 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszUserName
);
3932 HeapFree(GetProcessHeap(), 0, lpwhs
);
3935 static DWORD
HTTPSESSION_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
3938 case INTERNET_OPTION_HANDLE_TYPE
:
3939 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3941 if (*size
< sizeof(ULONG
))
3942 return ERROR_INSUFFICIENT_BUFFER
;
3944 *size
= sizeof(DWORD
);
3945 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_CONNECT_HTTP
;
3946 return ERROR_SUCCESS
;
3949 return INET_QueryOption(option
, buffer
, size
, unicode
);
3952 static DWORD
HTTPSESSION_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
3954 http_session_t
*ses
= (http_session_t
*)hdr
;
3957 case INTERNET_OPTION_USERNAME
:
3959 HeapFree(GetProcessHeap(), 0, ses
->lpszUserName
);
3960 if (!(ses
->lpszUserName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
3961 return ERROR_SUCCESS
;
3963 case INTERNET_OPTION_PASSWORD
:
3965 HeapFree(GetProcessHeap(), 0, ses
->lpszPassword
);
3966 if (!(ses
->lpszPassword
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
3967 return ERROR_SUCCESS
;
3972 return ERROR_INTERNET_INVALID_OPTION
;
3975 static const object_vtbl_t HTTPSESSIONVtbl
= {
3976 HTTPSESSION_Destroy
,
3978 HTTPSESSION_QueryOption
,
3979 HTTPSESSION_SetOption
,
3988 /***********************************************************************
3989 * HTTP_Connect (internal)
3991 * Create http session handle
3994 * HINTERNET a session handle on success
3998 HINTERNET
HTTP_Connect(appinfo_t
*hIC
, LPCWSTR lpszServerName
,
3999 INTERNET_PORT nServerPort
, LPCWSTR lpszUserName
,
4000 LPCWSTR lpszPassword
, DWORD dwFlags
, DWORD_PTR dwContext
,
4001 DWORD dwInternalFlags
)
4003 http_session_t
*lpwhs
= NULL
;
4004 HINTERNET handle
= NULL
;
4008 if (!lpszServerName
|| !lpszServerName
[0])
4010 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
4014 assert( hIC
->hdr
.htype
== WH_HINIT
);
4016 lpwhs
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(http_session_t
));
4019 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
4024 * According to my tests. The name is not resolved until a request is sent
4027 lpwhs
->hdr
.htype
= WH_HHTTPSESSION
;
4028 lpwhs
->hdr
.vtbl
= &HTTPSESSIONVtbl
;
4029 lpwhs
->hdr
.dwFlags
= dwFlags
;
4030 lpwhs
->hdr
.dwContext
= dwContext
;
4031 lpwhs
->hdr
.dwInternalFlags
= dwInternalFlags
| (hIC
->hdr
.dwInternalFlags
& INET_CALLBACKW
);
4032 lpwhs
->hdr
.refs
= 1;
4033 lpwhs
->hdr
.lpfnStatusCB
= hIC
->hdr
.lpfnStatusCB
;
4035 WININET_AddRef( &hIC
->hdr
);
4036 lpwhs
->lpAppInfo
= hIC
;
4037 list_add_head( &hIC
->hdr
.children
, &lpwhs
->hdr
.entry
);
4039 handle
= WININET_AllocHandle( &lpwhs
->hdr
);
4042 ERR("Failed to alloc handle\n");
4043 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
4047 if(hIC
->lpszProxy
&& hIC
->dwAccessType
== INTERNET_OPEN_TYPE_PROXY
) {
4048 if(strchrW(hIC
->lpszProxy
, ' '))
4049 FIXME("Several proxies not implemented.\n");
4050 if(hIC
->lpszProxyBypass
)
4051 FIXME("Proxy bypass is ignored.\n");
4053 if (lpszServerName
&& lpszServerName
[0])
4055 lpwhs
->lpszServerName
= heap_strdupW(lpszServerName
);
4056 lpwhs
->lpszHostName
= heap_strdupW(lpszServerName
);
4058 if (lpszUserName
&& lpszUserName
[0])
4059 lpwhs
->lpszUserName
= heap_strdupW(lpszUserName
);
4060 if (lpszPassword
&& lpszPassword
[0])
4061 lpwhs
->lpszPassword
= heap_strdupW(lpszPassword
);
4062 lpwhs
->nServerPort
= nServerPort
;
4063 lpwhs
->nHostPort
= nServerPort
;
4065 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4066 if (!(lpwhs
->hdr
.dwInternalFlags
& INET_OPENURL
))
4068 INTERNET_SendCallback(&hIC
->hdr
, dwContext
,
4069 INTERNET_STATUS_HANDLE_CREATED
, &handle
,
4075 WININET_Release( &lpwhs
->hdr
);
4078 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4082 TRACE("%p --> %p (%p)\n", hIC
, handle
, lpwhs
);
4087 /***********************************************************************
4088 * HTTP_OpenConnection (internal)
4090 * Connect to a web server
4097 static BOOL
HTTP_OpenConnection(http_request_t
*lpwhr
)
4099 BOOL bSuccess
= FALSE
;
4100 http_session_t
*lpwhs
;
4101 appinfo_t
*hIC
= NULL
;
4102 char szaddr
[INET6_ADDRSTRLEN
];
4108 if (lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
4110 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
4114 if (NETCON_connected(&lpwhr
->netConnection
))
4119 if (!HTTP_ResolveName(lpwhr
)) goto lend
;
4121 lpwhs
= lpwhr
->lpHttpSession
;
4123 hIC
= lpwhs
->lpAppInfo
;
4124 switch (lpwhs
->socketAddress
.ss_family
)
4127 addr
= &((struct sockaddr_in
*)&lpwhs
->socketAddress
)->sin_addr
;
4130 addr
= &((struct sockaddr_in6
*)&lpwhs
->socketAddress
)->sin6_addr
;
4133 WARN("unsupported family %d\n", lpwhs
->socketAddress
.ss_family
);
4134 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED
);
4137 inet_ntop(lpwhs
->socketAddress
.ss_family
, addr
, szaddr
, sizeof(szaddr
));
4138 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
4139 INTERNET_STATUS_CONNECTING_TO_SERVER
,
4143 if (!NETCON_create(&lpwhr
->netConnection
, lpwhs
->socketAddress
.ss_family
,
4146 WARN("Socket creation failed: %u\n", INTERNET_GetLastError());
4150 if (!NETCON_connect(&lpwhr
->netConnection
, (struct sockaddr
*)&lpwhs
->socketAddress
,
4154 if (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
4156 /* Note: we differ from Microsoft's WinINet here. they seem to have
4157 * a bug that causes no status callbacks to be sent when starting
4158 * a tunnel to a proxy server using the CONNECT verb. i believe our
4159 * behaviour to be more correct and to not cause any incompatibilities
4160 * because using a secure connection through a proxy server is a rare
4161 * case that would be hard for anyone to depend on */
4162 if (hIC
->lpszProxy
&& !HTTP_SecureProxyConnect(lpwhr
))
4165 if (!NETCON_secure_connect(&lpwhr
->netConnection
, lpwhs
->lpszHostName
))
4167 WARN("Couldn't connect securely to host\n");
4172 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
4173 INTERNET_STATUS_CONNECTED_TO_SERVER
,
4174 szaddr
, strlen(szaddr
)+1);
4179 lpwhr
->read_pos
= lpwhr
->read_size
= 0;
4180 lpwhr
->read_chunked
= FALSE
;
4182 TRACE("%d <--\n", bSuccess
);
4187 /***********************************************************************
4188 * HTTP_clear_response_headers (internal)
4190 * clear out any old response headers
4192 static void HTTP_clear_response_headers( http_request_t
*lpwhr
)
4196 for( i
=0; i
<lpwhr
->nCustHeaders
; i
++)
4198 if( !lpwhr
->pCustHeaders
[i
].lpszField
)
4200 if( !lpwhr
->pCustHeaders
[i
].lpszValue
)
4202 if ( lpwhr
->pCustHeaders
[i
].wFlags
& HDR_ISREQUEST
)
4204 HTTP_DeleteCustomHeader( lpwhr
, i
);
4209 /***********************************************************************
4210 * HTTP_GetResponseHeaders (internal)
4212 * Read server response
4219 static INT
HTTP_GetResponseHeaders(http_request_t
*lpwhr
, BOOL clear
)
4222 WCHAR buffer
[MAX_REPLY_LEN
];
4223 DWORD buflen
= MAX_REPLY_LEN
;
4224 BOOL bSuccess
= FALSE
;
4226 char bufferA
[MAX_REPLY_LEN
];
4227 LPWSTR status_code
= NULL
, status_text
= NULL
;
4228 DWORD cchMaxRawHeaders
= 1024;
4229 LPWSTR lpszRawHeaders
= HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
4231 DWORD cchRawHeaders
= 0;
4232 BOOL codeHundred
= FALSE
;
4236 /* clear old response headers (eg. from a redirect response) */
4237 if (clear
) HTTP_clear_response_headers( lpwhr
);
4239 if (!NETCON_connected(&lpwhr
->netConnection
))
4243 static const WCHAR szHundred
[] = {'1','0','0',0};
4245 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4247 buflen
= MAX_REPLY_LEN
;
4248 if (!read_line(lpwhr
, bufferA
, &buflen
))
4251 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
4252 /* check is this a status code line? */
4253 if (!strncmpW(buffer
, g_szHttp1_0
, 4))
4255 /* split the version from the status code */
4256 status_code
= strchrW( buffer
, ' ' );
4261 /* split the status code from the status text */
4262 status_text
= strchrW( status_code
, ' ' );
4267 TRACE("version [%s] status code [%s] status text [%s]\n",
4268 debugstr_w(buffer
), debugstr_w(status_code
), debugstr_w(status_text
) );
4270 codeHundred
= (!strcmpW(status_code
, szHundred
));
4272 else if (!codeHundred
)
4274 FIXME("Non status line at head of response (%s)\n",debugstr_w(buffer
));
4277 } while (codeHundred
);
4279 /* Add status code */
4280 HTTP_ProcessHeader(lpwhr
, szStatus
, status_code
,
4281 HTTP_ADDHDR_FLAG_REPLACE
);
4283 HeapFree(GetProcessHeap(),0,lpwhr
->lpszVersion
);
4284 HeapFree(GetProcessHeap(),0,lpwhr
->lpszStatusText
);
4286 lpwhr
->lpszVersion
= heap_strdupW(buffer
);
4287 lpwhr
->lpszStatusText
= heap_strdupW(status_text
);
4289 /* Restore the spaces */
4290 *(status_code
-1) = ' ';
4291 *(status_text
-1) = ' ';
4293 /* regenerate raw headers */
4294 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
4295 cchMaxRawHeaders
*= 2;
4296 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
4297 if (temp
== NULL
) goto lend
;
4298 lpszRawHeaders
= temp
;
4299 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
4300 cchRawHeaders
+= (buflen
-1);
4301 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
4302 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
4303 lpszRawHeaders
[cchRawHeaders
] = '\0';
4305 /* Parse each response line */
4308 buflen
= MAX_REPLY_LEN
;
4309 if (read_line(lpwhr
, bufferA
, &buflen
))
4311 LPWSTR
* pFieldAndValue
;
4313 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA
));
4315 if (!bufferA
[0]) break;
4316 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
4318 pFieldAndValue
= HTTP_InterpretHttpHeader(buffer
);
4321 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
4322 cchMaxRawHeaders
*= 2;
4323 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
4324 if (temp
== NULL
) goto lend
;
4325 lpszRawHeaders
= temp
;
4326 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
4327 cchRawHeaders
+= (buflen
-1);
4328 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
4329 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
4330 lpszRawHeaders
[cchRawHeaders
] = '\0';
4332 HTTP_ProcessHeader(lpwhr
, pFieldAndValue
[0], pFieldAndValue
[1],
4333 HTTP_ADDREQ_FLAG_ADD
);
4335 HTTP_FreeTokens(pFieldAndValue
);
4346 /* make sure the response header is terminated with an empty line. Some apps really
4347 truly care about that empty line being there for some reason. Just add it to the
4349 if (cchRawHeaders
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
4351 cchMaxRawHeaders
= cchRawHeaders
+ strlenW(szCrLf
);
4352 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
4353 if (temp
== NULL
) goto lend
;
4354 lpszRawHeaders
= temp
;
4357 memcpy(&lpszRawHeaders
[cchRawHeaders
], szCrLf
, sizeof(szCrLf
));
4359 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszRawHeaders
);
4360 lpwhr
->lpszRawHeaders
= lpszRawHeaders
;
4361 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders
));
4371 HeapFree(GetProcessHeap(), 0, lpszRawHeaders
);
4377 static void strip_spaces(LPWSTR start
)
4382 while (*str
== ' ' && *str
!= '\0')
4386 memmove(start
, str
, sizeof(WCHAR
) * (strlenW(str
) + 1));
4388 end
= start
+ strlenW(start
) - 1;
4389 while (end
>= start
&& *end
== ' ')
4397 /***********************************************************************
4398 * HTTP_InterpretHttpHeader (internal)
4400 * Parse server response
4404 * Pointer to array of field, value, NULL on success.
4407 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
)
4409 LPWSTR
* pTokenPair
;
4413 pTokenPair
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*pTokenPair
)*3);
4415 pszColon
= strchrW(buffer
, ':');
4416 /* must have two tokens */
4419 HTTP_FreeTokens(pTokenPair
);
4421 TRACE("No ':' in line: %s\n", debugstr_w(buffer
));
4425 pTokenPair
[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon
- buffer
+ 1) * sizeof(WCHAR
));
4428 HTTP_FreeTokens(pTokenPair
);
4431 memcpy(pTokenPair
[0], buffer
, (pszColon
- buffer
) * sizeof(WCHAR
));
4432 pTokenPair
[0][pszColon
- buffer
] = '\0';
4436 len
= strlenW(pszColon
);
4437 pTokenPair
[1] = HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
));
4440 HTTP_FreeTokens(pTokenPair
);
4443 memcpy(pTokenPair
[1], pszColon
, (len
+ 1) * sizeof(WCHAR
));
4445 strip_spaces(pTokenPair
[0]);
4446 strip_spaces(pTokenPair
[1]);
4448 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair
[0]), debugstr_w(pTokenPair
[1]));
4452 /***********************************************************************
4453 * HTTP_ProcessHeader (internal)
4455 * Stuff header into header tables according to <dwModifier>
4459 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4461 static BOOL
HTTP_ProcessHeader(http_request_t
*lpwhr
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
)
4463 LPHTTPHEADERW lphttpHdr
= NULL
;
4464 BOOL bSuccess
= FALSE
;
4466 BOOL request_only
= dwModifier
& HTTP_ADDHDR_FLAG_REQ
;
4468 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field
), debugstr_w(value
), dwModifier
);
4470 /* REPLACE wins out over ADD */
4471 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
4472 dwModifier
&= ~HTTP_ADDHDR_FLAG_ADD
;
4474 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD
)
4477 index
= HTTP_GetCustomHeaderIndex(lpwhr
, field
, 0, request_only
);
4481 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD_IF_NEW
)
4485 lphttpHdr
= &lpwhr
->pCustHeaders
[index
];
4491 hdr
.lpszField
= (LPWSTR
)field
;
4492 hdr
.lpszValue
= (LPWSTR
)value
;
4493 hdr
.wFlags
= hdr
.wCount
= 0;
4495 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
4496 hdr
.wFlags
|= HDR_ISREQUEST
;
4498 return HTTP_InsertCustomHeader(lpwhr
, &hdr
);
4500 /* no value to delete */
4503 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
4504 lphttpHdr
->wFlags
|= HDR_ISREQUEST
;
4506 lphttpHdr
->wFlags
&= ~HDR_ISREQUEST
;
4508 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
4510 HTTP_DeleteCustomHeader( lpwhr
, index
);
4516 hdr
.lpszField
= (LPWSTR
)field
;
4517 hdr
.lpszValue
= (LPWSTR
)value
;
4518 hdr
.wFlags
= hdr
.wCount
= 0;
4520 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
4521 hdr
.wFlags
|= HDR_ISREQUEST
;
4523 return HTTP_InsertCustomHeader(lpwhr
, &hdr
);
4528 else if (dwModifier
& COALESCEFLAGS
)
4533 INT origlen
= strlenW(lphttpHdr
->lpszValue
);
4534 INT valuelen
= strlenW(value
);
4536 if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
)
4539 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
4541 else if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON
)
4544 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
4547 len
= origlen
+ valuelen
+ ((ch
> 0) ? 2 : 0);
4549 lpsztmp
= HeapReAlloc(GetProcessHeap(), 0, lphttpHdr
->lpszValue
, (len
+1)*sizeof(WCHAR
));
4552 lphttpHdr
->lpszValue
= lpsztmp
;
4553 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4556 lphttpHdr
->lpszValue
[origlen
] = ch
;
4558 lphttpHdr
->lpszValue
[origlen
] = ' ';
4562 memcpy(&lphttpHdr
->lpszValue
[origlen
], value
, valuelen
*sizeof(WCHAR
));
4563 lphttpHdr
->lpszValue
[len
] = '\0';
4568 WARN("HeapReAlloc (%d bytes) failed\n",len
+1);
4569 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
4572 TRACE("<-- %d\n",bSuccess
);
4577 /***********************************************************************
4578 * HTTP_FinishedReading (internal)
4580 * Called when all content from server has been read by client.
4583 static BOOL
HTTP_FinishedReading(http_request_t
*lpwhr
)
4585 WCHAR szVersion
[10];
4586 WCHAR szConnectionResponse
[20];
4587 DWORD dwBufferSize
= sizeof(szVersion
);
4588 BOOL keepalive
= FALSE
;
4592 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
4593 * the connection is keep-alive by default */
4594 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_VERSION
, szVersion
,
4595 &dwBufferSize
, NULL
) &&
4596 !strcmpiW(szVersion
, g_szHttp1_1
))
4601 dwBufferSize
= sizeof(szConnectionResponse
);
4602 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_PROXY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) ||
4603 HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
))
4605 keepalive
= !strcmpiW(szConnectionResponse
, szKeepAlive
);
4610 HTTPREQ_CloseConnection(&lpwhr
->hdr
);
4613 /* FIXME: store data in the URL cache here */
4619 /***********************************************************************
4620 * HTTP_GetCustomHeaderIndex (internal)
4622 * Return index of custom header from header array
4625 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*lpwhr
, LPCWSTR lpszField
,
4626 int requested_index
, BOOL request_only
)
4630 TRACE("%s\n", debugstr_w(lpszField
));
4632 for (index
= 0; index
< lpwhr
->nCustHeaders
; index
++)
4634 if (strcmpiW(lpwhr
->pCustHeaders
[index
].lpszField
, lpszField
))
4637 if (request_only
&& !(lpwhr
->pCustHeaders
[index
].wFlags
& HDR_ISREQUEST
))
4640 if (!request_only
&& (lpwhr
->pCustHeaders
[index
].wFlags
& HDR_ISREQUEST
))
4643 if (requested_index
== 0)
4648 if (index
>= lpwhr
->nCustHeaders
)
4651 TRACE("Return: %d\n", index
);
4656 /***********************************************************************
4657 * HTTP_InsertCustomHeader (internal)
4659 * Insert header into array
4662 static BOOL
HTTP_InsertCustomHeader(http_request_t
*lpwhr
, LPHTTPHEADERW lpHdr
)
4665 LPHTTPHEADERW lph
= NULL
;
4668 TRACE("--> %s: %s\n", debugstr_w(lpHdr
->lpszField
), debugstr_w(lpHdr
->lpszValue
));
4669 count
= lpwhr
->nCustHeaders
+ 1;
4671 lph
= HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, lpwhr
->pCustHeaders
, sizeof(HTTPHEADERW
) * count
);
4673 lph
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(HTTPHEADERW
) * count
);
4677 lpwhr
->pCustHeaders
= lph
;
4678 lpwhr
->pCustHeaders
[count
-1].lpszField
= heap_strdupW(lpHdr
->lpszField
);
4679 lpwhr
->pCustHeaders
[count
-1].lpszValue
= heap_strdupW(lpHdr
->lpszValue
);
4680 lpwhr
->pCustHeaders
[count
-1].wFlags
= lpHdr
->wFlags
;
4681 lpwhr
->pCustHeaders
[count
-1].wCount
= lpHdr
->wCount
;
4682 lpwhr
->nCustHeaders
++;
4687 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
4694 /***********************************************************************
4695 * HTTP_DeleteCustomHeader (internal)
4697 * Delete header from array
4698 * If this function is called, the indexs may change.
4700 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*lpwhr
, DWORD index
)
4702 if( lpwhr
->nCustHeaders
<= 0 )
4704 if( index
>= lpwhr
->nCustHeaders
)
4706 lpwhr
->nCustHeaders
--;
4708 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[index
].lpszField
);
4709 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[index
].lpszValue
);
4711 memmove( &lpwhr
->pCustHeaders
[index
], &lpwhr
->pCustHeaders
[index
+1],
4712 (lpwhr
->nCustHeaders
- index
)* sizeof(HTTPHEADERW
) );
4713 memset( &lpwhr
->pCustHeaders
[lpwhr
->nCustHeaders
], 0, sizeof(HTTPHEADERW
) );
4719 /***********************************************************************
4720 * HTTP_VerifyValidHeader (internal)
4722 * Verify the given header is not invalid for the given http request
4725 static BOOL
HTTP_VerifyValidHeader(http_request_t
*lpwhr
, LPCWSTR field
)
4727 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4728 if (!strcmpW(lpwhr
->lpszVersion
, g_szHttp1_0
) && !strcmpiW(field
, szAccept_Encoding
))
4734 /***********************************************************************
4735 * IsHostInProxyBypassList (@)
4740 BOOL WINAPI
IsHostInProxyBypassList(DWORD flags
, LPCSTR szHost
, DWORD length
)
4742 FIXME("STUB: flags=%d host=%s length=%d\n",flags
,szHost
,length
);