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 #include "inet_ntop.c"
74 WINE_DEFAULT_DEBUG_CHANNEL(wininet
);
76 static const WCHAR g_szHttp1_0
[] = {'H','T','T','P','/','1','.','0',0};
77 static const WCHAR g_szHttp1_1
[] = {'H','T','T','P','/','1','.','1',0};
78 static const WCHAR szOK
[] = {'O','K',0};
79 static const WCHAR szDefaultHeader
[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
80 static const WCHAR hostW
[] = { 'H','o','s','t',0 };
81 static const WCHAR szAuthorization
[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
82 static const WCHAR szProxy_Authorization
[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
83 static const WCHAR szStatus
[] = { 'S','t','a','t','u','s',0 };
84 static const WCHAR szKeepAlive
[] = {'K','e','e','p','-','A','l','i','v','e',0};
85 static const WCHAR szGET
[] = { 'G','E','T', 0 };
86 static const WCHAR szHEAD
[] = { 'H','E','A','D', 0 };
87 static const WCHAR szCrLf
[] = {'\r','\n', 0};
89 static const WCHAR szAccept
[] = { 'A','c','c','e','p','t',0 };
90 static const WCHAR szAccept_Charset
[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
91 static const WCHAR szAccept_Encoding
[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
92 static const WCHAR szAccept_Language
[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
93 static const WCHAR szAccept_Ranges
[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
94 static const WCHAR szAge
[] = { 'A','g','e',0 };
95 static const WCHAR szAllow
[] = { 'A','l','l','o','w',0 };
96 static const WCHAR szCache_Control
[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
97 static const WCHAR szConnection
[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
98 static const WCHAR szContent_Base
[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
99 static const WCHAR szContent_Encoding
[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
100 static const WCHAR szContent_ID
[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
101 static const WCHAR szContent_Language
[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
102 static const WCHAR szContent_Length
[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
103 static const WCHAR szContent_Location
[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
104 static const WCHAR szContent_MD5
[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
105 static const WCHAR szContent_Range
[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
106 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 };
107 static const WCHAR szContent_Type
[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
108 static const WCHAR szCookie
[] = { 'C','o','o','k','i','e',0 };
109 static const WCHAR szDate
[] = { 'D','a','t','e',0 };
110 static const WCHAR szFrom
[] = { 'F','r','o','m',0 };
111 static const WCHAR szETag
[] = { 'E','T','a','g',0 };
112 static const WCHAR szExpect
[] = { 'E','x','p','e','c','t',0 };
113 static const WCHAR szExpires
[] = { 'E','x','p','i','r','e','s',0 };
114 static const WCHAR szIf_Match
[] = { 'I','f','-','M','a','t','c','h',0 };
115 static const WCHAR szIf_Modified_Since
[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
116 static const WCHAR szIf_None_Match
[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
117 static const WCHAR szIf_Range
[] = { 'I','f','-','R','a','n','g','e',0 };
118 static const WCHAR szIf_Unmodified_Since
[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
119 static const WCHAR szLast_Modified
[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
120 static const WCHAR szLocation
[] = { 'L','o','c','a','t','i','o','n',0 };
121 static const WCHAR szMax_Forwards
[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
122 static const WCHAR szMime_Version
[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
123 static const WCHAR szPragma
[] = { 'P','r','a','g','m','a',0 };
124 static const WCHAR szProxy_Authenticate
[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
125 static const WCHAR szProxy_Connection
[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
126 static const WCHAR szPublic
[] = { 'P','u','b','l','i','c',0 };
127 static const WCHAR szRange
[] = { 'R','a','n','g','e',0 };
128 static const WCHAR szReferer
[] = { 'R','e','f','e','r','e','r',0 };
129 static const WCHAR szRetry_After
[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
130 static const WCHAR szServer
[] = { 'S','e','r','v','e','r',0 };
131 static const WCHAR szSet_Cookie
[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
132 static const WCHAR szTransfer_Encoding
[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
133 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 };
134 static const WCHAR szUpgrade
[] = { 'U','p','g','r','a','d','e',0 };
135 static const WCHAR szURI
[] = { 'U','R','I',0 };
136 static const WCHAR szUser_Agent
[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
137 static const WCHAR szVary
[] = { 'V','a','r','y',0 };
138 static const WCHAR szVia
[] = { 'V','i','a',0 };
139 static const WCHAR szWarning
[] = { 'W','a','r','n','i','n','g',0 };
140 static const WCHAR szWWW_Authenticate
[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
142 #define MAXHOSTNAME 100
143 #define MAX_FIELD_VALUE_LEN 256
144 #define MAX_FIELD_LEN 256
146 #define HTTP_REFERER szReferer
147 #define HTTP_ACCEPT szAccept
148 #define HTTP_USERAGENT szUser_Agent
150 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
151 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
152 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
153 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
154 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
155 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
156 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
158 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
169 unsigned int auth_data_len
;
170 BOOL finished
; /* finished authenticating */
174 struct gzip_stream_t
{
184 typedef struct _basicAuthorizationData
190 LPSTR lpszAuthorization
;
191 UINT AuthorizationLen
;
192 } basicAuthorizationData
;
194 typedef struct _authorizationData
208 static struct list basicAuthorizationCache
= LIST_INIT(basicAuthorizationCache
);
209 static struct list authorizationCache
= LIST_INIT(authorizationCache
);
211 static CRITICAL_SECTION authcache_cs
;
212 static CRITICAL_SECTION_DEBUG critsect_debug
=
215 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
216 0, 0, { (DWORD_PTR
)(__FILE__
": authcache_cs") }
218 static CRITICAL_SECTION authcache_cs
= { &critsect_debug
, -1, 0, 0, 0, 0 };
220 static DWORD
HTTP_OpenConnection(http_request_t
*req
);
221 static BOOL
HTTP_GetResponseHeaders(http_request_t
*req
, BOOL clear
);
222 static DWORD
HTTP_ProcessHeader(http_request_t
*req
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
);
223 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
);
224 static DWORD
HTTP_InsertCustomHeader(http_request_t
*req
, LPHTTPHEADERW lpHdr
);
225 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*req
, LPCWSTR lpszField
, INT index
, BOOL Request
);
226 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*req
, DWORD index
);
227 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
);
228 static DWORD
HTTP_HttpQueryInfoW(http_request_t
*, DWORD
, LPVOID
, LPDWORD
, LPDWORD
);
229 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*req
, LPCWSTR lpszUrl
);
230 static UINT
HTTP_DecodeBase64(LPCWSTR base64
, LPSTR bin
);
231 static BOOL
HTTP_VerifyValidHeader(http_request_t
*req
, LPCWSTR field
);
232 static void HTTP_DrainContent(http_request_t
*req
);
233 static BOOL
HTTP_FinishedReading(http_request_t
*req
);
235 static LPHTTPHEADERW
HTTP_GetHeader(http_request_t
*req
, LPCWSTR head
)
238 HeaderIndex
= HTTP_GetCustomHeaderIndex(req
, head
, 0, TRUE
);
239 if (HeaderIndex
== -1)
242 return &req
->pCustHeaders
[HeaderIndex
];
247 static voidpf
wininet_zalloc(voidpf opaque
, uInt items
, uInt size
)
249 return HeapAlloc(GetProcessHeap(), 0, items
*size
);
252 static void wininet_zfree(voidpf opaque
, voidpf address
)
254 HeapFree(GetProcessHeap(), 0, address
);
257 static void init_gzip_stream(http_request_t
*req
)
259 gzip_stream_t
*gzip_stream
;
262 gzip_stream
= HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t
));
263 gzip_stream
->zstream
.zalloc
= wininet_zalloc
;
264 gzip_stream
->zstream
.zfree
= wininet_zfree
;
265 gzip_stream
->zstream
.opaque
= NULL
;
266 gzip_stream
->zstream
.next_in
= NULL
;
267 gzip_stream
->zstream
.avail_in
= 0;
268 gzip_stream
->zstream
.next_out
= NULL
;
269 gzip_stream
->zstream
.avail_out
= 0;
270 gzip_stream
->buf_pos
= 0;
271 gzip_stream
->buf_size
= 0;
272 gzip_stream
->end_of_data
= FALSE
;
274 zres
= inflateInit2(&gzip_stream
->zstream
, 0x1f);
276 ERR("inflateInit failed: %d\n", zres
);
277 HeapFree(GetProcessHeap(), 0, gzip_stream
);
281 req
->gzip_stream
= gzip_stream
;
283 index
= HTTP_GetCustomHeaderIndex(req
, szContent_Length
, 0, FALSE
);
285 HTTP_DeleteCustomHeader(req
, index
);
290 static void init_gzip_stream(http_request_t
*req
)
292 ERR("gzip stream not supported, missing zlib.\n");
297 /* set the request content length based on the headers */
298 static DWORD
set_content_length( http_request_t
*lpwhr
)
300 static const WCHAR szChunked
[] = {'c','h','u','n','k','e','d',0};
304 size
= sizeof(lpwhr
->dwContentLength
);
305 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_CONTENT_LENGTH
,
306 &lpwhr
->dwContentLength
, &size
, NULL
) != ERROR_SUCCESS
)
307 lpwhr
->dwContentLength
= ~0u;
309 size
= sizeof(encoding
);
310 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_TRANSFER_ENCODING
, encoding
, &size
, NULL
) == ERROR_SUCCESS
&&
311 !strcmpiW(encoding
, szChunked
))
313 lpwhr
->dwContentLength
= ~0u;
314 lpwhr
->read_chunked
= TRUE
;
317 if(lpwhr
->decoding
) {
320 static const WCHAR gzipW
[] = {'g','z','i','p',0};
322 encoding_idx
= HTTP_GetCustomHeaderIndex(lpwhr
, szContent_Encoding
, 0, FALSE
);
323 if(encoding_idx
!= -1 && !strcmpiW(lpwhr
->pCustHeaders
[encoding_idx
].lpszValue
, gzipW
))
324 init_gzip_stream(lpwhr
);
327 return lpwhr
->dwContentLength
;
330 /***********************************************************************
331 * HTTP_Tokenize (internal)
333 * Tokenize a string, allocating memory for the tokens.
335 static LPWSTR
* HTTP_Tokenize(LPCWSTR string
, LPCWSTR token_string
)
337 LPWSTR
* token_array
;
344 /* empty string has no tokens */
348 for (i
= 0; string
[i
]; i
++)
350 if (!strncmpW(string
+i
, token_string
, strlenW(token_string
)))
354 /* we want to skip over separators, but not the null terminator */
355 for (j
= 0; j
< strlenW(token_string
) - 1; j
++)
363 /* add 1 for terminating NULL */
364 token_array
= HeapAlloc(GetProcessHeap(), 0, (tokens
+1) * sizeof(*token_array
));
365 token_array
[tokens
] = NULL
;
368 for (i
= 0; i
< tokens
; i
++)
371 next_token
= strstrW(string
, token_string
);
372 if (!next_token
) next_token
= string
+strlenW(string
);
373 len
= next_token
- string
;
374 token_array
[i
] = HeapAlloc(GetProcessHeap(), 0, (len
+1)*sizeof(WCHAR
));
375 memcpy(token_array
[i
], string
, len
*sizeof(WCHAR
));
376 token_array
[i
][len
] = '\0';
377 string
= next_token
+strlenW(token_string
);
382 /***********************************************************************
383 * HTTP_FreeTokens (internal)
385 * Frees memory returned from HTTP_Tokenize.
387 static void HTTP_FreeTokens(LPWSTR
* token_array
)
390 for (i
= 0; token_array
[i
]; i
++)
391 HeapFree(GetProcessHeap(), 0, token_array
[i
]);
392 HeapFree(GetProcessHeap(), 0, token_array
);
395 static void HTTP_FixURL(http_request_t
*lpwhr
)
397 static const WCHAR szSlash
[] = { '/',0 };
398 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/', 0 };
400 /* If we don't have a path we set it to root */
401 if (NULL
== lpwhr
->lpszPath
)
402 lpwhr
->lpszPath
= heap_strdupW(szSlash
);
403 else /* remove \r and \n*/
405 int nLen
= strlenW(lpwhr
->lpszPath
);
406 while ((nLen
>0 ) && ((lpwhr
->lpszPath
[nLen
-1] == '\r')||(lpwhr
->lpszPath
[nLen
-1] == '\n')))
409 lpwhr
->lpszPath
[nLen
]='\0';
411 /* Replace '\' with '/' */
414 if (lpwhr
->lpszPath
[nLen
] == '\\') lpwhr
->lpszPath
[nLen
]='/';
418 if(CSTR_EQUAL
!= CompareStringW( LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
419 lpwhr
->lpszPath
, strlenW(lpwhr
->lpszPath
), szHttp
, strlenW(szHttp
) )
420 && lpwhr
->lpszPath
[0] != '/') /* not an absolute path ?? --> fix it !! */
422 WCHAR
*fixurl
= HeapAlloc(GetProcessHeap(), 0,
423 (strlenW(lpwhr
->lpszPath
) + 2)*sizeof(WCHAR
));
425 strcpyW(fixurl
+ 1, lpwhr
->lpszPath
);
426 HeapFree( GetProcessHeap(), 0, lpwhr
->lpszPath
);
427 lpwhr
->lpszPath
= fixurl
;
431 static LPWSTR
HTTP_BuildHeaderRequestString( http_request_t
*lpwhr
, LPCWSTR verb
, LPCWSTR path
, LPCWSTR version
)
433 LPWSTR requestString
;
439 static const WCHAR szSpace
[] = { ' ',0 };
440 static const WCHAR szColon
[] = { ':',' ',0 };
441 static const WCHAR sztwocrlf
[] = {'\r','\n','\r','\n', 0};
443 /* allocate space for an array of all the string pointers to be added */
444 len
= (lpwhr
->nCustHeaders
)*4 + 10;
445 req
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(LPCWSTR
) );
447 /* add the verb, path and HTTP version string */
455 /* Append custom request headers */
456 for (i
= 0; i
< lpwhr
->nCustHeaders
; i
++)
458 if (lpwhr
->pCustHeaders
[i
].wFlags
& HDR_ISREQUEST
)
461 req
[n
++] = lpwhr
->pCustHeaders
[i
].lpszField
;
463 req
[n
++] = lpwhr
->pCustHeaders
[i
].lpszValue
;
465 TRACE("Adding custom header %s (%s)\n",
466 debugstr_w(lpwhr
->pCustHeaders
[i
].lpszField
),
467 debugstr_w(lpwhr
->pCustHeaders
[i
].lpszValue
));
472 ERR("oops. buffer overrun\n");
475 requestString
= HTTP_build_req( req
, 4 );
476 HeapFree( GetProcessHeap(), 0, req
);
479 * Set (header) termination string for request
480 * Make sure there's exactly two new lines at the end of the request
482 p
= &requestString
[strlenW(requestString
)-1];
483 while ( (*p
== '\n') || (*p
== '\r') )
485 strcpyW( p
+1, sztwocrlf
);
487 return requestString
;
490 static void HTTP_ProcessCookies( http_request_t
*lpwhr
)
494 LPHTTPHEADERW setCookieHeader
;
496 while((HeaderIndex
= HTTP_GetCustomHeaderIndex(lpwhr
, szSet_Cookie
, numCookies
, FALSE
)) != -1)
498 setCookieHeader
= &lpwhr
->pCustHeaders
[HeaderIndex
];
500 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
) && setCookieHeader
->lpszValue
)
503 static const WCHAR szFmt
[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
507 Host
= HTTP_GetHeader(lpwhr
, hostW
);
508 len
= lstrlenW(Host
->lpszValue
) + 9 + lstrlenW(lpwhr
->lpszPath
);
509 buf_url
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
510 sprintfW(buf_url
, szFmt
, Host
->lpszValue
, lpwhr
->lpszPath
);
511 InternetSetCookieW(buf_url
, NULL
, setCookieHeader
->lpszValue
);
513 HeapFree(GetProcessHeap(), 0, buf_url
);
519 static void strip_spaces(LPWSTR start
)
524 while (*str
== ' ' && *str
!= '\0')
528 memmove(start
, str
, sizeof(WCHAR
) * (strlenW(str
) + 1));
530 end
= start
+ strlenW(start
) - 1;
531 while (end
>= start
&& *end
== ' ')
538 static inline BOOL
is_basic_auth_value( LPCWSTR pszAuthValue
, LPWSTR
*pszRealm
)
540 static const WCHAR szBasic
[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
541 static const WCHAR szRealm
[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
543 is_basic
= !strncmpiW(pszAuthValue
, szBasic
, ARRAYSIZE(szBasic
)) &&
544 ((pszAuthValue
[ARRAYSIZE(szBasic
)] == ' ') || !pszAuthValue
[ARRAYSIZE(szBasic
)]);
545 if (is_basic
&& pszRealm
)
548 LPCWSTR ptr
= &pszAuthValue
[ARRAYSIZE(szBasic
)];
552 token
= strchrW(ptr
,'=');
556 while (*realm
== ' ' && *realm
!= '\0')
558 if(!strncmpiW(realm
, szRealm
, ARRAYSIZE(szRealm
)) &&
559 (realm
[ARRAYSIZE(szRealm
)] == ' ' || realm
[ARRAYSIZE(szRealm
)] == '='))
562 while (*token
== ' ' && *token
!= '\0')
566 *pszRealm
= heap_strdupW(token
);
567 strip_spaces(*pszRealm
);
574 static void destroy_authinfo( struct HttpAuthInfo
*authinfo
)
576 if (!authinfo
) return;
578 if (SecIsValidHandle(&authinfo
->ctx
))
579 DeleteSecurityContext(&authinfo
->ctx
);
580 if (SecIsValidHandle(&authinfo
->cred
))
581 FreeCredentialsHandle(&authinfo
->cred
);
583 HeapFree(GetProcessHeap(), 0, authinfo
->auth_data
);
584 HeapFree(GetProcessHeap(), 0, authinfo
->scheme
);
585 HeapFree(GetProcessHeap(), 0, authinfo
);
588 static UINT
retrieve_cached_basic_authorization(LPWSTR host
, LPWSTR realm
, LPSTR
*auth_data
)
590 basicAuthorizationData
*ad
;
593 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host
),debugstr_w(realm
));
595 EnterCriticalSection(&authcache_cs
);
596 LIST_FOR_EACH_ENTRY(ad
, &basicAuthorizationCache
, basicAuthorizationData
, entry
)
598 if (!strcmpiW(host
,ad
->lpszwHost
) && !strcmpW(realm
,ad
->lpszwRealm
))
600 TRACE("Authorization found in cache\n");
601 *auth_data
= HeapAlloc(GetProcessHeap(),0,ad
->AuthorizationLen
);
602 memcpy(*auth_data
,ad
->lpszAuthorization
,ad
->AuthorizationLen
);
603 rc
= ad
->AuthorizationLen
;
607 LeaveCriticalSection(&authcache_cs
);
611 static void cache_basic_authorization(LPWSTR host
, LPWSTR realm
, LPSTR auth_data
, UINT auth_data_len
)
614 basicAuthorizationData
* ad
= NULL
;
616 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host
),debugstr_w(realm
),debugstr_an(auth_data
,auth_data_len
));
618 EnterCriticalSection(&authcache_cs
);
619 LIST_FOR_EACH(cursor
, &basicAuthorizationCache
)
621 basicAuthorizationData
*check
= LIST_ENTRY(cursor
,basicAuthorizationData
,entry
);
622 if (!strcmpiW(host
,check
->lpszwHost
) && !strcmpW(realm
,check
->lpszwRealm
))
631 TRACE("Found match in cache, replacing\n");
632 HeapFree(GetProcessHeap(),0,ad
->lpszAuthorization
);
633 ad
->lpszAuthorization
= HeapAlloc(GetProcessHeap(),0,auth_data_len
);
634 memcpy(ad
->lpszAuthorization
, auth_data
, auth_data_len
);
635 ad
->AuthorizationLen
= auth_data_len
;
639 ad
= HeapAlloc(GetProcessHeap(),0,sizeof(basicAuthorizationData
));
640 ad
->lpszwHost
= heap_strdupW(host
);
641 ad
->lpszwRealm
= heap_strdupW(realm
);
642 ad
->lpszAuthorization
= HeapAlloc(GetProcessHeap(),0,auth_data_len
);
643 memcpy(ad
->lpszAuthorization
, auth_data
, auth_data_len
);
644 ad
->AuthorizationLen
= auth_data_len
;
645 list_add_head(&basicAuthorizationCache
,&ad
->entry
);
646 TRACE("authorization cached\n");
648 LeaveCriticalSection(&authcache_cs
);
651 static BOOL
retrieve_cached_authorization(LPWSTR host
, LPWSTR scheme
,
652 SEC_WINNT_AUTH_IDENTITY_W
*nt_auth_identity
)
654 authorizationData
*ad
;
656 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host
), debugstr_w(scheme
));
658 EnterCriticalSection(&authcache_cs
);
659 LIST_FOR_EACH_ENTRY(ad
, &authorizationCache
, authorizationData
, entry
) {
660 if(!strcmpiW(host
, ad
->host
) && !strcmpiW(scheme
, ad
->scheme
)) {
661 TRACE("Authorization found in cache\n");
663 nt_auth_identity
->User
= heap_strdupW(ad
->user
);
664 nt_auth_identity
->Password
= heap_strdupW(ad
->password
);
665 nt_auth_identity
->Domain
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*ad
->domain_len
);
666 if(!nt_auth_identity
->User
|| !nt_auth_identity
->Password
||
667 (!nt_auth_identity
->Domain
&& ad
->domain_len
)) {
668 HeapFree(GetProcessHeap(), 0, nt_auth_identity
->User
);
669 HeapFree(GetProcessHeap(), 0, nt_auth_identity
->Password
);
670 HeapFree(GetProcessHeap(), 0, nt_auth_identity
->Domain
);
674 nt_auth_identity
->Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
675 nt_auth_identity
->UserLength
= ad
->user_len
;
676 nt_auth_identity
->PasswordLength
= ad
->password_len
;
677 memcpy(nt_auth_identity
->Domain
, ad
->domain
, sizeof(WCHAR
)*ad
->domain_len
);
678 nt_auth_identity
->DomainLength
= ad
->domain_len
;
679 LeaveCriticalSection(&authcache_cs
);
683 LeaveCriticalSection(&authcache_cs
);
688 static void cache_authorization(LPWSTR host
, LPWSTR scheme
,
689 SEC_WINNT_AUTH_IDENTITY_W
*nt_auth_identity
)
691 authorizationData
*ad
;
694 TRACE("Caching authorization for %s:%s\n", debugstr_w(host
), debugstr_w(scheme
));
696 EnterCriticalSection(&authcache_cs
);
697 LIST_FOR_EACH_ENTRY(ad
, &authorizationCache
, authorizationData
, entry
)
698 if(!strcmpiW(host
, ad
->host
) && !strcmpiW(scheme
, ad
->scheme
)) {
704 HeapFree(GetProcessHeap(), 0, ad
->user
);
705 HeapFree(GetProcessHeap(), 0, ad
->password
);
706 HeapFree(GetProcessHeap(), 0, ad
->domain
);
708 ad
= HeapAlloc(GetProcessHeap(), 0, sizeof(authorizationData
));
710 LeaveCriticalSection(&authcache_cs
);
714 ad
->host
= heap_strdupW(host
);
715 ad
->scheme
= heap_strdupW(scheme
);
716 list_add_head(&authorizationCache
, &ad
->entry
);
719 ad
->user
= heap_strndupW(nt_auth_identity
->User
, nt_auth_identity
->UserLength
);
720 ad
->password
= heap_strndupW(nt_auth_identity
->Password
, nt_auth_identity
->PasswordLength
);
721 ad
->domain
= heap_strndupW(nt_auth_identity
->Domain
, nt_auth_identity
->DomainLength
);
722 ad
->user_len
= nt_auth_identity
->UserLength
;
723 ad
->password_len
= nt_auth_identity
->PasswordLength
;
724 ad
->domain_len
= nt_auth_identity
->DomainLength
;
726 if(!ad
->host
|| !ad
->scheme
|| !ad
->user
|| !ad
->password
727 || (nt_auth_identity
->Domain
&& !ad
->domain
)) {
728 HeapFree(GetProcessHeap(), 0, ad
->host
);
729 HeapFree(GetProcessHeap(), 0, ad
->scheme
);
730 HeapFree(GetProcessHeap(), 0, ad
->user
);
731 HeapFree(GetProcessHeap(), 0, ad
->password
);
732 HeapFree(GetProcessHeap(), 0, ad
->domain
);
733 list_remove(&ad
->entry
);
734 HeapFree(GetProcessHeap(), 0, ad
);
737 LeaveCriticalSection(&authcache_cs
);
740 static BOOL
HTTP_DoAuthorization( http_request_t
*lpwhr
, LPCWSTR pszAuthValue
,
741 struct HttpAuthInfo
**ppAuthInfo
,
742 LPWSTR domain_and_username
, LPWSTR password
,
745 SECURITY_STATUS sec_status
;
746 struct HttpAuthInfo
*pAuthInfo
= *ppAuthInfo
;
748 LPWSTR szRealm
= NULL
;
750 TRACE("%s\n", debugstr_w(pszAuthValue
));
757 pAuthInfo
= HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo
));
761 SecInvalidateHandle(&pAuthInfo
->cred
);
762 SecInvalidateHandle(&pAuthInfo
->ctx
);
763 memset(&pAuthInfo
->exp
, 0, sizeof(pAuthInfo
->exp
));
765 pAuthInfo
->auth_data
= NULL
;
766 pAuthInfo
->auth_data_len
= 0;
767 pAuthInfo
->finished
= FALSE
;
769 if (is_basic_auth_value(pszAuthValue
,NULL
))
771 static const WCHAR szBasic
[] = {'B','a','s','i','c',0};
772 pAuthInfo
->scheme
= heap_strdupW(szBasic
);
773 if (!pAuthInfo
->scheme
)
775 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
782 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity
;
784 pAuthInfo
->scheme
= heap_strdupW(pszAuthValue
);
785 if (!pAuthInfo
->scheme
)
787 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
791 if (domain_and_username
)
793 WCHAR
*user
= strchrW(domain_and_username
, '\\');
794 WCHAR
*domain
= domain_and_username
;
796 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
798 pAuthData
= &nt_auth_identity
;
803 user
= domain_and_username
;
807 nt_auth_identity
.Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
808 nt_auth_identity
.User
= user
;
809 nt_auth_identity
.UserLength
= strlenW(nt_auth_identity
.User
);
810 nt_auth_identity
.Domain
= domain
;
811 nt_auth_identity
.DomainLength
= domain
? user
- domain
- 1 : 0;
812 nt_auth_identity
.Password
= password
;
813 nt_auth_identity
.PasswordLength
= strlenW(nt_auth_identity
.Password
);
815 cache_authorization(host
, pAuthInfo
->scheme
, &nt_auth_identity
);
817 else if(retrieve_cached_authorization(host
, pAuthInfo
->scheme
, &nt_auth_identity
))
818 pAuthData
= &nt_auth_identity
;
820 /* use default credentials */
823 sec_status
= AcquireCredentialsHandleW(NULL
, pAuthInfo
->scheme
,
824 SECPKG_CRED_OUTBOUND
, NULL
,
826 NULL
, &pAuthInfo
->cred
,
829 if(pAuthData
&& !domain_and_username
) {
830 HeapFree(GetProcessHeap(), 0, nt_auth_identity
.User
);
831 HeapFree(GetProcessHeap(), 0, nt_auth_identity
.Domain
);
832 HeapFree(GetProcessHeap(), 0, nt_auth_identity
.Password
);
835 if (sec_status
== SEC_E_OK
)
837 PSecPkgInfoW sec_pkg_info
;
838 sec_status
= QuerySecurityPackageInfoW(pAuthInfo
->scheme
, &sec_pkg_info
);
839 if (sec_status
== SEC_E_OK
)
841 pAuthInfo
->max_token
= sec_pkg_info
->cbMaxToken
;
842 FreeContextBuffer(sec_pkg_info
);
845 if (sec_status
!= SEC_E_OK
)
847 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
848 debugstr_w(pAuthInfo
->scheme
), sec_status
);
849 HeapFree(GetProcessHeap(), 0, pAuthInfo
->scheme
);
850 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
854 *ppAuthInfo
= pAuthInfo
;
856 else if (pAuthInfo
->finished
)
859 if ((strlenW(pszAuthValue
) < strlenW(pAuthInfo
->scheme
)) ||
860 strncmpiW(pszAuthValue
, pAuthInfo
->scheme
, strlenW(pAuthInfo
->scheme
)))
862 ERR("authentication scheme changed from %s to %s\n",
863 debugstr_w(pAuthInfo
->scheme
), debugstr_w(pszAuthValue
));
867 if (is_basic_auth_value(pszAuthValue
,&szRealm
))
871 char *auth_data
= NULL
;
872 UINT auth_data_len
= 0;
874 TRACE("basic authentication realm %s\n",debugstr_w(szRealm
));
876 if (!domain_and_username
)
879 auth_data_len
= retrieve_cached_basic_authorization(host
, szRealm
,&auth_data
);
880 if (auth_data_len
== 0)
882 HeapFree(GetProcessHeap(),0,szRealm
);
888 userlen
= WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, lstrlenW(domain_and_username
), NULL
, 0, NULL
, NULL
);
889 passlen
= WideCharToMultiByte(CP_UTF8
, 0, password
, lstrlenW(password
), NULL
, 0, NULL
, NULL
);
891 /* length includes a nul terminator, which will be re-used for the ':' */
892 auth_data
= HeapAlloc(GetProcessHeap(), 0, userlen
+ 1 + passlen
);
895 HeapFree(GetProcessHeap(),0,szRealm
);
899 WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, -1, auth_data
, userlen
, NULL
, NULL
);
900 auth_data
[userlen
] = ':';
901 WideCharToMultiByte(CP_UTF8
, 0, password
, -1, &auth_data
[userlen
+1], passlen
, NULL
, NULL
);
902 auth_data_len
= userlen
+ 1 + passlen
;
904 cache_basic_authorization(host
, szRealm
, auth_data
, auth_data_len
);
907 pAuthInfo
->auth_data
= auth_data
;
908 pAuthInfo
->auth_data_len
= auth_data_len
;
909 pAuthInfo
->finished
= TRUE
;
910 HeapFree(GetProcessHeap(),0,szRealm
);
917 SecBufferDesc out_desc
, in_desc
;
919 unsigned char *buffer
;
920 ULONG context_req
= ISC_REQ_CONNECTION
| ISC_REQ_USE_DCE_STYLE
|
921 ISC_REQ_MUTUAL_AUTH
| ISC_REQ_DELEGATE
;
923 in
.BufferType
= SECBUFFER_TOKEN
;
927 in_desc
.ulVersion
= 0;
928 in_desc
.cBuffers
= 1;
929 in_desc
.pBuffers
= &in
;
931 pszAuthData
= pszAuthValue
+ strlenW(pAuthInfo
->scheme
);
932 if (*pszAuthData
== ' ')
935 in
.cbBuffer
= HTTP_DecodeBase64(pszAuthData
, NULL
);
936 in
.pvBuffer
= HeapAlloc(GetProcessHeap(), 0, in
.cbBuffer
);
937 HTTP_DecodeBase64(pszAuthData
, in
.pvBuffer
);
940 buffer
= HeapAlloc(GetProcessHeap(), 0, pAuthInfo
->max_token
);
942 out
.BufferType
= SECBUFFER_TOKEN
;
943 out
.cbBuffer
= pAuthInfo
->max_token
;
944 out
.pvBuffer
= buffer
;
946 out_desc
.ulVersion
= 0;
947 out_desc
.cBuffers
= 1;
948 out_desc
.pBuffers
= &out
;
950 sec_status
= InitializeSecurityContextW(first
? &pAuthInfo
->cred
: NULL
,
951 first
? NULL
: &pAuthInfo
->ctx
,
952 first
? lpwhr
->lpHttpSession
->lpszServerName
: NULL
,
953 context_req
, 0, SECURITY_NETWORK_DREP
,
954 in
.pvBuffer
? &in_desc
: NULL
,
955 0, &pAuthInfo
->ctx
, &out_desc
,
956 &pAuthInfo
->attr
, &pAuthInfo
->exp
);
957 if (sec_status
== SEC_E_OK
)
959 pAuthInfo
->finished
= TRUE
;
960 pAuthInfo
->auth_data
= out
.pvBuffer
;
961 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
962 TRACE("sending last auth packet\n");
964 else if (sec_status
== SEC_I_CONTINUE_NEEDED
)
966 pAuthInfo
->auth_data
= out
.pvBuffer
;
967 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
968 TRACE("sending next auth packet\n");
972 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status
);
973 HeapFree(GetProcessHeap(), 0, out
.pvBuffer
);
974 destroy_authinfo(pAuthInfo
);
983 /***********************************************************************
984 * HTTP_HttpAddRequestHeadersW (internal)
986 static DWORD
HTTP_HttpAddRequestHeadersW(http_request_t
*lpwhr
,
987 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
992 DWORD len
, res
= ERROR_HTTP_INVALID_HEADER
;
994 TRACE("copying header: %s\n", debugstr_wn(lpszHeader
, dwHeaderLength
));
996 if( dwHeaderLength
== ~0U )
997 len
= strlenW(lpszHeader
);
999 len
= dwHeaderLength
;
1000 buffer
= HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR
)*(len
+1) );
1001 lstrcpynW( buffer
, lpszHeader
, len
+ 1);
1007 LPWSTR
* pFieldAndValue
;
1009 lpszEnd
= lpszStart
;
1011 while (*lpszEnd
!= '\0')
1013 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
1018 if (*lpszStart
== '\0')
1021 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
1024 lpszEnd
++; /* Jump over newline */
1026 TRACE("interpreting header %s\n", debugstr_w(lpszStart
));
1027 if (*lpszStart
== '\0')
1029 /* Skip 0-length headers */
1030 lpszStart
= lpszEnd
;
1031 res
= ERROR_SUCCESS
;
1034 pFieldAndValue
= HTTP_InterpretHttpHeader(lpszStart
);
1037 res
= HTTP_VerifyValidHeader(lpwhr
, pFieldAndValue
[0]);
1038 if (res
== ERROR_SUCCESS
)
1039 res
= HTTP_ProcessHeader(lpwhr
, pFieldAndValue
[0],
1040 pFieldAndValue
[1], dwModifier
| HTTP_ADDHDR_FLAG_REQ
);
1041 HTTP_FreeTokens(pFieldAndValue
);
1044 lpszStart
= lpszEnd
;
1045 } while (res
== ERROR_SUCCESS
);
1047 HeapFree(GetProcessHeap(), 0, buffer
);
1052 /***********************************************************************
1053 * HttpAddRequestHeadersW (WININET.@)
1055 * Adds one or more HTTP header to the request handler
1058 * On Windows if dwHeaderLength includes the trailing '\0', then
1059 * HttpAddRequestHeadersW() adds it too. However this results in an
1060 * invalid Http header which is rejected by some servers so we probably
1061 * don't need to match Windows on that point.
1068 BOOL WINAPI
HttpAddRequestHeadersW(HINTERNET hHttpRequest
,
1069 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1071 http_request_t
*lpwhr
;
1072 DWORD res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
1074 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_wn(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
1079 lpwhr
= (http_request_t
*) WININET_GetObject( hHttpRequest
);
1080 if (lpwhr
&& lpwhr
->hdr
.htype
== WH_HHTTPREQ
)
1081 res
= HTTP_HttpAddRequestHeadersW( lpwhr
, lpszHeader
, dwHeaderLength
, dwModifier
);
1083 WININET_Release( &lpwhr
->hdr
);
1085 if(res
!= ERROR_SUCCESS
)
1087 return res
== ERROR_SUCCESS
;
1090 /***********************************************************************
1091 * HttpAddRequestHeadersA (WININET.@)
1093 * Adds one or more HTTP header to the request handler
1100 BOOL WINAPI
HttpAddRequestHeadersA(HINTERNET hHttpRequest
,
1101 LPCSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1107 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_an(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
1109 len
= MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, NULL
, 0 );
1110 hdr
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
1111 MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, hdr
, len
);
1112 if( dwHeaderLength
!= ~0U )
1113 dwHeaderLength
= len
;
1115 r
= HttpAddRequestHeadersW( hHttpRequest
, hdr
, dwHeaderLength
, dwModifier
);
1117 HeapFree( GetProcessHeap(), 0, hdr
);
1122 /***********************************************************************
1123 * HttpOpenRequestA (WININET.@)
1125 * Open a HTTP request handle
1128 * HINTERNET a HTTP request handle on success
1132 HINTERNET WINAPI
HttpOpenRequestA(HINTERNET hHttpSession
,
1133 LPCSTR lpszVerb
, LPCSTR lpszObjectName
, LPCSTR lpszVersion
,
1134 LPCSTR lpszReferrer
, LPCSTR
*lpszAcceptTypes
,
1135 DWORD dwFlags
, DWORD_PTR dwContext
)
1137 LPWSTR szVerb
= NULL
, szObjectName
= NULL
;
1138 LPWSTR szVersion
= NULL
, szReferrer
= NULL
, *szAcceptTypes
= NULL
;
1139 INT acceptTypesCount
;
1140 HINTERNET rc
= FALSE
;
1143 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
1144 debugstr_a(lpszVerb
), debugstr_a(lpszObjectName
),
1145 debugstr_a(lpszVersion
), debugstr_a(lpszReferrer
), lpszAcceptTypes
,
1146 dwFlags
, dwContext
);
1150 szVerb
= heap_strdupAtoW(lpszVerb
);
1157 szObjectName
= heap_strdupAtoW(lpszObjectName
);
1158 if ( !szObjectName
)
1164 szVersion
= heap_strdupAtoW(lpszVersion
);
1171 szReferrer
= heap_strdupAtoW(lpszReferrer
);
1176 if (lpszAcceptTypes
)
1178 acceptTypesCount
= 0;
1179 types
= lpszAcceptTypes
;
1184 /* find out how many there are */
1185 if (*types
&& **types
)
1187 TRACE("accept type: %s\n", debugstr_a(*types
));
1193 WARN("invalid accept type pointer\n");
1198 szAcceptTypes
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
*) * (acceptTypesCount
+1));
1199 if (!szAcceptTypes
) goto end
;
1201 acceptTypesCount
= 0;
1202 types
= lpszAcceptTypes
;
1207 if (*types
&& **types
)
1208 szAcceptTypes
[acceptTypesCount
++] = heap_strdupAtoW(*types
);
1212 /* ignore invalid pointer */
1217 szAcceptTypes
[acceptTypesCount
] = NULL
;
1220 rc
= HttpOpenRequestW(hHttpSession
, szVerb
, szObjectName
,
1221 szVersion
, szReferrer
,
1222 (LPCWSTR
*)szAcceptTypes
, dwFlags
, dwContext
);
1227 acceptTypesCount
= 0;
1228 while (szAcceptTypes
[acceptTypesCount
])
1230 HeapFree(GetProcessHeap(), 0, szAcceptTypes
[acceptTypesCount
]);
1233 HeapFree(GetProcessHeap(), 0, szAcceptTypes
);
1235 HeapFree(GetProcessHeap(), 0, szReferrer
);
1236 HeapFree(GetProcessHeap(), 0, szVersion
);
1237 HeapFree(GetProcessHeap(), 0, szObjectName
);
1238 HeapFree(GetProcessHeap(), 0, szVerb
);
1243 /***********************************************************************
1246 static UINT
HTTP_EncodeBase64( LPCSTR bin
, unsigned int len
, LPWSTR base64
)
1249 static const CHAR HTTP_Base64Enc
[] =
1250 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1254 /* first 6 bits, all from bin[0] */
1255 base64
[n
++] = HTTP_Base64Enc
[(bin
[0] & 0xfc) >> 2];
1256 x
= (bin
[0] & 3) << 4;
1258 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1261 base64
[n
++] = HTTP_Base64Enc
[x
];
1266 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[1]&0xf0) >> 4 ) ];
1267 x
= ( bin
[1] & 0x0f ) << 2;
1269 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1272 base64
[n
++] = HTTP_Base64Enc
[x
];
1276 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[2]&0xc0 ) >> 6 ) ];
1278 /* last 6 bits, all from bin [2] */
1279 base64
[n
++] = HTTP_Base64Enc
[ bin
[2] & 0x3f ];
1287 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1288 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1289 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1290 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1291 static const signed char HTTP_Base64Dec
[256] =
1293 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1294 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1295 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1296 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1297 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1298 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1299 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1300 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1301 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1302 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1303 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1304 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1305 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1306 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1307 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1308 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1309 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1310 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1311 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1312 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1313 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1314 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1315 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1316 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1317 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1318 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1322 /***********************************************************************
1325 static UINT
HTTP_DecodeBase64( LPCWSTR base64
, LPSTR bin
)
1333 if (base64
[0] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1334 ((in
[0] = HTTP_Base64Dec
[base64
[0]]) == -1) ||
1335 base64
[1] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1336 ((in
[1] = HTTP_Base64Dec
[base64
[1]]) == -1))
1338 WARN("invalid base64: %s\n", debugstr_w(base64
));
1342 bin
[n
] = (unsigned char) (in
[0] << 2 | in
[1] >> 4);
1345 if ((base64
[2] == '=') && (base64
[3] == '='))
1347 if (base64
[2] > ARRAYSIZE(HTTP_Base64Dec
) ||
1348 ((in
[2] = HTTP_Base64Dec
[base64
[2]]) == -1))
1350 WARN("invalid base64: %s\n", debugstr_w(&base64
[2]));
1354 bin
[n
] = (unsigned char) (in
[1] << 4 | in
[2] >> 2);
1357 if (base64
[3] == '=')
1359 if (base64
[3] > ARRAYSIZE(HTTP_Base64Dec
) ||
1360 ((in
[3] = HTTP_Base64Dec
[base64
[3]]) == -1))
1362 WARN("invalid base64: %s\n", debugstr_w(&base64
[3]));
1366 bin
[n
] = (unsigned char) (((in
[2] << 6) & 0xc0) | in
[3]);
1375 /***********************************************************************
1376 * HTTP_InsertAuthorization
1378 * Insert or delete the authorization field in the request header.
1380 static BOOL
HTTP_InsertAuthorization( http_request_t
*lpwhr
, struct HttpAuthInfo
*pAuthInfo
, LPCWSTR header
)
1384 static const WCHAR wszSpace
[] = {' ',0};
1385 static const WCHAR wszBasic
[] = {'B','a','s','i','c',0};
1387 WCHAR
*authorization
= NULL
;
1389 if (pAuthInfo
->auth_data_len
)
1391 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1392 len
= strlenW(pAuthInfo
->scheme
)+1+((pAuthInfo
->auth_data_len
+2)*4)/3;
1393 authorization
= HeapAlloc(GetProcessHeap(), 0, (len
+1)*sizeof(WCHAR
));
1397 strcpyW(authorization
, pAuthInfo
->scheme
);
1398 strcatW(authorization
, wszSpace
);
1399 HTTP_EncodeBase64(pAuthInfo
->auth_data
,
1400 pAuthInfo
->auth_data_len
,
1401 authorization
+strlenW(authorization
));
1403 /* clear the data as it isn't valid now that it has been sent to the
1404 * server, unless it's Basic authentication which doesn't do
1405 * connection tracking */
1406 if (strcmpiW(pAuthInfo
->scheme
, wszBasic
))
1408 HeapFree(GetProcessHeap(), 0, pAuthInfo
->auth_data
);
1409 pAuthInfo
->auth_data
= NULL
;
1410 pAuthInfo
->auth_data_len
= 0;
1414 TRACE("Inserting authorization: %s\n", debugstr_w(authorization
));
1416 HTTP_ProcessHeader(lpwhr
, header
, authorization
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
1418 HeapFree(GetProcessHeap(), 0, authorization
);
1423 static WCHAR
*HTTP_BuildProxyRequestUrl(http_request_t
*req
)
1425 WCHAR new_location
[INTERNET_MAX_URL_LENGTH
], *url
;
1428 size
= sizeof(new_location
);
1429 if (HTTP_HttpQueryInfoW(req
, HTTP_QUERY_LOCATION
, new_location
, &size
, NULL
) == ERROR_SUCCESS
)
1431 if (!(url
= HeapAlloc( GetProcessHeap(), 0, size
+ sizeof(WCHAR
) ))) return NULL
;
1432 strcpyW( url
, new_location
);
1436 static const WCHAR slash
[] = { '/',0 };
1437 static const WCHAR format
[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1438 static const WCHAR formatSSL
[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1439 http_session_t
*session
= req
->lpHttpSession
;
1441 size
= 16; /* "https://" + sizeof(port#) + ":/\0" */
1442 size
+= strlenW( session
->lpszHostName
) + strlenW( req
->lpszPath
);
1444 if (!(url
= HeapAlloc( GetProcessHeap(), 0, size
* sizeof(WCHAR
) ))) return NULL
;
1446 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1447 sprintfW( url
, formatSSL
, session
->lpszHostName
, session
->nHostPort
);
1449 sprintfW( url
, format
, session
->lpszHostName
, session
->nHostPort
);
1450 if (req
->lpszPath
[0] != '/') strcatW( url
, slash
);
1451 strcatW( url
, req
->lpszPath
);
1453 TRACE("url=%s\n", debugstr_w(url
));
1457 /***********************************************************************
1458 * HTTP_DealWithProxy
1460 static BOOL
HTTP_DealWithProxy(appinfo_t
*hIC
, http_session_t
*lpwhs
, http_request_t
*lpwhr
)
1462 WCHAR buf
[MAXHOSTNAME
];
1463 WCHAR protoProxy
[MAXHOSTNAME
+ 15];
1464 DWORD protoProxyLen
= sizeof(protoProxy
) / sizeof(protoProxy
[0]);
1465 WCHAR proxy
[MAXHOSTNAME
+ 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1466 static WCHAR szNul
[] = { 0 };
1467 URL_COMPONENTSW UrlComponents
;
1468 static const WCHAR protoHttp
[] = { 'h','t','t','p',0 };
1469 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/',0 };
1470 static const WCHAR szFormat
[] = { 'h','t','t','p',':','/','/','%','s',0 };
1472 memset( &UrlComponents
, 0, sizeof UrlComponents
);
1473 UrlComponents
.dwStructSize
= sizeof UrlComponents
;
1474 UrlComponents
.lpszHostName
= buf
;
1475 UrlComponents
.dwHostNameLength
= MAXHOSTNAME
;
1477 if (!INTERNET_FindProxyForProtocol(hIC
->lpszProxy
, protoHttp
, protoProxy
, &protoProxyLen
))
1479 if( CSTR_EQUAL
!= CompareStringW(LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
1480 protoProxy
,strlenW(szHttp
),szHttp
,strlenW(szHttp
)) )
1481 sprintfW(proxy
, szFormat
, protoProxy
);
1483 strcpyW(proxy
, protoProxy
);
1484 if( !InternetCrackUrlW(proxy
, 0, 0, &UrlComponents
) )
1486 if( UrlComponents
.dwHostNameLength
== 0 )
1489 if( !lpwhr
->lpszPath
)
1490 lpwhr
->lpszPath
= szNul
;
1492 if(UrlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
1493 UrlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
1495 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszServerName
);
1496 lpwhs
->lpszServerName
= heap_strdupW(UrlComponents
.lpszHostName
);
1497 lpwhs
->nServerPort
= UrlComponents
.nPort
;
1499 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs
->lpszServerName
), lpwhs
->nServerPort
);
1503 #ifndef INET6_ADDRSTRLEN
1504 #define INET6_ADDRSTRLEN 46
1507 static DWORD
HTTP_ResolveName(http_request_t
*lpwhr
)
1509 char szaddr
[INET6_ADDRSTRLEN
];
1510 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
1513 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1514 INTERNET_STATUS_RESOLVING_NAME
,
1515 lpwhs
->lpszServerName
,
1516 (strlenW(lpwhs
->lpszServerName
)+1) * sizeof(WCHAR
));
1518 lpwhs
->sa_len
= sizeof(lpwhs
->socketAddress
);
1519 if (!GetAddress(lpwhs
->lpszServerName
, lpwhs
->nServerPort
,
1520 (struct sockaddr
*)&lpwhs
->socketAddress
, &lpwhs
->sa_len
))
1521 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
1523 switch (lpwhs
->socketAddress
.ss_family
)
1526 addr
= &((struct sockaddr_in
*)&lpwhs
->socketAddress
)->sin_addr
;
1529 addr
= &((struct sockaddr_in6
*)&lpwhs
->socketAddress
)->sin6_addr
;
1532 WARN("unsupported family %d\n", lpwhs
->socketAddress
.ss_family
);
1533 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
1535 inet_ntop(lpwhs
->socketAddress
.ss_family
, addr
, szaddr
, sizeof(szaddr
));
1536 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1537 INTERNET_STATUS_NAME_RESOLVED
,
1538 szaddr
, strlen(szaddr
)+1);
1540 TRACE("resolved %s to %s\n", debugstr_w(lpwhs
->lpszServerName
), szaddr
);
1541 return ERROR_SUCCESS
;
1544 static BOOL
HTTP_GetRequestURL(http_request_t
*req
, LPWSTR buf
)
1546 LPHTTPHEADERW host_header
;
1548 static const WCHAR formatW
[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1550 host_header
= HTTP_GetHeader(req
, hostW
);
1554 sprintfW(buf
, formatW
, host_header
->lpszValue
, req
->lpszPath
); /* FIXME */
1559 /***********************************************************************
1560 * HTTPREQ_Destroy (internal)
1562 * Deallocate request handle
1565 static void HTTPREQ_Destroy(object_header_t
*hdr
)
1567 http_request_t
*lpwhr
= (http_request_t
*) hdr
;
1572 if(lpwhr
->hCacheFile
) {
1573 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1576 CloseHandle(lpwhr
->hCacheFile
);
1578 memset(&ft
, 0, sizeof(FILETIME
));
1579 if(HTTP_GetRequestURL(lpwhr
, url
)) {
1580 CommitUrlCacheEntryW(url
, lpwhr
->lpszCacheFile
, ft
, ft
,
1581 NORMAL_CACHE_ENTRY
, NULL
, 0, NULL
, 0);
1585 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszCacheFile
);
1587 DeleteCriticalSection( &lpwhr
->read_section
);
1588 WININET_Release(&lpwhr
->lpHttpSession
->hdr
);
1590 destroy_authinfo(lpwhr
->pAuthInfo
);
1591 destroy_authinfo(lpwhr
->pProxyAuthInfo
);
1593 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszPath
);
1594 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVerb
);
1595 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszRawHeaders
);
1596 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVersion
);
1597 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszStatusText
);
1599 for (i
= 0; i
< lpwhr
->nCustHeaders
; i
++)
1601 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[i
].lpszField
);
1602 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[i
].lpszValue
);
1606 if(lpwhr
->gzip_stream
) {
1607 if(!lpwhr
->gzip_stream
->end_of_data
)
1608 inflateEnd(&lpwhr
->gzip_stream
->zstream
);
1609 HeapFree(GetProcessHeap(), 0, lpwhr
->gzip_stream
);
1613 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
);
1614 HeapFree(GetProcessHeap(), 0, lpwhr
);
1617 static void HTTPREQ_CloseConnection(object_header_t
*hdr
)
1619 http_request_t
*lpwhr
= (http_request_t
*) hdr
;
1621 TRACE("%p\n",lpwhr
);
1623 if (!NETCON_connected(&lpwhr
->netConnection
))
1626 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1627 INTERNET_STATUS_CLOSING_CONNECTION
, 0, 0);
1629 NETCON_close(&lpwhr
->netConnection
);
1631 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1632 INTERNET_STATUS_CONNECTION_CLOSED
, 0, 0);
1635 static BOOL
HTTP_KeepAlive(http_request_t
*lpwhr
)
1637 WCHAR szVersion
[10];
1638 WCHAR szConnectionResponse
[20];
1639 DWORD dwBufferSize
= sizeof(szVersion
);
1640 BOOL keepalive
= FALSE
;
1642 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1643 * the connection is keep-alive by default */
1644 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_VERSION
, szVersion
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1645 && !strcmpiW(szVersion
, g_szHttp1_1
))
1650 dwBufferSize
= sizeof(szConnectionResponse
);
1651 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_PROXY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1652 || HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
)
1654 keepalive
= !strcmpiW(szConnectionResponse
, szKeepAlive
);
1660 static DWORD
HTTPREQ_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
1662 http_request_t
*req
= (http_request_t
*)hdr
;
1665 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO
:
1667 http_session_t
*lpwhs
= req
->lpHttpSession
;
1668 INTERNET_DIAGNOSTIC_SOCKET_INFO
*info
= buffer
;
1670 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1672 if (*size
< sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
))
1673 return ERROR_INSUFFICIENT_BUFFER
;
1674 *size
= sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
);
1675 /* FIXME: can't get a SOCKET from our connection since we don't use
1679 /* FIXME: get source port from req->netConnection */
1680 info
->SourcePort
= 0;
1681 info
->DestPort
= lpwhs
->nHostPort
;
1683 if (HTTP_KeepAlive(req
))
1684 info
->Flags
|= IDSI_FLAG_KEEP_ALIVE
;
1685 if (lpwhs
->lpAppInfo
->lpszProxy
&& lpwhs
->lpAppInfo
->lpszProxy
[0] != 0)
1686 info
->Flags
|= IDSI_FLAG_PROXY
;
1687 if (req
->netConnection
.useSSL
)
1688 info
->Flags
|= IDSI_FLAG_SECURE
;
1690 return ERROR_SUCCESS
;
1693 case INTERNET_OPTION_SECURITY_FLAGS
:
1698 if (*size
< sizeof(ULONG
))
1699 return ERROR_INSUFFICIENT_BUFFER
;
1701 *size
= sizeof(DWORD
);
1703 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1704 flags
|= SECURITY_FLAG_SECURE
;
1705 flags
|= req
->netConnection
.security_flags
;
1706 bits
= NETCON_GetCipherStrength(&req
->netConnection
);
1708 flags
|= SECURITY_FLAG_STRENGTH_STRONG
;
1709 else if (bits
>= 56)
1710 flags
|= SECURITY_FLAG_STRENGTH_MEDIUM
;
1712 flags
|= SECURITY_FLAG_STRENGTH_WEAK
;
1713 *(DWORD
*)buffer
= flags
;
1714 return ERROR_SUCCESS
;
1717 case INTERNET_OPTION_HANDLE_TYPE
:
1718 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1720 if (*size
< sizeof(ULONG
))
1721 return ERROR_INSUFFICIENT_BUFFER
;
1723 *size
= sizeof(DWORD
);
1724 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_HTTP_REQUEST
;
1725 return ERROR_SUCCESS
;
1727 case INTERNET_OPTION_URL
: {
1728 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1733 static const WCHAR httpW
[] = {'h','t','t','p',':','/','/',0};
1735 TRACE("INTERNET_OPTION_URL\n");
1737 host
= HTTP_GetHeader(req
, hostW
);
1738 strcpyW(url
, httpW
);
1739 strcatW(url
, host
->lpszValue
);
1740 if (NULL
!= (pch
= strchrW(url
+ strlenW(httpW
), ':')))
1742 strcatW(url
, req
->lpszPath
);
1744 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url
));
1747 len
= (strlenW(url
)+1) * sizeof(WCHAR
);
1749 return ERROR_INSUFFICIENT_BUFFER
;
1752 strcpyW(buffer
, url
);
1753 return ERROR_SUCCESS
;
1755 len
= WideCharToMultiByte(CP_ACP
, 0, url
, -1, buffer
, *size
, NULL
, NULL
);
1757 return ERROR_INSUFFICIENT_BUFFER
;
1760 return ERROR_SUCCESS
;
1764 case INTERNET_OPTION_CACHE_TIMESTAMPS
: {
1765 INTERNET_CACHE_ENTRY_INFOW
*info
;
1766 INTERNET_CACHE_TIMESTAMPS
*ts
= buffer
;
1767 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1768 DWORD nbytes
, error
;
1771 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1773 if (*size
< sizeof(*ts
))
1775 *size
= sizeof(*ts
);
1776 return ERROR_INSUFFICIENT_BUFFER
;
1779 HTTP_GetRequestURL(req
, url
);
1780 ret
= GetUrlCacheEntryInfoW(url
, NULL
, &nbytes
);
1781 error
= GetLastError();
1782 if (!ret
&& error
== ERROR_INSUFFICIENT_BUFFER
)
1784 if (!(info
= HeapAlloc(GetProcessHeap(), 0, nbytes
)))
1785 return ERROR_OUTOFMEMORY
;
1787 GetUrlCacheEntryInfoW(url
, info
, &nbytes
);
1789 ts
->ftExpires
= info
->ExpireTime
;
1790 ts
->ftLastModified
= info
->LastModifiedTime
;
1792 HeapFree(GetProcessHeap(), 0, info
);
1793 *size
= sizeof(*ts
);
1794 return ERROR_SUCCESS
;
1799 case INTERNET_OPTION_DATAFILE_NAME
: {
1802 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1804 if(!req
->lpszCacheFile
) {
1806 return ERROR_INTERNET_ITEM_NOT_FOUND
;
1810 req_size
= (lstrlenW(req
->lpszCacheFile
)+1) * sizeof(WCHAR
);
1811 if(*size
< req_size
)
1812 return ERROR_INSUFFICIENT_BUFFER
;
1815 memcpy(buffer
, req
->lpszCacheFile
, *size
);
1816 return ERROR_SUCCESS
;
1818 req_size
= WideCharToMultiByte(CP_ACP
, 0, req
->lpszCacheFile
, -1, NULL
, 0, NULL
, NULL
);
1819 if (req_size
> *size
)
1820 return ERROR_INSUFFICIENT_BUFFER
;
1822 *size
= WideCharToMultiByte(CP_ACP
, 0, req
->lpszCacheFile
,
1823 -1, buffer
, *size
, NULL
, NULL
);
1824 return ERROR_SUCCESS
;
1828 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT
: {
1829 PCCERT_CONTEXT context
;
1831 if(*size
< sizeof(INTERNET_CERTIFICATE_INFOA
)) {
1832 *size
= sizeof(INTERNET_CERTIFICATE_INFOA
);
1833 return ERROR_INSUFFICIENT_BUFFER
;
1836 context
= (PCCERT_CONTEXT
)NETCON_GetCert(&(req
->netConnection
));
1838 INTERNET_CERTIFICATE_INFOA
*info
= (INTERNET_CERTIFICATE_INFOA
*)buffer
;
1841 memset(info
, 0, sizeof(INTERNET_CERTIFICATE_INFOW
));
1842 info
->ftExpiry
= context
->pCertInfo
->NotAfter
;
1843 info
->ftStart
= context
->pCertInfo
->NotBefore
;
1844 len
= CertNameToStrA(context
->dwCertEncodingType
,
1845 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1846 info
->lpszSubjectInfo
= LocalAlloc(0, len
);
1847 if(info
->lpszSubjectInfo
)
1848 CertNameToStrA(context
->dwCertEncodingType
,
1849 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
,
1850 info
->lpszSubjectInfo
, len
);
1851 len
= CertNameToStrA(context
->dwCertEncodingType
,
1852 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1853 info
->lpszIssuerInfo
= LocalAlloc(0, len
);
1854 if(info
->lpszIssuerInfo
)
1855 CertNameToStrA(context
->dwCertEncodingType
,
1856 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
,
1857 info
->lpszIssuerInfo
, len
);
1858 info
->dwKeySize
= NETCON_GetCipherStrength(&req
->netConnection
);
1859 CertFreeCertificateContext(context
);
1860 return ERROR_SUCCESS
;
1865 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
1868 static DWORD
HTTPREQ_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
1870 http_request_t
*req
= (http_request_t
*)hdr
;
1873 case INTERNET_OPTION_SECURITY_FLAGS
:
1877 if (!buffer
|| size
!= sizeof(DWORD
))
1878 return ERROR_INVALID_PARAMETER
;
1879 flags
= *(DWORD
*)buffer
;
1880 TRACE("%08x\n", flags
);
1881 req
->netConnection
.security_flags
= flags
;
1882 return ERROR_SUCCESS
;
1884 case INTERNET_OPTION_SEND_TIMEOUT
:
1885 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
1886 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1888 if (size
!= sizeof(DWORD
))
1889 return ERROR_INVALID_PARAMETER
;
1891 return NETCON_set_timeout(&req
->netConnection
, option
== INTERNET_OPTION_SEND_TIMEOUT
,
1894 case INTERNET_OPTION_USERNAME
:
1895 HeapFree(GetProcessHeap(), 0, req
->lpHttpSession
->lpszUserName
);
1896 if (!(req
->lpHttpSession
->lpszUserName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
1897 return ERROR_SUCCESS
;
1899 case INTERNET_OPTION_PASSWORD
:
1900 HeapFree(GetProcessHeap(), 0, req
->lpHttpSession
->lpszPassword
);
1901 if (!(req
->lpHttpSession
->lpszPassword
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
1902 return ERROR_SUCCESS
;
1903 case INTERNET_OPTION_HTTP_DECODING
:
1904 if(size
!= sizeof(BOOL
))
1905 return ERROR_INVALID_PARAMETER
;
1906 req
->decoding
= *(BOOL
*)buffer
;
1907 return ERROR_SUCCESS
;
1910 return ERROR_INTERNET_INVALID_OPTION
;
1913 /* read some more data into the read buffer (the read section must be held) */
1914 static DWORD
read_more_data( http_request_t
*req
, int maxlen
)
1921 /* move existing data to the start of the buffer */
1923 memmove( req
->read_buf
, req
->read_buf
+ req
->read_pos
, req
->read_size
);
1927 if (maxlen
== -1) maxlen
= sizeof(req
->read_buf
);
1929 res
= NETCON_recv( &req
->netConnection
, req
->read_buf
+ req
->read_size
,
1930 maxlen
- req
->read_size
, 0, &len
);
1931 if(res
== ERROR_SUCCESS
)
1932 req
->read_size
+= len
;
1937 /* remove some amount of data from the read buffer (the read section must be held) */
1938 static void remove_data( http_request_t
*req
, int count
)
1940 if (!(req
->read_size
-= count
)) req
->read_pos
= 0;
1941 else req
->read_pos
+= count
;
1944 static BOOL
read_line( http_request_t
*req
, LPSTR buffer
, DWORD
*len
)
1946 int count
, bytes_read
, pos
= 0;
1949 EnterCriticalSection( &req
->read_section
);
1952 BYTE
*eol
= memchr( req
->read_buf
+ req
->read_pos
, '\n', req
->read_size
);
1956 count
= eol
- (req
->read_buf
+ req
->read_pos
);
1957 bytes_read
= count
+ 1;
1959 else count
= bytes_read
= req
->read_size
;
1961 count
= min( count
, *len
- pos
);
1962 memcpy( buffer
+ pos
, req
->read_buf
+ req
->read_pos
, count
);
1964 remove_data( req
, bytes_read
);
1967 if ((res
= read_more_data( req
, -1 )) != ERROR_SUCCESS
|| !req
->read_size
)
1970 TRACE( "returning empty string\n" );
1971 LeaveCriticalSection( &req
->read_section
);
1972 INTERNET_SetLastError(res
);
1976 LeaveCriticalSection( &req
->read_section
);
1980 if (pos
&& buffer
[pos
- 1] == '\r') pos
--;
1983 buffer
[*len
- 1] = 0;
1984 TRACE( "returning %s\n", debugstr_a(buffer
));
1988 /* discard data contents until we reach end of line (the read section must be held) */
1989 static DWORD
discard_eol( http_request_t
*req
)
1995 BYTE
*eol
= memchr( req
->read_buf
+ req
->read_pos
, '\n', req
->read_size
);
1998 remove_data( req
, (eol
+ 1) - (req
->read_buf
+ req
->read_pos
) );
2001 req
->read_pos
= req
->read_size
= 0; /* discard everything */
2002 if ((res
= read_more_data( req
, -1 )) != ERROR_SUCCESS
) return res
;
2003 } while (req
->read_size
);
2004 return ERROR_SUCCESS
;
2007 /* read the size of the next chunk (the read section must be held) */
2008 static DWORD
start_next_chunk( http_request_t
*req
)
2010 DWORD chunk_size
= 0, res
;
2012 if (!req
->dwContentLength
) return ERROR_SUCCESS
;
2013 if (req
->dwContentLength
== req
->dwContentRead
)
2015 /* read terminator for the previous chunk */
2016 if ((res
= discard_eol( req
)) != ERROR_SUCCESS
) return res
;
2017 req
->dwContentLength
= ~0u;
2018 req
->dwContentRead
= 0;
2022 while (req
->read_size
)
2024 char ch
= req
->read_buf
[req
->read_pos
];
2025 if (ch
>= '0' && ch
<= '9') chunk_size
= chunk_size
* 16 + ch
- '0';
2026 else if (ch
>= 'a' && ch
<= 'f') chunk_size
= chunk_size
* 16 + ch
- 'a' + 10;
2027 else if (ch
>= 'A' && ch
<= 'F') chunk_size
= chunk_size
* 16 + ch
- 'A' + 10;
2028 else if (ch
== ';' || ch
== '\r' || ch
== '\n')
2030 TRACE( "reading %u byte chunk\n", chunk_size
);
2031 req
->dwContentLength
= chunk_size
;
2032 req
->dwContentRead
= 0;
2033 return discard_eol( req
);
2035 remove_data( req
, 1 );
2037 if ((res
= read_more_data( req
, -1 )) != ERROR_SUCCESS
) return res
;
2038 if (!req
->read_size
)
2040 req
->dwContentLength
= req
->dwContentRead
= 0;
2041 return ERROR_SUCCESS
;
2046 /* check if we have reached the end of the data to read (the read section must be held) */
2047 static BOOL
end_of_read_data( http_request_t
*req
)
2049 if (req
->gzip_stream
) return req
->gzip_stream
->end_of_data
&& !req
->gzip_stream
->buf_size
;
2050 if (req
->read_chunked
) return (req
->dwContentLength
== 0);
2051 if (req
->dwContentLength
== ~0u) return FALSE
;
2052 return (req
->dwContentLength
== req
->dwContentRead
);
2055 /* fetch some more data into the read buffer (the read section must be held) */
2056 static DWORD
refill_buffer( http_request_t
*req
)
2058 int len
= sizeof(req
->read_buf
);
2061 if (req
->read_chunked
&& (req
->dwContentLength
== ~0u || req
->dwContentLength
== req
->dwContentRead
))
2063 if ((res
= start_next_chunk( req
)) != ERROR_SUCCESS
) return res
;
2066 if (req
->dwContentLength
!= ~0u) len
= min( len
, req
->dwContentLength
- req
->dwContentRead
);
2067 if (len
<= req
->read_size
) return ERROR_SUCCESS
;
2069 if ((res
= read_more_data( req
, len
)) != ERROR_SUCCESS
) return res
;
2070 if (!req
->read_size
) req
->dwContentLength
= req
->dwContentRead
= 0;
2071 return ERROR_SUCCESS
;
2074 static DWORD
read_gzip_data(http_request_t
*req
, BYTE
*buf
, int size
, BOOL sync
, int *read_ret
)
2076 DWORD ret
= ERROR_SUCCESS
;
2080 z_stream
*zstream
= &req
->gzip_stream
->zstream
;
2084 while(read
< size
&& !req
->gzip_stream
->end_of_data
) {
2085 if(!req
->read_size
) {
2086 if(!sync
|| refill_buffer(req
) != ERROR_SUCCESS
)
2090 if(req
->dwContentRead
== req
->dwContentLength
)
2093 buf_avail
= req
->dwContentLength
== ~0 ? req
->read_size
: min(req
->read_size
, req
->dwContentLength
-req
->dwContentRead
);
2095 zstream
->next_in
= req
->read_buf
+req
->read_pos
;
2096 zstream
->avail_in
= buf_avail
;
2097 zstream
->next_out
= buf
+read
;
2098 zstream
->avail_out
= size
-read
;
2099 zres
= inflate(zstream
, Z_FULL_FLUSH
);
2100 read
= size
- zstream
->avail_out
;
2101 req
->dwContentRead
+= buf_avail
-zstream
->avail_in
;
2102 remove_data(req
, buf_avail
-zstream
->avail_in
);
2103 if(zres
== Z_STREAM_END
) {
2104 TRACE("end of data\n");
2105 req
->gzip_stream
->end_of_data
= TRUE
;
2106 inflateEnd(&req
->gzip_stream
->zstream
);
2107 }else if(zres
!= Z_OK
) {
2108 WARN("inflate failed %d\n", zres
);
2110 ret
= ERROR_INTERNET_DECODING_FAILED
;
2120 static void refill_gzip_buffer(http_request_t
*req
)
2125 if(!req
->gzip_stream
|| !req
->read_size
|| req
->gzip_stream
->buf_size
== sizeof(req
->gzip_stream
->buf
))
2128 if(req
->gzip_stream
->buf_pos
) {
2129 if(req
->gzip_stream
->buf_size
)
2130 memmove(req
->gzip_stream
->buf
, req
->gzip_stream
->buf
+ req
->gzip_stream
->buf_pos
, req
->gzip_stream
->buf_size
);
2131 req
->gzip_stream
->buf_pos
= 0;
2134 res
= read_gzip_data(req
, req
->gzip_stream
->buf
+ req
->gzip_stream
->buf_size
,
2135 sizeof(req
->gzip_stream
->buf
) - req
->gzip_stream
->buf_size
, FALSE
, &len
);
2136 if(res
== ERROR_SUCCESS
)
2137 req
->gzip_stream
->buf_size
+= len
;
2140 /* return the size of data available to be read immediately (the read section must be held) */
2141 static DWORD
get_avail_data( http_request_t
*req
)
2143 if (req
->gzip_stream
) {
2144 refill_gzip_buffer(req
);
2145 return req
->gzip_stream
->buf_size
;
2147 if (req
->read_chunked
&& (req
->dwContentLength
== ~0u || req
->dwContentLength
== req
->dwContentRead
))
2149 return min( req
->read_size
, req
->dwContentLength
- req
->dwContentRead
);
2152 static void HTTP_ReceiveRequestData(http_request_t
*req
, BOOL first_notif
)
2154 INTERNET_ASYNC_RESULT iar
;
2159 EnterCriticalSection( &req
->read_section
);
2160 if ((res
= refill_buffer( req
)) == ERROR_SUCCESS
) {
2161 iar
.dwResult
= (DWORD_PTR
)req
->hdr
.hInternet
;
2162 iar
.dwError
= first_notif
? 0 : get_avail_data(req
);
2167 LeaveCriticalSection( &req
->read_section
);
2169 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2170 sizeof(INTERNET_ASYNC_RESULT
));
2173 /* read data from the http connection (the read section must be held) */
2174 static DWORD
HTTPREQ_Read(http_request_t
*req
, void *buffer
, DWORD size
, DWORD
*read
, BOOL sync
)
2176 BOOL finished_reading
= FALSE
;
2177 int len
, bytes_read
= 0;
2178 DWORD ret
= ERROR_SUCCESS
;
2180 EnterCriticalSection( &req
->read_section
);
2182 if (req
->read_chunked
&& (req
->dwContentLength
== ~0u || req
->dwContentLength
== req
->dwContentRead
))
2184 if (start_next_chunk( req
) != ERROR_SUCCESS
) goto done
;
2187 if(req
->gzip_stream
) {
2188 if(req
->gzip_stream
->buf_size
) {
2189 bytes_read
= min(req
->gzip_stream
->buf_size
, size
);
2190 memcpy(buffer
, req
->gzip_stream
->buf
+ req
->gzip_stream
->buf_pos
, bytes_read
);
2191 req
->gzip_stream
->buf_pos
+= bytes_read
;
2192 req
->gzip_stream
->buf_size
-= bytes_read
;
2193 }else if(!req
->read_size
&& !req
->gzip_stream
->end_of_data
) {
2197 if(size
> bytes_read
) {
2198 ret
= read_gzip_data(req
, (BYTE
*)buffer
+bytes_read
, size
-bytes_read
, sync
, &len
);
2199 if(ret
== ERROR_SUCCESS
)
2203 finished_reading
= req
->gzip_stream
->end_of_data
&& !req
->gzip_stream
->buf_size
;
2205 if (req
->dwContentLength
!= ~0u) size
= min( size
, req
->dwContentLength
- req
->dwContentRead
);
2207 if (req
->read_size
) {
2208 bytes_read
= min( req
->read_size
, size
);
2209 memcpy( buffer
, req
->read_buf
+ req
->read_pos
, bytes_read
);
2210 remove_data( req
, bytes_read
);
2213 if (size
> bytes_read
&& (!bytes_read
|| sync
)) {
2214 if (NETCON_recv( &req
->netConnection
, (char *)buffer
+ bytes_read
, size
- bytes_read
,
2215 sync
? MSG_WAITALL
: 0, &len
) == ERROR_SUCCESS
)
2217 /* always return success, even if the network layer returns an error */
2220 finished_reading
= !bytes_read
&& req
->dwContentRead
== req
->dwContentLength
;
2221 req
->dwContentRead
+= bytes_read
;
2226 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read
, req
->dwContentRead
, req
->dwContentLength
);
2227 LeaveCriticalSection( &req
->read_section
);
2229 if(ret
== ERROR_SUCCESS
&& req
->lpszCacheFile
) {
2231 DWORD dwBytesWritten
;
2233 res
= WriteFile(req
->hCacheFile
, buffer
, bytes_read
, &dwBytesWritten
, NULL
);
2235 WARN("WriteFile failed: %u\n", GetLastError());
2238 if(finished_reading
)
2239 HTTP_FinishedReading(req
);
2245 static DWORD
HTTPREQ_ReadFile(object_header_t
*hdr
, void *buffer
, DWORD size
, DWORD
*read
)
2247 http_request_t
*req
= (http_request_t
*)hdr
;
2250 EnterCriticalSection( &req
->read_section
);
2251 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2252 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2254 res
= HTTPREQ_Read(req
, buffer
, size
, read
, TRUE
);
2255 if(res
== ERROR_SUCCESS
)
2257 LeaveCriticalSection( &req
->read_section
);
2262 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST
*workRequest
)
2264 struct WORKREQ_INTERNETREADFILEEXA
const *data
= &workRequest
->u
.InternetReadFileExA
;
2265 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2266 INTERNET_ASYNC_RESULT iar
;
2269 TRACE("INTERNETREADFILEEXA %p\n", workRequest
->hdr
);
2271 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2272 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2274 iar
.dwResult
= res
== ERROR_SUCCESS
;
2277 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2278 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2279 sizeof(INTERNET_ASYNC_RESULT
));
2282 static DWORD
HTTPREQ_ReadFileExA(object_header_t
*hdr
, INTERNET_BUFFERSA
*buffers
,
2283 DWORD flags
, DWORD_PTR context
)
2285 http_request_t
*req
= (http_request_t
*)hdr
;
2286 DWORD res
, size
, read
, error
= ERROR_SUCCESS
;
2288 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2289 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2291 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2292 return ERROR_INVALID_PARAMETER
;
2294 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2296 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2298 WORKREQUEST workRequest
;
2300 if (TryEnterCriticalSection( &req
->read_section
))
2302 if (get_avail_data(req
))
2304 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2305 &buffers
->dwBufferLength
, FALSE
);
2306 size
= buffers
->dwBufferLength
;
2307 LeaveCriticalSection( &req
->read_section
);
2310 LeaveCriticalSection( &req
->read_section
);
2313 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExAProc
;
2314 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2315 workRequest
.u
.InternetReadFileExA
.lpBuffersOut
= buffers
;
2317 INTERNET_AsyncCall(&workRequest
);
2319 return ERROR_IO_PENDING
;
2323 size
= buffers
->dwBufferLength
;
2325 EnterCriticalSection( &req
->read_section
);
2326 if(hdr
->dwError
== ERROR_SUCCESS
)
2327 hdr
->dwError
= INTERNET_HANDLE_IN_USE
;
2328 else if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2329 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2332 res
= HTTPREQ_Read(req
, (char*)buffers
->lpvBuffer
+read
, size
-read
,
2333 &buffers
->dwBufferLength
, !(flags
& IRF_NO_WAIT
));
2334 if(res
== ERROR_SUCCESS
)
2335 read
+= buffers
->dwBufferLength
;
2339 if(!req
->read_chunked
|| read
==size
|| req
->dwContentLength
!=req
->dwContentRead
2340 || !req
->dwContentLength
|| (req
->gzip_stream
&& req
->gzip_stream
->end_of_data
))
2342 LeaveCriticalSection( &req
->read_section
);
2344 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2345 &buffers
->dwBufferLength
, sizeof(buffers
->dwBufferLength
));
2346 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2347 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2349 EnterCriticalSection( &req
->read_section
);
2352 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2353 hdr
->dwError
= ERROR_SUCCESS
;
2355 error
= hdr
->dwError
;
2357 LeaveCriticalSection( &req
->read_section
);
2358 size
= buffers
->dwBufferLength
;
2359 buffers
->dwBufferLength
= read
;
2362 if (res
== ERROR_SUCCESS
) {
2363 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2364 &size
, sizeof(size
));
2367 return res
==ERROR_SUCCESS
? error
: res
;
2370 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST
*workRequest
)
2372 struct WORKREQ_INTERNETREADFILEEXW
const *data
= &workRequest
->u
.InternetReadFileExW
;
2373 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2374 INTERNET_ASYNC_RESULT iar
;
2377 TRACE("INTERNETREADFILEEXW %p\n", workRequest
->hdr
);
2379 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2380 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2382 iar
.dwResult
= res
== ERROR_SUCCESS
;
2385 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2386 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2387 sizeof(INTERNET_ASYNC_RESULT
));
2390 static DWORD
HTTPREQ_ReadFileExW(object_header_t
*hdr
, INTERNET_BUFFERSW
*buffers
,
2391 DWORD flags
, DWORD_PTR context
)
2394 http_request_t
*req
= (http_request_t
*)hdr
;
2395 DWORD res
, size
, read
, error
= ERROR_SUCCESS
;
2397 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2398 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2400 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2401 return ERROR_INVALID_PARAMETER
;
2403 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2405 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2407 WORKREQUEST workRequest
;
2409 if (TryEnterCriticalSection( &req
->read_section
))
2411 if (get_avail_data(req
))
2413 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2414 &buffers
->dwBufferLength
, FALSE
);
2415 size
= buffers
->dwBufferLength
;
2416 LeaveCriticalSection( &req
->read_section
);
2419 LeaveCriticalSection( &req
->read_section
);
2422 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExWProc
;
2423 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2424 workRequest
.u
.InternetReadFileExW
.lpBuffersOut
= buffers
;
2426 INTERNET_AsyncCall(&workRequest
);
2428 return ERROR_IO_PENDING
;
2432 size
= buffers
->dwBufferLength
;
2434 EnterCriticalSection( &req
->read_section
);
2435 if(hdr
->dwError
== ERROR_SUCCESS
)
2436 hdr
->dwError
= INTERNET_HANDLE_IN_USE
;
2437 else if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2438 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2441 res
= HTTPREQ_Read(req
, (char*)buffers
->lpvBuffer
+read
, size
-read
,
2442 &buffers
->dwBufferLength
, !(flags
& IRF_NO_WAIT
));
2443 if(res
== ERROR_SUCCESS
)
2444 read
+= buffers
->dwBufferLength
;
2448 if(!req
->read_chunked
|| read
==size
|| req
->dwContentLength
!=req
->dwContentRead
2449 || !req
->dwContentLength
|| (req
->gzip_stream
&& req
->gzip_stream
->end_of_data
))
2451 LeaveCriticalSection( &req
->read_section
);
2453 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2454 &buffers
->dwBufferLength
, sizeof(buffers
->dwBufferLength
));
2455 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2456 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2458 EnterCriticalSection( &req
->read_section
);
2461 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2462 hdr
->dwError
= ERROR_SUCCESS
;
2464 error
= hdr
->dwError
;
2466 LeaveCriticalSection( &req
->read_section
);
2467 size
= buffers
->dwBufferLength
;
2468 buffers
->dwBufferLength
= read
;
2471 if (res
== ERROR_SUCCESS
) {
2472 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2473 &size
, sizeof(size
));
2476 return res
==ERROR_SUCCESS
? error
: res
;
2479 static DWORD
HTTPREQ_WriteFile(object_header_t
*hdr
, const void *buffer
, DWORD size
, DWORD
*written
)
2482 http_request_t
*lpwhr
= (http_request_t
*)hdr
;
2484 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
2487 res
= NETCON_send(&lpwhr
->netConnection
, buffer
, size
, 0, (LPINT
)written
);
2488 if (res
== ERROR_SUCCESS
)
2489 lpwhr
->dwBytesWritten
+= *written
;
2491 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_SENT
, written
, sizeof(DWORD
));
2495 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST
*workRequest
)
2497 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2499 HTTP_ReceiveRequestData(req
, FALSE
);
2502 static DWORD
HTTPREQ_QueryDataAvailable(object_header_t
*hdr
, DWORD
*available
, DWORD flags
, DWORD_PTR ctx
)
2504 http_request_t
*req
= (http_request_t
*)hdr
;
2506 TRACE("(%p %p %x %lx)\n", req
, available
, flags
, ctx
);
2508 if (req
->lpHttpSession
->lpAppInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
2510 WORKREQUEST workRequest
;
2512 /* never wait, if we can't enter the section we queue an async request right away */
2513 if (TryEnterCriticalSection( &req
->read_section
))
2515 if ((*available
= get_avail_data( req
))) goto done
;
2516 if (end_of_read_data( req
)) goto done
;
2517 LeaveCriticalSection( &req
->read_section
);
2520 workRequest
.asyncproc
= HTTPREQ_AsyncQueryDataAvailableProc
;
2521 workRequest
.hdr
= WININET_AddRef( &req
->hdr
);
2523 INTERNET_AsyncCall(&workRequest
);
2525 return ERROR_IO_PENDING
;
2528 EnterCriticalSection( &req
->read_section
);
2530 if (!(*available
= get_avail_data( req
)) && !end_of_read_data( req
))
2532 refill_buffer( req
);
2533 *available
= get_avail_data( req
);
2537 if (*available
== sizeof(req
->read_buf
) && !req
->gzip_stream
) /* check if we have even more pending in the socket */
2540 if (NETCON_query_data_available(&req
->netConnection
, &extra
))
2541 *available
= min( *available
+ extra
, req
->dwContentLength
- req
->dwContentRead
);
2543 LeaveCriticalSection( &req
->read_section
);
2545 TRACE( "returning %u\n", *available
);
2546 return ERROR_SUCCESS
;
2549 static const object_vtbl_t HTTPREQVtbl
= {
2551 HTTPREQ_CloseConnection
,
2552 HTTPREQ_QueryOption
,
2555 HTTPREQ_ReadFileExA
,
2556 HTTPREQ_ReadFileExW
,
2558 HTTPREQ_QueryDataAvailable
,
2562 /***********************************************************************
2563 * HTTP_HttpOpenRequestW (internal)
2565 * Open a HTTP request handle
2568 * HINTERNET a HTTP request handle on success
2572 static DWORD
HTTP_HttpOpenRequestW(http_session_t
*lpwhs
,
2573 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
2574 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
2575 DWORD dwFlags
, DWORD_PTR dwContext
, HINTERNET
*ret
)
2577 appinfo_t
*hIC
= NULL
;
2578 http_request_t
*lpwhr
;
2579 LPWSTR lpszHostName
= NULL
;
2580 HINTERNET handle
= NULL
;
2581 static const WCHAR szHostForm
[] = {'%','s',':','%','u',0};
2586 assert( lpwhs
->hdr
.htype
== WH_HHTTPSESSION
);
2587 hIC
= lpwhs
->lpAppInfo
;
2589 lpwhr
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(http_request_t
));
2592 res
= ERROR_OUTOFMEMORY
;
2595 lpwhr
->hdr
.htype
= WH_HHTTPREQ
;
2596 lpwhr
->hdr
.vtbl
= &HTTPREQVtbl
;
2597 lpwhr
->hdr
.dwFlags
= dwFlags
;
2598 lpwhr
->hdr
.dwContext
= dwContext
;
2599 lpwhr
->hdr
.refs
= 1;
2600 lpwhr
->hdr
.lpfnStatusCB
= lpwhs
->hdr
.lpfnStatusCB
;
2601 lpwhr
->hdr
.dwInternalFlags
= lpwhs
->hdr
.dwInternalFlags
& INET_CALLBACKW
;
2602 lpwhr
->dwContentLength
= ~0u;
2603 InitializeCriticalSection( &lpwhr
->read_section
);
2605 WININET_AddRef( &lpwhs
->hdr
);
2606 lpwhr
->lpHttpSession
= lpwhs
;
2607 list_add_head( &lpwhs
->hdr
.children
, &lpwhr
->hdr
.entry
);
2609 lpszHostName
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
) *
2610 (strlenW(lpwhs
->lpszHostName
) + 7 /* length of ":65535" + 1 */));
2611 if (NULL
== lpszHostName
)
2613 res
= ERROR_OUTOFMEMORY
;
2617 handle
= WININET_AllocHandle( &lpwhr
->hdr
);
2620 res
= ERROR_OUTOFMEMORY
;
2624 if ((res
= NETCON_init(&lpwhr
->netConnection
, dwFlags
& INTERNET_FLAG_SECURE
)) != ERROR_SUCCESS
)
2626 InternetCloseHandle( handle
);
2631 if (lpszObjectName
&& *lpszObjectName
) {
2635 rc
= UrlEscapeW(lpszObjectName
, NULL
, &len
, URL_ESCAPE_SPACES_ONLY
);
2636 if (rc
!= E_POINTER
)
2637 len
= strlenW(lpszObjectName
)+1;
2638 lpwhr
->lpszPath
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
2639 rc
= UrlEscapeW(lpszObjectName
, lpwhr
->lpszPath
, &len
,
2640 URL_ESCAPE_SPACES_ONLY
);
2643 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName
),rc
);
2644 strcpyW(lpwhr
->lpszPath
,lpszObjectName
);
2647 static const WCHAR slashW
[] = {'/',0};
2649 lpwhr
->lpszPath
= heap_strdupW(slashW
);
2652 if (lpszReferrer
&& *lpszReferrer
)
2653 HTTP_ProcessHeader(lpwhr
, HTTP_REFERER
, lpszReferrer
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2655 if (lpszAcceptTypes
)
2658 for (i
= 0; lpszAcceptTypes
[i
]; i
++)
2660 if (!*lpszAcceptTypes
[i
]) continue;
2661 HTTP_ProcessHeader(lpwhr
, HTTP_ACCEPT
, lpszAcceptTypes
[i
],
2662 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
|
2663 HTTP_ADDHDR_FLAG_REQ
|
2664 (i
== 0 ? HTTP_ADDHDR_FLAG_REPLACE
: 0));
2668 lpwhr
->lpszVerb
= heap_strdupW(lpszVerb
&& *lpszVerb
? lpszVerb
: szGET
);
2669 lpwhr
->lpszVersion
= heap_strdupW(lpszVersion
? lpszVersion
: g_szHttp1_1
);
2671 if (lpwhs
->nHostPort
!= INTERNET_INVALID_PORT_NUMBER
&&
2672 lpwhs
->nHostPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
2673 lpwhs
->nHostPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
2675 sprintfW(lpszHostName
, szHostForm
, lpwhs
->lpszHostName
, lpwhs
->nHostPort
);
2676 HTTP_ProcessHeader(lpwhr
, hostW
, lpszHostName
,
2677 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2680 HTTP_ProcessHeader(lpwhr
, hostW
, lpwhs
->lpszHostName
,
2681 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2683 if (lpwhs
->nServerPort
== INTERNET_INVALID_PORT_NUMBER
)
2684 lpwhs
->nServerPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
2685 INTERNET_DEFAULT_HTTPS_PORT
:
2686 INTERNET_DEFAULT_HTTP_PORT
);
2688 if (lpwhs
->nHostPort
== INTERNET_INVALID_PORT_NUMBER
)
2689 lpwhs
->nHostPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
2690 INTERNET_DEFAULT_HTTPS_PORT
:
2691 INTERNET_DEFAULT_HTTP_PORT
);
2693 if (NULL
!= hIC
->lpszProxy
&& hIC
->lpszProxy
[0] != 0)
2694 HTTP_DealWithProxy( hIC
, lpwhs
, lpwhr
);
2696 INTERNET_SendCallback(&lpwhs
->hdr
, dwContext
,
2697 INTERNET_STATUS_HANDLE_CREATED
, &handle
,
2701 HeapFree(GetProcessHeap(), 0, lpszHostName
);
2703 WININET_Release( &lpwhr
->hdr
);
2705 TRACE("<-- %p (%p)\n", handle
, lpwhr
);
2710 /***********************************************************************
2711 * HttpOpenRequestW (WININET.@)
2713 * Open a HTTP request handle
2716 * HINTERNET a HTTP request handle on success
2720 HINTERNET WINAPI
HttpOpenRequestW(HINTERNET hHttpSession
,
2721 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
2722 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
2723 DWORD dwFlags
, DWORD_PTR dwContext
)
2725 http_session_t
*lpwhs
;
2726 HINTERNET handle
= NULL
;
2729 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
2730 debugstr_w(lpszVerb
), debugstr_w(lpszObjectName
),
2731 debugstr_w(lpszVersion
), debugstr_w(lpszReferrer
), lpszAcceptTypes
,
2732 dwFlags
, dwContext
);
2733 if(lpszAcceptTypes
!=NULL
)
2736 for(i
=0;lpszAcceptTypes
[i
]!=NULL
;i
++)
2737 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes
[i
]));
2740 lpwhs
= (http_session_t
*) WININET_GetObject( hHttpSession
);
2741 if (NULL
== lpwhs
|| lpwhs
->hdr
.htype
!= WH_HHTTPSESSION
)
2743 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
2748 * My tests seem to show that the windows version does not
2749 * become asynchronous until after this point. And anyhow
2750 * if this call was asynchronous then how would you get the
2751 * necessary HINTERNET pointer returned by this function.
2754 res
= HTTP_HttpOpenRequestW(lpwhs
, lpszVerb
, lpszObjectName
,
2755 lpszVersion
, lpszReferrer
, lpszAcceptTypes
,
2756 dwFlags
, dwContext
, &handle
);
2759 WININET_Release( &lpwhs
->hdr
);
2760 TRACE("returning %p\n", handle
);
2761 if(res
!= ERROR_SUCCESS
)
2766 /* read any content returned by the server so that the connection can be
2768 static void HTTP_DrainContent(http_request_t
*req
)
2772 if (!NETCON_connected(&req
->netConnection
)) return;
2774 if (req
->dwContentLength
== -1)
2776 NETCON_close(&req
->netConnection
);
2779 if (!strcmpW(req
->lpszVerb
, szHEAD
)) return;
2784 if (HTTPREQ_Read(req
, buffer
, sizeof(buffer
), &bytes_read
, TRUE
) != ERROR_SUCCESS
)
2786 } while (bytes_read
);
2789 static const LPCWSTR header_lookup
[] = {
2790 szMime_Version
, /* HTTP_QUERY_MIME_VERSION = 0 */
2791 szContent_Type
, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2792 szContent_Transfer_Encoding
,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2793 szContent_ID
, /* HTTP_QUERY_CONTENT_ID = 3 */
2794 NULL
, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2795 szContent_Length
, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2796 szContent_Language
, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2797 szAllow
, /* HTTP_QUERY_ALLOW = 7 */
2798 szPublic
, /* HTTP_QUERY_PUBLIC = 8 */
2799 szDate
, /* HTTP_QUERY_DATE = 9 */
2800 szExpires
, /* HTTP_QUERY_EXPIRES = 10 */
2801 szLast_Modified
, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2802 NULL
, /* HTTP_QUERY_MESSAGE_ID = 12 */
2803 szURI
, /* HTTP_QUERY_URI = 13 */
2804 szFrom
, /* HTTP_QUERY_DERIVED_FROM = 14 */
2805 NULL
, /* HTTP_QUERY_COST = 15 */
2806 NULL
, /* HTTP_QUERY_LINK = 16 */
2807 szPragma
, /* HTTP_QUERY_PRAGMA = 17 */
2808 NULL
, /* HTTP_QUERY_VERSION = 18 */
2809 szStatus
, /* HTTP_QUERY_STATUS_CODE = 19 */
2810 NULL
, /* HTTP_QUERY_STATUS_TEXT = 20 */
2811 NULL
, /* HTTP_QUERY_RAW_HEADERS = 21 */
2812 NULL
, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2813 szConnection
, /* HTTP_QUERY_CONNECTION = 23 */
2814 szAccept
, /* HTTP_QUERY_ACCEPT = 24 */
2815 szAccept_Charset
, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2816 szAccept_Encoding
, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2817 szAccept_Language
, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2818 szAuthorization
, /* HTTP_QUERY_AUTHORIZATION = 28 */
2819 szContent_Encoding
, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2820 NULL
, /* HTTP_QUERY_FORWARDED = 30 */
2821 NULL
, /* HTTP_QUERY_FROM = 31 */
2822 szIf_Modified_Since
, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2823 szLocation
, /* HTTP_QUERY_LOCATION = 33 */
2824 NULL
, /* HTTP_QUERY_ORIG_URI = 34 */
2825 szReferer
, /* HTTP_QUERY_REFERER = 35 */
2826 szRetry_After
, /* HTTP_QUERY_RETRY_AFTER = 36 */
2827 szServer
, /* HTTP_QUERY_SERVER = 37 */
2828 NULL
, /* HTTP_TITLE = 38 */
2829 szUser_Agent
, /* HTTP_QUERY_USER_AGENT = 39 */
2830 szWWW_Authenticate
, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2831 szProxy_Authenticate
, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2832 szAccept_Ranges
, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2833 szSet_Cookie
, /* HTTP_QUERY_SET_COOKIE = 43 */
2834 szCookie
, /* HTTP_QUERY_COOKIE = 44 */
2835 NULL
, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2836 NULL
, /* HTTP_QUERY_REFRESH = 46 */
2837 NULL
, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2838 szAge
, /* HTTP_QUERY_AGE = 48 */
2839 szCache_Control
, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2840 szContent_Base
, /* HTTP_QUERY_CONTENT_BASE = 50 */
2841 szContent_Location
, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2842 szContent_MD5
, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2843 szContent_Range
, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2844 szETag
, /* HTTP_QUERY_ETAG = 54 */
2845 hostW
, /* HTTP_QUERY_HOST = 55 */
2846 szIf_Match
, /* HTTP_QUERY_IF_MATCH = 56 */
2847 szIf_None_Match
, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2848 szIf_Range
, /* HTTP_QUERY_IF_RANGE = 58 */
2849 szIf_Unmodified_Since
, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2850 szMax_Forwards
, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2851 szProxy_Authorization
, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2852 szRange
, /* HTTP_QUERY_RANGE = 62 */
2853 szTransfer_Encoding
, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2854 szUpgrade
, /* HTTP_QUERY_UPGRADE = 64 */
2855 szVary
, /* HTTP_QUERY_VARY = 65 */
2856 szVia
, /* HTTP_QUERY_VIA = 66 */
2857 szWarning
, /* HTTP_QUERY_WARNING = 67 */
2858 szExpect
, /* HTTP_QUERY_EXPECT = 68 */
2859 szProxy_Connection
, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2860 szUnless_Modified_Since
, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2863 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2865 /***********************************************************************
2866 * HTTP_HttpQueryInfoW (internal)
2868 static DWORD
HTTP_HttpQueryInfoW(http_request_t
*lpwhr
, DWORD dwInfoLevel
,
2869 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
2871 LPHTTPHEADERW lphttpHdr
= NULL
;
2872 BOOL request_only
= dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
;
2873 INT requested_index
= lpdwIndex
? *lpdwIndex
: 0;
2874 DWORD level
= (dwInfoLevel
& ~HTTP_QUERY_MODIFIER_FLAGS_MASK
);
2877 /* Find requested header structure */
2880 case HTTP_QUERY_CUSTOM
:
2881 if (!lpBuffer
) return ERROR_INVALID_PARAMETER
;
2882 index
= HTTP_GetCustomHeaderIndex(lpwhr
, lpBuffer
, requested_index
, request_only
);
2884 case HTTP_QUERY_RAW_HEADERS_CRLF
:
2888 DWORD res
= ERROR_INVALID_PARAMETER
;
2891 headers
= HTTP_BuildHeaderRequestString(lpwhr
, lpwhr
->lpszVerb
, lpwhr
->lpszPath
, lpwhr
->lpszVersion
);
2893 headers
= lpwhr
->lpszRawHeaders
;
2896 len
= strlenW(headers
) * sizeof(WCHAR
);
2898 if (len
+ sizeof(WCHAR
) > *lpdwBufferLength
)
2900 len
+= sizeof(WCHAR
);
2901 res
= ERROR_INSUFFICIENT_BUFFER
;
2906 memcpy(lpBuffer
, headers
, len
+ sizeof(WCHAR
));
2909 len
= strlenW(szCrLf
) * sizeof(WCHAR
);
2910 memcpy(lpBuffer
, szCrLf
, sizeof(szCrLf
));
2912 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
/ sizeof(WCHAR
)));
2913 res
= ERROR_SUCCESS
;
2915 *lpdwBufferLength
= len
;
2918 HeapFree(GetProcessHeap(), 0, headers
);
2921 case HTTP_QUERY_RAW_HEADERS
:
2923 LPWSTR
* ppszRawHeaderLines
= HTTP_Tokenize(lpwhr
->lpszRawHeaders
, szCrLf
);
2925 LPWSTR pszString
= lpBuffer
;
2927 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
2928 size
+= strlenW(ppszRawHeaderLines
[i
]) + 1;
2930 if (size
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
2932 HTTP_FreeTokens(ppszRawHeaderLines
);
2933 *lpdwBufferLength
= (size
+ 1) * sizeof(WCHAR
);
2934 return ERROR_INSUFFICIENT_BUFFER
;
2938 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
2940 DWORD len
= strlenW(ppszRawHeaderLines
[i
]);
2941 memcpy(pszString
, ppszRawHeaderLines
[i
], (len
+1)*sizeof(WCHAR
));
2945 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, size
));
2947 *lpdwBufferLength
= size
* sizeof(WCHAR
);
2948 HTTP_FreeTokens(ppszRawHeaderLines
);
2950 return ERROR_SUCCESS
;
2952 case HTTP_QUERY_STATUS_TEXT
:
2953 if (lpwhr
->lpszStatusText
)
2955 DWORD len
= strlenW(lpwhr
->lpszStatusText
);
2956 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
2958 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
2959 return ERROR_INSUFFICIENT_BUFFER
;
2963 memcpy(lpBuffer
, lpwhr
->lpszStatusText
, (len
+ 1) * sizeof(WCHAR
));
2964 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
2966 *lpdwBufferLength
= len
* sizeof(WCHAR
);
2967 return ERROR_SUCCESS
;
2970 case HTTP_QUERY_VERSION
:
2971 if (lpwhr
->lpszVersion
)
2973 DWORD len
= strlenW(lpwhr
->lpszVersion
);
2974 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
2976 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
2977 return ERROR_INSUFFICIENT_BUFFER
;
2981 memcpy(lpBuffer
, lpwhr
->lpszVersion
, (len
+ 1) * sizeof(WCHAR
));
2982 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
2984 *lpdwBufferLength
= len
* sizeof(WCHAR
);
2985 return ERROR_SUCCESS
;
2988 case HTTP_QUERY_CONTENT_ENCODING
:
2989 index
= HTTP_GetCustomHeaderIndex(lpwhr
, header_lookup
[lpwhr
->gzip_stream
? HTTP_QUERY_CONTENT_TYPE
: level
],
2990 requested_index
,request_only
);
2993 assert (LAST_TABLE_HEADER
== (HTTP_QUERY_UNLESS_MODIFIED_SINCE
+ 1));
2995 if (level
< LAST_TABLE_HEADER
&& header_lookup
[level
])
2996 index
= HTTP_GetCustomHeaderIndex(lpwhr
, header_lookup
[level
],
2997 requested_index
,request_only
);
3001 lphttpHdr
= &lpwhr
->pCustHeaders
[index
];
3003 /* Ensure header satisfies requested attributes */
3005 ((dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
) &&
3006 (~lphttpHdr
->wFlags
& HDR_ISREQUEST
)))
3008 return ERROR_HTTP_HEADER_NOT_FOUND
;
3011 if (lpdwIndex
&& level
!= HTTP_QUERY_STATUS_CODE
) (*lpdwIndex
)++;
3013 /* coalesce value to requested type */
3014 if (dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
&& lpBuffer
)
3016 *(int *)lpBuffer
= atoiW(lphttpHdr
->lpszValue
);
3017 TRACE(" returning number: %d\n", *(int *)lpBuffer
);
3019 else if (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
&& lpBuffer
)
3025 tmpTime
= ConvertTimeString(lphttpHdr
->lpszValue
);
3027 tmpTM
= *gmtime(&tmpTime
);
3028 STHook
= (SYSTEMTIME
*)lpBuffer
;
3029 STHook
->wDay
= tmpTM
.tm_mday
;
3030 STHook
->wHour
= tmpTM
.tm_hour
;
3031 STHook
->wMilliseconds
= 0;
3032 STHook
->wMinute
= tmpTM
.tm_min
;
3033 STHook
->wDayOfWeek
= tmpTM
.tm_wday
;
3034 STHook
->wMonth
= tmpTM
.tm_mon
+ 1;
3035 STHook
->wSecond
= tmpTM
.tm_sec
;
3036 STHook
->wYear
= tmpTM
.tm_year
;
3038 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3039 STHook
->wYear
, STHook
->wMonth
, STHook
->wDay
, STHook
->wDayOfWeek
,
3040 STHook
->wHour
, STHook
->wMinute
, STHook
->wSecond
, STHook
->wMilliseconds
);
3042 else if (lphttpHdr
->lpszValue
)
3044 DWORD len
= (strlenW(lphttpHdr
->lpszValue
) + 1) * sizeof(WCHAR
);
3046 if (len
> *lpdwBufferLength
)
3048 *lpdwBufferLength
= len
;
3049 return ERROR_INSUFFICIENT_BUFFER
;
3053 memcpy(lpBuffer
, lphttpHdr
->lpszValue
, len
);
3054 TRACE("! returning string: %s\n", debugstr_w(lpBuffer
));
3056 *lpdwBufferLength
= len
- sizeof(WCHAR
);
3058 return ERROR_SUCCESS
;
3061 /***********************************************************************
3062 * HttpQueryInfoW (WININET.@)
3064 * Queries for information about an HTTP request
3071 BOOL WINAPI
HttpQueryInfoW(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3072 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3074 http_request_t
*lpwhr
;
3077 if (TRACE_ON(wininet
)) {
3078 #define FE(x) { x, #x }
3079 static const wininet_flag_info query_flags
[] = {
3080 FE(HTTP_QUERY_MIME_VERSION
),
3081 FE(HTTP_QUERY_CONTENT_TYPE
),
3082 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING
),
3083 FE(HTTP_QUERY_CONTENT_ID
),
3084 FE(HTTP_QUERY_CONTENT_DESCRIPTION
),
3085 FE(HTTP_QUERY_CONTENT_LENGTH
),
3086 FE(HTTP_QUERY_CONTENT_LANGUAGE
),
3087 FE(HTTP_QUERY_ALLOW
),
3088 FE(HTTP_QUERY_PUBLIC
),
3089 FE(HTTP_QUERY_DATE
),
3090 FE(HTTP_QUERY_EXPIRES
),
3091 FE(HTTP_QUERY_LAST_MODIFIED
),
3092 FE(HTTP_QUERY_MESSAGE_ID
),
3094 FE(HTTP_QUERY_DERIVED_FROM
),
3095 FE(HTTP_QUERY_COST
),
3096 FE(HTTP_QUERY_LINK
),
3097 FE(HTTP_QUERY_PRAGMA
),
3098 FE(HTTP_QUERY_VERSION
),
3099 FE(HTTP_QUERY_STATUS_CODE
),
3100 FE(HTTP_QUERY_STATUS_TEXT
),
3101 FE(HTTP_QUERY_RAW_HEADERS
),
3102 FE(HTTP_QUERY_RAW_HEADERS_CRLF
),
3103 FE(HTTP_QUERY_CONNECTION
),
3104 FE(HTTP_QUERY_ACCEPT
),
3105 FE(HTTP_QUERY_ACCEPT_CHARSET
),
3106 FE(HTTP_QUERY_ACCEPT_ENCODING
),
3107 FE(HTTP_QUERY_ACCEPT_LANGUAGE
),
3108 FE(HTTP_QUERY_AUTHORIZATION
),
3109 FE(HTTP_QUERY_CONTENT_ENCODING
),
3110 FE(HTTP_QUERY_FORWARDED
),
3111 FE(HTTP_QUERY_FROM
),
3112 FE(HTTP_QUERY_IF_MODIFIED_SINCE
),
3113 FE(HTTP_QUERY_LOCATION
),
3114 FE(HTTP_QUERY_ORIG_URI
),
3115 FE(HTTP_QUERY_REFERER
),
3116 FE(HTTP_QUERY_RETRY_AFTER
),
3117 FE(HTTP_QUERY_SERVER
),
3118 FE(HTTP_QUERY_TITLE
),
3119 FE(HTTP_QUERY_USER_AGENT
),
3120 FE(HTTP_QUERY_WWW_AUTHENTICATE
),
3121 FE(HTTP_QUERY_PROXY_AUTHENTICATE
),
3122 FE(HTTP_QUERY_ACCEPT_RANGES
),
3123 FE(HTTP_QUERY_SET_COOKIE
),
3124 FE(HTTP_QUERY_COOKIE
),
3125 FE(HTTP_QUERY_REQUEST_METHOD
),
3126 FE(HTTP_QUERY_REFRESH
),
3127 FE(HTTP_QUERY_CONTENT_DISPOSITION
),
3129 FE(HTTP_QUERY_CACHE_CONTROL
),
3130 FE(HTTP_QUERY_CONTENT_BASE
),
3131 FE(HTTP_QUERY_CONTENT_LOCATION
),
3132 FE(HTTP_QUERY_CONTENT_MD5
),
3133 FE(HTTP_QUERY_CONTENT_RANGE
),
3134 FE(HTTP_QUERY_ETAG
),
3135 FE(HTTP_QUERY_HOST
),
3136 FE(HTTP_QUERY_IF_MATCH
),
3137 FE(HTTP_QUERY_IF_NONE_MATCH
),
3138 FE(HTTP_QUERY_IF_RANGE
),
3139 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE
),
3140 FE(HTTP_QUERY_MAX_FORWARDS
),
3141 FE(HTTP_QUERY_PROXY_AUTHORIZATION
),
3142 FE(HTTP_QUERY_RANGE
),
3143 FE(HTTP_QUERY_TRANSFER_ENCODING
),
3144 FE(HTTP_QUERY_UPGRADE
),
3145 FE(HTTP_QUERY_VARY
),
3147 FE(HTTP_QUERY_WARNING
),
3148 FE(HTTP_QUERY_CUSTOM
)
3150 static const wininet_flag_info modifier_flags
[] = {
3151 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS
),
3152 FE(HTTP_QUERY_FLAG_SYSTEMTIME
),
3153 FE(HTTP_QUERY_FLAG_NUMBER
),
3154 FE(HTTP_QUERY_FLAG_COALESCE
)
3157 DWORD info_mod
= dwInfoLevel
& HTTP_QUERY_MODIFIER_FLAGS_MASK
;
3158 DWORD info
= dwInfoLevel
& HTTP_QUERY_HEADER_MASK
;
3161 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest
, dwInfoLevel
, info
);
3162 TRACE(" Attribute:");
3163 for (i
= 0; i
< (sizeof(query_flags
) / sizeof(query_flags
[0])); i
++) {
3164 if (query_flags
[i
].val
== info
) {
3165 TRACE(" %s", query_flags
[i
].name
);
3169 if (i
== (sizeof(query_flags
) / sizeof(query_flags
[0]))) {
3170 TRACE(" Unknown (%08x)", info
);
3173 TRACE(" Modifier:");
3174 for (i
= 0; i
< (sizeof(modifier_flags
) / sizeof(modifier_flags
[0])); i
++) {
3175 if (modifier_flags
[i
].val
& info_mod
) {
3176 TRACE(" %s", modifier_flags
[i
].name
);
3177 info_mod
&= ~ modifier_flags
[i
].val
;
3182 TRACE(" Unknown (%08x)", info_mod
);
3187 lpwhr
= (http_request_t
*) WININET_GetObject( hHttpRequest
);
3188 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
3190 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
3194 if (lpBuffer
== NULL
)
3195 *lpdwBufferLength
= 0;
3196 res
= HTTP_HttpQueryInfoW( lpwhr
, dwInfoLevel
,
3197 lpBuffer
, lpdwBufferLength
, lpdwIndex
);
3201 WININET_Release( &lpwhr
->hdr
);
3203 TRACE("%u <--\n", res
);
3204 if(res
!= ERROR_SUCCESS
)
3206 return res
== ERROR_SUCCESS
;
3209 /***********************************************************************
3210 * HttpQueryInfoA (WININET.@)
3212 * Queries for information about an HTTP request
3219 BOOL WINAPI
HttpQueryInfoA(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3220 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3226 if((dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
) ||
3227 (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
))
3229 return HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, lpBuffer
,
3230 lpdwBufferLength
, lpdwIndex
);
3236 len
= (*lpdwBufferLength
)*sizeof(WCHAR
);
3237 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3239 alloclen
= MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, NULL
, 0 ) * sizeof(WCHAR
);
3245 bufferW
= HeapAlloc( GetProcessHeap(), 0, alloclen
);
3246 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3247 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3248 MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, bufferW
, alloclen
/ sizeof(WCHAR
) );
3255 result
= HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, bufferW
,
3259 len
= WideCharToMultiByte( CP_ACP
,0, bufferW
, len
/ sizeof(WCHAR
) + 1,
3260 lpBuffer
, *lpdwBufferLength
, NULL
, NULL
);
3261 *lpdwBufferLength
= len
- 1;
3263 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer
));
3266 /* since the strings being returned from HttpQueryInfoW should be
3267 * only ASCII characters, it is reasonable to assume that all of
3268 * the Unicode characters can be reduced to a single byte */
3269 *lpdwBufferLength
= len
/ sizeof(WCHAR
);
3271 HeapFree(GetProcessHeap(), 0, bufferW
);
3276 /***********************************************************************
3277 * HTTP_GetRedirectURL (internal)
3279 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*lpwhr
, LPCWSTR lpszUrl
)
3281 static WCHAR szHttp
[] = {'h','t','t','p',0};
3282 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3283 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
3284 URL_COMPONENTSW urlComponents
;
3285 DWORD url_length
= 0;
3287 LPWSTR combined_url
;
3289 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3290 urlComponents
.lpszScheme
= (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) ? szHttps
: szHttp
;
3291 urlComponents
.dwSchemeLength
= 0;
3292 urlComponents
.lpszHostName
= lpwhs
->lpszHostName
;
3293 urlComponents
.dwHostNameLength
= 0;
3294 urlComponents
.nPort
= lpwhs
->nHostPort
;
3295 urlComponents
.lpszUserName
= lpwhs
->lpszUserName
;
3296 urlComponents
.dwUserNameLength
= 0;
3297 urlComponents
.lpszPassword
= NULL
;
3298 urlComponents
.dwPasswordLength
= 0;
3299 urlComponents
.lpszUrlPath
= lpwhr
->lpszPath
;
3300 urlComponents
.dwUrlPathLength
= 0;
3301 urlComponents
.lpszExtraInfo
= NULL
;
3302 urlComponents
.dwExtraInfoLength
= 0;
3304 if (!InternetCreateUrlW(&urlComponents
, 0, NULL
, &url_length
) &&
3305 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3308 orig_url
= HeapAlloc(GetProcessHeap(), 0, url_length
);
3310 /* convert from bytes to characters */
3311 url_length
= url_length
/ sizeof(WCHAR
) - 1;
3312 if (!InternetCreateUrlW(&urlComponents
, 0, orig_url
, &url_length
))
3314 HeapFree(GetProcessHeap(), 0, orig_url
);
3319 if (!InternetCombineUrlW(orig_url
, lpszUrl
, NULL
, &url_length
, ICU_ENCODE_SPACES_ONLY
) &&
3320 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3322 HeapFree(GetProcessHeap(), 0, orig_url
);
3325 combined_url
= HeapAlloc(GetProcessHeap(), 0, url_length
* sizeof(WCHAR
));
3327 if (!InternetCombineUrlW(orig_url
, lpszUrl
, combined_url
, &url_length
, ICU_ENCODE_SPACES_ONLY
))
3329 HeapFree(GetProcessHeap(), 0, orig_url
);
3330 HeapFree(GetProcessHeap(), 0, combined_url
);
3333 HeapFree(GetProcessHeap(), 0, orig_url
);
3334 return combined_url
;
3338 /***********************************************************************
3339 * HTTP_HandleRedirect (internal)
3341 static DWORD
HTTP_HandleRedirect(http_request_t
*lpwhr
, LPCWSTR lpszUrl
)
3343 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
3344 appinfo_t
*hIC
= lpwhs
->lpAppInfo
;
3345 BOOL using_proxy
= hIC
->lpszProxy
&& hIC
->lpszProxy
[0];
3346 WCHAR path
[INTERNET_MAX_URL_LENGTH
];
3351 /* if it's an absolute path, keep the same session info */
3352 lstrcpynW(path
, lpszUrl
, INTERNET_MAX_URL_LENGTH
);
3356 URL_COMPONENTSW urlComponents
;
3357 WCHAR protocol
[32], hostName
[MAXHOSTNAME
], userName
[1024];
3358 static WCHAR szHttp
[] = {'h','t','t','p',0};
3359 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3365 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3366 urlComponents
.lpszScheme
= protocol
;
3367 urlComponents
.dwSchemeLength
= 32;
3368 urlComponents
.lpszHostName
= hostName
;
3369 urlComponents
.dwHostNameLength
= MAXHOSTNAME
;
3370 urlComponents
.lpszUserName
= userName
;
3371 urlComponents
.dwUserNameLength
= 1024;
3372 urlComponents
.lpszPassword
= NULL
;
3373 urlComponents
.dwPasswordLength
= 0;
3374 urlComponents
.lpszUrlPath
= path
;
3375 urlComponents
.dwUrlPathLength
= 2048;
3376 urlComponents
.lpszExtraInfo
= NULL
;
3377 urlComponents
.dwExtraInfoLength
= 0;
3378 if(!InternetCrackUrlW(lpszUrl
, strlenW(lpszUrl
), 0, &urlComponents
))
3379 return INTERNET_GetLastError();
3381 if (!strncmpW(szHttp
, urlComponents
.lpszScheme
, strlenW(szHttp
)) &&
3382 (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
))
3384 TRACE("redirect from secure page to non-secure page\n");
3385 /* FIXME: warn about from secure redirect to non-secure page */
3386 lpwhr
->hdr
.dwFlags
&= ~INTERNET_FLAG_SECURE
;
3388 if (!strncmpW(szHttps
, urlComponents
.lpszScheme
, strlenW(szHttps
)) &&
3389 !(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
))
3391 TRACE("redirect from non-secure page to secure page\n");
3392 /* FIXME: notify about redirect to secure page */
3393 lpwhr
->hdr
.dwFlags
|= INTERNET_FLAG_SECURE
;
3396 if (urlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
3398 if (lstrlenW(protocol
)>4) /*https*/
3399 urlComponents
.nPort
= INTERNET_DEFAULT_HTTPS_PORT
;
3401 urlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
3406 * This upsets redirects to binary files on sourceforge.net
3407 * and gives an html page instead of the target file
3408 * Examination of the HTTP request sent by native wininet.dll
3409 * reveals that it doesn't send a referrer in that case.
3410 * Maybe there's a flag that enables this, or maybe a referrer
3411 * shouldn't be added in case of a redirect.
3414 /* consider the current host as the referrer */
3415 if (lpwhs
->lpszServerName
&& *lpwhs
->lpszServerName
)
3416 HTTP_ProcessHeader(lpwhr
, HTTP_REFERER
, lpwhs
->lpszServerName
,
3417 HTTP_ADDHDR_FLAG_REQ
|HTTP_ADDREQ_FLAG_REPLACE
|
3418 HTTP_ADDHDR_FLAG_ADD_IF_NEW
);
3421 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszHostName
);
3422 if (urlComponents
.nPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
3423 urlComponents
.nPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
3426 static const WCHAR fmt
[] = {'%','s',':','%','i',0};
3427 len
= lstrlenW(hostName
);
3428 len
+= 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3429 lpwhs
->lpszHostName
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
3430 sprintfW(lpwhs
->lpszHostName
, fmt
, hostName
, urlComponents
.nPort
);
3433 lpwhs
->lpszHostName
= heap_strdupW(hostName
);
3435 HTTP_ProcessHeader(lpwhr
, hostW
, lpwhs
->lpszHostName
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDREQ_FLAG_REPLACE
| HTTP_ADDHDR_FLAG_REQ
);
3437 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszUserName
);
3438 lpwhs
->lpszUserName
= NULL
;
3440 lpwhs
->lpszUserName
= heap_strdupW(userName
);
3444 if (strcmpiW(lpwhs
->lpszServerName
, hostName
) || lpwhs
->nServerPort
!= urlComponents
.nPort
)
3448 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszServerName
);
3449 lpwhs
->lpszServerName
= heap_strdupW(hostName
);
3450 lpwhs
->nServerPort
= urlComponents
.nPort
;
3452 NETCON_close(&lpwhr
->netConnection
);
3453 if ((res
= HTTP_ResolveName(lpwhr
)) != ERROR_SUCCESS
)
3456 res
= NETCON_init(&lpwhr
->netConnection
, lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
);
3457 if (res
!= ERROR_SUCCESS
)
3460 lpwhr
->read_pos
= lpwhr
->read_size
= 0;
3461 lpwhr
->read_chunked
= FALSE
;
3465 TRACE("Redirect through proxy\n");
3468 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszPath
);
3469 lpwhr
->lpszPath
=NULL
;
3475 rc
= UrlEscapeW(path
, NULL
, &needed
, URL_ESCAPE_SPACES_ONLY
);
3476 if (rc
!= E_POINTER
)
3477 needed
= strlenW(path
)+1;
3478 lpwhr
->lpszPath
= HeapAlloc(GetProcessHeap(), 0, needed
*sizeof(WCHAR
));
3479 rc
= UrlEscapeW(path
, lpwhr
->lpszPath
, &needed
,
3480 URL_ESCAPE_SPACES_ONLY
);
3483 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path
),rc
);
3484 strcpyW(lpwhr
->lpszPath
,path
);
3488 /* Remove custom content-type/length headers on redirects. */
3489 index
= HTTP_GetCustomHeaderIndex(lpwhr
, szContent_Type
, 0, TRUE
);
3491 HTTP_DeleteCustomHeader(lpwhr
, index
);
3492 index
= HTTP_GetCustomHeaderIndex(lpwhr
, szContent_Length
, 0, TRUE
);
3494 HTTP_DeleteCustomHeader(lpwhr
, index
);
3496 return ERROR_SUCCESS
;
3499 /***********************************************************************
3500 * HTTP_build_req (internal)
3502 * concatenate all the strings in the request together
3504 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
)
3509 for( t
= list
; *t
; t
++ )
3510 len
+= strlenW( *t
);
3513 str
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
3516 for( t
= list
; *t
; t
++ )
3522 static DWORD
HTTP_SecureProxyConnect(http_request_t
*lpwhr
)
3525 LPWSTR requestString
;
3531 static const WCHAR szConnect
[] = {'C','O','N','N','E','C','T',0};
3532 static const WCHAR szFormat
[] = {'%','s',':','%','d',0};
3533 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
3537 lpszPath
= HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs
->lpszHostName
) + 13)*sizeof(WCHAR
) );
3538 sprintfW( lpszPath
, szFormat
, lpwhs
->lpszHostName
, lpwhs
->nHostPort
);
3539 requestString
= HTTP_BuildHeaderRequestString( lpwhr
, szConnect
, lpszPath
, g_szHttp1_1
);
3540 HeapFree( GetProcessHeap(), 0, lpszPath
);
3542 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3543 NULL
, 0, NULL
, NULL
);
3544 len
--; /* the nul terminator isn't needed */
3545 ascii_req
= HeapAlloc( GetProcessHeap(), 0, len
);
3546 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3547 ascii_req
, len
, NULL
, NULL
);
3548 HeapFree( GetProcessHeap(), 0, requestString
);
3550 TRACE("full request -> %s\n", debugstr_an( ascii_req
, len
) );
3552 res
= NETCON_send( &lpwhr
->netConnection
, ascii_req
, len
, 0, &cnt
);
3553 HeapFree( GetProcessHeap(), 0, ascii_req
);
3554 if (res
!= ERROR_SUCCESS
)
3557 responseLen
= HTTP_GetResponseHeaders( lpwhr
, TRUE
);
3559 return ERROR_HTTP_INVALID_HEADER
;
3561 return ERROR_SUCCESS
;
3564 static void HTTP_InsertCookies(http_request_t
*lpwhr
)
3566 static const WCHAR szUrlForm
[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3567 LPWSTR lpszCookies
, lpszUrl
= NULL
;
3568 DWORD nCookieSize
, size
;
3569 LPHTTPHEADERW Host
= HTTP_GetHeader(lpwhr
, hostW
);
3571 size
= (strlenW(Host
->lpszValue
) + strlenW(szUrlForm
) + strlenW(lpwhr
->lpszPath
)) * sizeof(WCHAR
);
3572 if (!(lpszUrl
= HeapAlloc(GetProcessHeap(), 0, size
))) return;
3573 sprintfW( lpszUrl
, szUrlForm
, Host
->lpszValue
, lpwhr
->lpszPath
);
3575 if (InternetGetCookieW(lpszUrl
, NULL
, NULL
, &nCookieSize
))
3578 static const WCHAR szCookie
[] = {'C','o','o','k','i','e',':',' ',0};
3580 size
= sizeof(szCookie
) + nCookieSize
* sizeof(WCHAR
) + sizeof(szCrLf
);
3581 if ((lpszCookies
= HeapAlloc(GetProcessHeap(), 0, size
)))
3583 cnt
+= sprintfW(lpszCookies
, szCookie
);
3584 InternetGetCookieW(lpszUrl
, NULL
, lpszCookies
+ cnt
, &nCookieSize
);
3585 strcatW(lpszCookies
, szCrLf
);
3587 HTTP_HttpAddRequestHeadersW(lpwhr
, lpszCookies
, strlenW(lpszCookies
), HTTP_ADDREQ_FLAG_REPLACE
);
3588 HeapFree(GetProcessHeap(), 0, lpszCookies
);
3591 HeapFree(GetProcessHeap(), 0, lpszUrl
);
3594 /***********************************************************************
3595 * HTTP_HttpSendRequestW (internal)
3597 * Sends the specified request to the HTTP server
3604 static DWORD
HTTP_HttpSendRequestW(http_request_t
*lpwhr
, LPCWSTR lpszHeaders
,
3605 DWORD dwHeaderLength
, LPVOID lpOptional
, DWORD dwOptionalLength
,
3606 DWORD dwContentLength
, BOOL bEndRequest
)
3609 BOOL redirected
= FALSE
;
3610 LPWSTR requestString
= NULL
;
3613 INTERNET_ASYNC_RESULT iar
;
3614 static const WCHAR szPost
[] = { 'P','O','S','T',0 };
3615 static const WCHAR szContentLength
[] =
3616 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3617 WCHAR contentLengthStr
[sizeof szContentLength
/2 /* includes \r\n */ + 20 /* int */ ];
3620 TRACE("--> %p\n", lpwhr
);
3622 assert(lpwhr
->hdr
.htype
== WH_HHTTPREQ
);
3624 /* if the verb is NULL default to GET */
3625 if (!lpwhr
->lpszVerb
)
3626 lpwhr
->lpszVerb
= heap_strdupW(szGET
);
3628 if (dwContentLength
|| strcmpW(lpwhr
->lpszVerb
, szGET
))
3630 sprintfW(contentLengthStr
, szContentLength
, dwContentLength
);
3631 HTTP_HttpAddRequestHeadersW(lpwhr
, contentLengthStr
, -1L, HTTP_ADDREQ_FLAG_REPLACE
);
3632 lpwhr
->dwBytesToWrite
= dwContentLength
;
3634 if (lpwhr
->lpHttpSession
->lpAppInfo
->lpszAgent
)
3636 WCHAR
*agent_header
;
3637 static const WCHAR user_agent
[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3640 len
= strlenW(lpwhr
->lpHttpSession
->lpAppInfo
->lpszAgent
) + strlenW(user_agent
);
3641 agent_header
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
3642 sprintfW(agent_header
, user_agent
, lpwhr
->lpHttpSession
->lpAppInfo
->lpszAgent
);
3644 HTTP_HttpAddRequestHeadersW(lpwhr
, agent_header
, strlenW(agent_header
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
3645 HeapFree(GetProcessHeap(), 0, agent_header
);
3647 if (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_PRAGMA_NOCACHE
)
3649 static const WCHAR pragma_nocache
[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3650 HTTP_HttpAddRequestHeadersW(lpwhr
, pragma_nocache
, strlenW(pragma_nocache
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
3652 if ((lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_CACHE_WRITE
) && !strcmpW(lpwhr
->lpszVerb
, szPost
))
3654 static const WCHAR cache_control
[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3655 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3656 HTTP_HttpAddRequestHeadersW(lpwhr
, cache_control
, strlenW(cache_control
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
3662 BOOL reusing_connection
;
3667 /* like native, just in case the caller forgot to call InternetReadFile
3668 * for all the data */
3669 HTTP_DrainContent(lpwhr
);
3670 lpwhr
->dwContentRead
= 0;
3672 lpwhr
->dwContentLength
= ~0u;
3673 lpwhr
->dwBytesToWrite
= 0;
3676 if (TRACE_ON(wininet
))
3678 LPHTTPHEADERW Host
= HTTP_GetHeader(lpwhr
, hostW
);
3679 TRACE("Going to url %s %s\n", debugstr_w(Host
->lpszValue
), debugstr_w(lpwhr
->lpszPath
));
3683 if (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_KEEP_CONNECTION
)
3685 HTTP_ProcessHeader(lpwhr
, szConnection
, szKeepAlive
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
3687 HTTP_InsertAuthorization(lpwhr
, lpwhr
->pAuthInfo
, szAuthorization
);
3688 HTTP_InsertAuthorization(lpwhr
, lpwhr
->pProxyAuthInfo
, szProxy_Authorization
);
3690 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
))
3691 HTTP_InsertCookies(lpwhr
);
3693 /* add the headers the caller supplied */
3694 if( lpszHeaders
&& dwHeaderLength
)
3696 HTTP_HttpAddRequestHeadersW(lpwhr
, lpszHeaders
, dwHeaderLength
,
3697 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REPLACE
);
3700 if (lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxy
&& lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxy
[0])
3702 WCHAR
*url
= HTTP_BuildProxyRequestUrl(lpwhr
);
3703 requestString
= HTTP_BuildHeaderRequestString(lpwhr
, lpwhr
->lpszVerb
, url
, lpwhr
->lpszVersion
);
3704 HeapFree(GetProcessHeap(), 0, url
);
3707 requestString
= HTTP_BuildHeaderRequestString(lpwhr
, lpwhr
->lpszVerb
, lpwhr
->lpszPath
, lpwhr
->lpszVersion
);
3710 TRACE("Request header -> %s\n", debugstr_w(requestString
) );
3712 /* Send the request and store the results */
3713 if(NETCON_connected(&lpwhr
->netConnection
))
3714 reusing_connection
= TRUE
;
3716 reusing_connection
= FALSE
;
3718 if ((res
= HTTP_OpenConnection(lpwhr
)) != ERROR_SUCCESS
)
3721 /* send the request as ASCII, tack on the optional data */
3722 if (!lpOptional
|| redirected
)
3723 dwOptionalLength
= 0;
3724 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3725 NULL
, 0, NULL
, NULL
);
3726 ascii_req
= HeapAlloc( GetProcessHeap(), 0, len
+ dwOptionalLength
);
3727 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3728 ascii_req
, len
, NULL
, NULL
);
3730 memcpy( &ascii_req
[len
-1], lpOptional
, dwOptionalLength
);
3731 len
= (len
+ dwOptionalLength
- 1);
3733 TRACE("full request -> %s\n", debugstr_a(ascii_req
) );
3735 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3736 INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
3738 res
= NETCON_send(&lpwhr
->netConnection
, ascii_req
, len
, 0, &cnt
);
3739 HeapFree( GetProcessHeap(), 0, ascii_req
);
3741 lpwhr
->dwBytesWritten
= dwOptionalLength
;
3743 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3744 INTERNET_STATUS_REQUEST_SENT
,
3745 &len
, sizeof(DWORD
));
3752 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3753 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
3755 if (res
!= ERROR_SUCCESS
)
3758 responseLen
= HTTP_GetResponseHeaders(lpwhr
, TRUE
);
3759 /* FIXME: We should know that connection is closed before sending
3760 * headers. Otherwise wrong callbacks are executed */
3761 if(!responseLen
&& reusing_connection
) {
3762 TRACE("Connection closed by server, reconnecting\n");
3767 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3768 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
,
3771 HTTP_ProcessCookies(lpwhr
);
3773 if (!set_content_length( lpwhr
)) HTTP_FinishedReading(lpwhr
);
3775 dwBufferSize
= sizeof(dwStatusCode
);
3776 if (HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_STATUS_CODE
,
3777 &dwStatusCode
,&dwBufferSize
,NULL
) != ERROR_SUCCESS
)
3780 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
) && responseLen
)
3782 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
3783 dwBufferSize
=sizeof(szNewLocation
);
3784 if ((dwStatusCode
== HTTP_STATUS_REDIRECT
||
3785 dwStatusCode
== HTTP_STATUS_MOVED
||
3786 dwStatusCode
== HTTP_STATUS_REDIRECT_METHOD
) &&
3787 HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_LOCATION
,szNewLocation
,&dwBufferSize
,NULL
) == ERROR_SUCCESS
)
3789 if (strcmpW(lpwhr
->lpszVerb
, szGET
) && strcmpW(lpwhr
->lpszVerb
, szHEAD
))
3791 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVerb
);
3792 lpwhr
->lpszVerb
= heap_strdupW(szGET
);
3794 HTTP_DrainContent(lpwhr
);
3795 if ((new_url
= HTTP_GetRedirectURL( lpwhr
, szNewLocation
)))
3797 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
3798 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
3799 res
= HTTP_HandleRedirect(lpwhr
, new_url
);
3800 if (res
== ERROR_SUCCESS
)
3802 HeapFree(GetProcessHeap(), 0, requestString
);
3805 HeapFree( GetProcessHeap(), 0, new_url
);
3810 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTH
) && res
== ERROR_SUCCESS
)
3812 WCHAR szAuthValue
[2048];
3814 if (dwStatusCode
== HTTP_STATUS_DENIED
)
3816 LPHTTPHEADERW Host
= HTTP_GetHeader(lpwhr
, hostW
);
3818 while (HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_WWW_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
3820 if (HTTP_DoAuthorization(lpwhr
, szAuthValue
,
3822 lpwhr
->lpHttpSession
->lpszUserName
,
3823 lpwhr
->lpHttpSession
->lpszPassword
,
3826 HeapFree(GetProcessHeap(), 0, requestString
);
3833 TRACE("Cleaning wrong authorization data\n");
3834 destroy_authinfo(lpwhr
->pAuthInfo
);
3835 lpwhr
->pAuthInfo
= NULL
;
3838 if (dwStatusCode
== HTTP_STATUS_PROXY_AUTH_REQ
)
3841 while (HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_PROXY_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
3843 if (HTTP_DoAuthorization(lpwhr
, szAuthValue
,
3844 &lpwhr
->pProxyAuthInfo
,
3845 lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxyUsername
,
3846 lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxyPassword
,
3855 TRACE("Cleaning wrong proxy authorization data\n");
3856 destroy_authinfo(lpwhr
->pProxyAuthInfo
);
3857 lpwhr
->pProxyAuthInfo
= NULL
;
3863 res
= ERROR_SUCCESS
;
3867 if(res
== ERROR_SUCCESS
) {
3868 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
3869 WCHAR cacheFileName
[MAX_PATH
+1];
3872 b
= HTTP_GetRequestURL(lpwhr
, url
);
3874 WARN("Could not get URL\n");
3878 b
= CreateUrlCacheEntryW(url
, lpwhr
->dwContentLength
> 0 ? lpwhr
->dwContentLength
: 0, NULL
, cacheFileName
, 0);
3880 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszCacheFile
);
3881 CloseHandle(lpwhr
->hCacheFile
);
3883 lpwhr
->lpszCacheFile
= heap_strdupW(cacheFileName
);
3884 lpwhr
->hCacheFile
= CreateFileW(lpwhr
->lpszCacheFile
, GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
3885 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
3886 if(lpwhr
->hCacheFile
== INVALID_HANDLE_VALUE
) {
3887 WARN("Could not create file: %u\n", GetLastError());
3888 lpwhr
->hCacheFile
= NULL
;
3891 WARN("Could not create cache entry: %08x\n", GetLastError());
3897 HeapFree(GetProcessHeap(), 0, requestString
);
3899 /* TODO: send notification for P3P header */
3901 if (lpwhr
->lpHttpSession
->lpAppInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
3903 if (res
== ERROR_SUCCESS
&& lpwhr
->dwBytesWritten
== lpwhr
->dwBytesToWrite
)
3904 HTTP_ReceiveRequestData(lpwhr
, TRUE
);
3907 iar
.dwResult
= (res
==ERROR_SUCCESS
? (DWORD_PTR
)lpwhr
->hdr
.hInternet
: 0);
3910 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3911 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
3912 sizeof(INTERNET_ASYNC_RESULT
));
3920 /***********************************************************************
3922 * Helper functions for the HttpSendRequest(Ex) functions
3925 static void AsyncHttpSendRequestProc(WORKREQUEST
*workRequest
)
3927 struct WORKREQ_HTTPSENDREQUESTW
const *req
= &workRequest
->u
.HttpSendRequestW
;
3928 http_request_t
*lpwhr
= (http_request_t
*) workRequest
->hdr
;
3930 TRACE("%p\n", lpwhr
);
3932 HTTP_HttpSendRequestW(lpwhr
, req
->lpszHeader
,
3933 req
->dwHeaderLength
, req
->lpOptional
, req
->dwOptionalLength
,
3934 req
->dwContentLength
, req
->bEndRequest
);
3936 HeapFree(GetProcessHeap(), 0, req
->lpszHeader
);
3940 static DWORD
HTTP_HttpEndRequestW(http_request_t
*lpwhr
, DWORD dwFlags
, DWORD_PTR dwContext
)
3944 INTERNET_ASYNC_RESULT iar
;
3945 DWORD res
= ERROR_SUCCESS
;
3947 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3948 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
3950 responseLen
= HTTP_GetResponseHeaders(lpwhr
, TRUE
);
3952 res
= ERROR_HTTP_HEADER_NOT_FOUND
;
3954 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3955 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
, sizeof(DWORD
));
3957 /* process cookies here. Is this right? */
3958 HTTP_ProcessCookies(lpwhr
);
3960 if (!set_content_length( lpwhr
)) HTTP_FinishedReading(lpwhr
);
3962 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
))
3964 DWORD dwCode
,dwCodeLength
= sizeof(DWORD
);
3965 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_STATUS_CODE
, &dwCode
, &dwCodeLength
, NULL
) == ERROR_SUCCESS
3966 && (dwCode
== 302 || dwCode
== 301 || dwCode
== 303))
3968 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
3969 dwBufferSize
=sizeof(szNewLocation
);
3970 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_LOCATION
, szNewLocation
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
)
3972 if (strcmpW(lpwhr
->lpszVerb
, szGET
) && strcmpW(lpwhr
->lpszVerb
, szHEAD
))
3974 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVerb
);
3975 lpwhr
->lpszVerb
= heap_strdupW(szGET
);
3977 HTTP_DrainContent(lpwhr
);
3978 if ((new_url
= HTTP_GetRedirectURL( lpwhr
, szNewLocation
)))
3980 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
3981 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
3982 res
= HTTP_HandleRedirect(lpwhr
, new_url
);
3983 if (res
== ERROR_SUCCESS
)
3984 res
= HTTP_HttpSendRequestW(lpwhr
, NULL
, 0, NULL
, 0, 0, TRUE
);
3985 HeapFree( GetProcessHeap(), 0, new_url
);
3991 iar
.dwResult
= (res
==ERROR_SUCCESS
? (DWORD_PTR
)lpwhr
->hdr
.hInternet
: 0);
3994 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3995 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
3996 sizeof(INTERNET_ASYNC_RESULT
));
4000 /***********************************************************************
4001 * HttpEndRequestA (WININET.@)
4003 * Ends an HTTP request that was started by HttpSendRequestEx
4006 * TRUE if successful
4010 BOOL WINAPI
HttpEndRequestA(HINTERNET hRequest
,
4011 LPINTERNET_BUFFERSA lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
4013 TRACE("(%p, %p, %08x, %08lx)\n", hRequest
, lpBuffersOut
, dwFlags
, dwContext
);
4017 SetLastError(ERROR_INVALID_PARAMETER
);
4021 return HttpEndRequestW(hRequest
, NULL
, dwFlags
, dwContext
);
4024 static void AsyncHttpEndRequestProc(WORKREQUEST
*work
)
4026 struct WORKREQ_HTTPENDREQUESTW
const *req
= &work
->u
.HttpEndRequestW
;
4027 http_request_t
*lpwhr
= (http_request_t
*)work
->hdr
;
4029 TRACE("%p\n", lpwhr
);
4031 HTTP_HttpEndRequestW(lpwhr
, req
->dwFlags
, req
->dwContext
);
4034 /***********************************************************************
4035 * HttpEndRequestW (WININET.@)
4037 * Ends an HTTP request that was started by HttpSendRequestEx
4040 * TRUE if successful
4044 BOOL WINAPI
HttpEndRequestW(HINTERNET hRequest
,
4045 LPINTERNET_BUFFERSW lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
4047 http_request_t
*lpwhr
;
4054 SetLastError(ERROR_INVALID_PARAMETER
);
4058 lpwhr
= (http_request_t
*) WININET_GetObject( hRequest
);
4060 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
4062 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
4064 WININET_Release( &lpwhr
->hdr
);
4067 lpwhr
->hdr
.dwFlags
|= dwFlags
;
4069 if (lpwhr
->lpHttpSession
->lpAppInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4072 struct WORKREQ_HTTPENDREQUESTW
*request
;
4074 work
.asyncproc
= AsyncHttpEndRequestProc
;
4075 work
.hdr
= WININET_AddRef( &lpwhr
->hdr
);
4077 request
= &work
.u
.HttpEndRequestW
;
4078 request
->dwFlags
= dwFlags
;
4079 request
->dwContext
= dwContext
;
4081 INTERNET_AsyncCall(&work
);
4082 res
= ERROR_IO_PENDING
;
4085 res
= HTTP_HttpEndRequestW(lpwhr
, dwFlags
, dwContext
);
4087 WININET_Release( &lpwhr
->hdr
);
4088 TRACE("%u <--\n", res
);
4089 if(res
!= ERROR_SUCCESS
)
4091 return res
== ERROR_SUCCESS
;
4094 /***********************************************************************
4095 * HttpSendRequestExA (WININET.@)
4097 * Sends the specified request to the HTTP server and allows chunked
4102 * Failure: FALSE, call GetLastError() for more information.
4104 BOOL WINAPI
HttpSendRequestExA(HINTERNET hRequest
,
4105 LPINTERNET_BUFFERSA lpBuffersIn
,
4106 LPINTERNET_BUFFERSA lpBuffersOut
,
4107 DWORD dwFlags
, DWORD_PTR dwContext
)
4109 INTERNET_BUFFERSW BuffersInW
;
4112 LPWSTR header
= NULL
;
4114 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
4115 lpBuffersOut
, dwFlags
, dwContext
);
4119 BuffersInW
.dwStructSize
= sizeof(LPINTERNET_BUFFERSW
);
4120 if (lpBuffersIn
->lpcszHeader
)
4122 headerlen
= MultiByteToWideChar(CP_ACP
,0,lpBuffersIn
->lpcszHeader
,
4123 lpBuffersIn
->dwHeadersLength
,0,0);
4124 header
= HeapAlloc(GetProcessHeap(),0,headerlen
*sizeof(WCHAR
));
4125 if (!(BuffersInW
.lpcszHeader
= header
))
4127 SetLastError(ERROR_OUTOFMEMORY
);
4130 BuffersInW
.dwHeadersLength
= MultiByteToWideChar(CP_ACP
, 0,
4131 lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
4135 BuffersInW
.lpcszHeader
= NULL
;
4136 BuffersInW
.dwHeadersTotal
= lpBuffersIn
->dwHeadersTotal
;
4137 BuffersInW
.lpvBuffer
= lpBuffersIn
->lpvBuffer
;
4138 BuffersInW
.dwBufferLength
= lpBuffersIn
->dwBufferLength
;
4139 BuffersInW
.dwBufferTotal
= lpBuffersIn
->dwBufferTotal
;
4140 BuffersInW
.Next
= NULL
;
4143 rc
= HttpSendRequestExW(hRequest
, lpBuffersIn
? &BuffersInW
: NULL
, NULL
, dwFlags
, dwContext
);
4145 HeapFree(GetProcessHeap(),0,header
);
4150 /***********************************************************************
4151 * HttpSendRequestExW (WININET.@)
4153 * Sends the specified request to the HTTP server and allows chunked
4158 * Failure: FALSE, call GetLastError() for more information.
4160 BOOL WINAPI
HttpSendRequestExW(HINTERNET hRequest
,
4161 LPINTERNET_BUFFERSW lpBuffersIn
,
4162 LPINTERNET_BUFFERSW lpBuffersOut
,
4163 DWORD dwFlags
, DWORD_PTR dwContext
)
4165 http_request_t
*lpwhr
;
4166 http_session_t
*lpwhs
;
4170 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
4171 lpBuffersOut
, dwFlags
, dwContext
);
4173 lpwhr
= (http_request_t
*) WININET_GetObject( hRequest
);
4175 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
4177 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
4181 lpwhs
= lpwhr
->lpHttpSession
;
4182 assert(lpwhs
->hdr
.htype
== WH_HHTTPSESSION
);
4183 hIC
= lpwhs
->lpAppInfo
;
4184 assert(hIC
->hdr
.htype
== WH_HINIT
);
4186 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4188 WORKREQUEST workRequest
;
4189 struct WORKREQ_HTTPSENDREQUESTW
*req
;
4191 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
4192 workRequest
.hdr
= WININET_AddRef( &lpwhr
->hdr
);
4193 req
= &workRequest
.u
.HttpSendRequestW
;
4198 if (lpBuffersIn
->lpcszHeader
)
4200 if (lpBuffersIn
->dwHeadersLength
== ~0u)
4201 size
= (strlenW( lpBuffersIn
->lpcszHeader
) + 1) * sizeof(WCHAR
);
4203 size
= lpBuffersIn
->dwHeadersLength
* sizeof(WCHAR
);
4205 req
->lpszHeader
= HeapAlloc( GetProcessHeap(), 0, size
);
4206 memcpy( req
->lpszHeader
, lpBuffersIn
->lpcszHeader
, size
);
4208 else req
->lpszHeader
= NULL
;
4210 req
->dwHeaderLength
= size
/ sizeof(WCHAR
);
4211 req
->lpOptional
= lpBuffersIn
->lpvBuffer
;
4212 req
->dwOptionalLength
= lpBuffersIn
->dwBufferLength
;
4213 req
->dwContentLength
= lpBuffersIn
->dwBufferTotal
;
4217 req
->lpszHeader
= NULL
;
4218 req
->dwHeaderLength
= 0;
4219 req
->lpOptional
= NULL
;
4220 req
->dwOptionalLength
= 0;
4221 req
->dwContentLength
= 0;
4224 req
->bEndRequest
= FALSE
;
4226 INTERNET_AsyncCall(&workRequest
);
4228 * This is from windows.
4230 res
= ERROR_IO_PENDING
;
4235 res
= HTTP_HttpSendRequestW(lpwhr
, lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
4236 lpBuffersIn
->lpvBuffer
, lpBuffersIn
->dwBufferLength
,
4237 lpBuffersIn
->dwBufferTotal
, FALSE
);
4239 res
= HTTP_HttpSendRequestW(lpwhr
, NULL
, 0, NULL
, 0, 0, FALSE
);
4244 WININET_Release( &lpwhr
->hdr
);
4248 return res
== ERROR_SUCCESS
;
4251 /***********************************************************************
4252 * HttpSendRequestW (WININET.@)
4254 * Sends the specified request to the HTTP server
4261 BOOL WINAPI
HttpSendRequestW(HINTERNET hHttpRequest
, LPCWSTR lpszHeaders
,
4262 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
4264 http_request_t
*lpwhr
;
4265 http_session_t
*lpwhs
= NULL
;
4266 appinfo_t
*hIC
= NULL
;
4267 DWORD res
= ERROR_SUCCESS
;
4269 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest
,
4270 debugstr_wn(lpszHeaders
, dwHeaderLength
), dwHeaderLength
, lpOptional
, dwOptionalLength
);
4272 lpwhr
= (http_request_t
*) WININET_GetObject( hHttpRequest
);
4273 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
4275 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
4279 lpwhs
= lpwhr
->lpHttpSession
;
4280 if (NULL
== lpwhs
|| lpwhs
->hdr
.htype
!= WH_HHTTPSESSION
)
4282 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
4286 hIC
= lpwhs
->lpAppInfo
;
4287 if (NULL
== hIC
|| hIC
->hdr
.htype
!= WH_HINIT
)
4289 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
4293 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4295 WORKREQUEST workRequest
;
4296 struct WORKREQ_HTTPSENDREQUESTW
*req
;
4298 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
4299 workRequest
.hdr
= WININET_AddRef( &lpwhr
->hdr
);
4300 req
= &workRequest
.u
.HttpSendRequestW
;
4305 if (dwHeaderLength
== ~0u) size
= (strlenW(lpszHeaders
) + 1) * sizeof(WCHAR
);
4306 else size
= dwHeaderLength
* sizeof(WCHAR
);
4308 req
->lpszHeader
= HeapAlloc(GetProcessHeap(), 0, size
);
4309 memcpy(req
->lpszHeader
, lpszHeaders
, size
);
4312 req
->lpszHeader
= 0;
4313 req
->dwHeaderLength
= dwHeaderLength
;
4314 req
->lpOptional
= lpOptional
;
4315 req
->dwOptionalLength
= dwOptionalLength
;
4316 req
->dwContentLength
= dwOptionalLength
;
4317 req
->bEndRequest
= TRUE
;
4319 INTERNET_AsyncCall(&workRequest
);
4321 * This is from windows.
4323 res
= ERROR_IO_PENDING
;
4327 res
= HTTP_HttpSendRequestW(lpwhr
, lpszHeaders
,
4328 dwHeaderLength
, lpOptional
, dwOptionalLength
,
4329 dwOptionalLength
, TRUE
);
4333 WININET_Release( &lpwhr
->hdr
);
4336 return res
== ERROR_SUCCESS
;
4339 /***********************************************************************
4340 * HttpSendRequestA (WININET.@)
4342 * Sends the specified request to the HTTP server
4349 BOOL WINAPI
HttpSendRequestA(HINTERNET hHttpRequest
, LPCSTR lpszHeaders
,
4350 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
4353 LPWSTR szHeaders
=NULL
;
4354 DWORD nLen
=dwHeaderLength
;
4355 if(lpszHeaders
!=NULL
)
4357 nLen
=MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,NULL
,0);
4358 szHeaders
=HeapAlloc(GetProcessHeap(),0,nLen
*sizeof(WCHAR
));
4359 MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,szHeaders
,nLen
);
4361 result
=HttpSendRequestW(hHttpRequest
, szHeaders
, nLen
, lpOptional
, dwOptionalLength
);
4362 HeapFree(GetProcessHeap(),0,szHeaders
);
4366 /***********************************************************************
4367 * HTTPSESSION_Destroy (internal)
4369 * Deallocate session handle
4372 static void HTTPSESSION_Destroy(object_header_t
*hdr
)
4374 http_session_t
*lpwhs
= (http_session_t
*) hdr
;
4376 TRACE("%p\n", lpwhs
);
4378 WININET_Release(&lpwhs
->lpAppInfo
->hdr
);
4380 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszHostName
);
4381 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszServerName
);
4382 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszPassword
);
4383 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszUserName
);
4384 HeapFree(GetProcessHeap(), 0, lpwhs
);
4387 static DWORD
HTTPSESSION_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
4390 case INTERNET_OPTION_HANDLE_TYPE
:
4391 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4393 if (*size
< sizeof(ULONG
))
4394 return ERROR_INSUFFICIENT_BUFFER
;
4396 *size
= sizeof(DWORD
);
4397 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_CONNECT_HTTP
;
4398 return ERROR_SUCCESS
;
4401 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
4404 static DWORD
HTTPSESSION_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
4406 http_session_t
*ses
= (http_session_t
*)hdr
;
4409 case INTERNET_OPTION_USERNAME
:
4411 HeapFree(GetProcessHeap(), 0, ses
->lpszUserName
);
4412 if (!(ses
->lpszUserName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
4413 return ERROR_SUCCESS
;
4415 case INTERNET_OPTION_PASSWORD
:
4417 HeapFree(GetProcessHeap(), 0, ses
->lpszPassword
);
4418 if (!(ses
->lpszPassword
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
4419 return ERROR_SUCCESS
;
4424 return ERROR_INTERNET_INVALID_OPTION
;
4427 static const object_vtbl_t HTTPSESSIONVtbl
= {
4428 HTTPSESSION_Destroy
,
4430 HTTPSESSION_QueryOption
,
4431 HTTPSESSION_SetOption
,
4440 /***********************************************************************
4441 * HTTP_Connect (internal)
4443 * Create http session handle
4446 * HINTERNET a session handle on success
4450 DWORD
HTTP_Connect(appinfo_t
*hIC
, LPCWSTR lpszServerName
,
4451 INTERNET_PORT nServerPort
, LPCWSTR lpszUserName
,
4452 LPCWSTR lpszPassword
, DWORD dwFlags
, DWORD_PTR dwContext
,
4453 DWORD dwInternalFlags
, HINTERNET
*ret
)
4455 http_session_t
*lpwhs
= NULL
;
4456 HINTERNET handle
= NULL
;
4457 DWORD res
= ERROR_SUCCESS
;
4461 if (!lpszServerName
|| !lpszServerName
[0])
4462 return ERROR_INVALID_PARAMETER
;
4464 assert( hIC
->hdr
.htype
== WH_HINIT
);
4466 lpwhs
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(http_session_t
));
4468 return ERROR_OUTOFMEMORY
;
4471 * According to my tests. The name is not resolved until a request is sent
4474 lpwhs
->hdr
.htype
= WH_HHTTPSESSION
;
4475 lpwhs
->hdr
.vtbl
= &HTTPSESSIONVtbl
;
4476 lpwhs
->hdr
.dwFlags
= dwFlags
;
4477 lpwhs
->hdr
.dwContext
= dwContext
;
4478 lpwhs
->hdr
.dwInternalFlags
= dwInternalFlags
| (hIC
->hdr
.dwInternalFlags
& INET_CALLBACKW
);
4479 lpwhs
->hdr
.refs
= 1;
4480 lpwhs
->hdr
.lpfnStatusCB
= hIC
->hdr
.lpfnStatusCB
;
4482 WININET_AddRef( &hIC
->hdr
);
4483 lpwhs
->lpAppInfo
= hIC
;
4484 list_add_head( &hIC
->hdr
.children
, &lpwhs
->hdr
.entry
);
4486 handle
= WININET_AllocHandle( &lpwhs
->hdr
);
4489 ERR("Failed to alloc handle\n");
4490 res
= ERROR_OUTOFMEMORY
;
4494 if(hIC
->lpszProxy
&& hIC
->dwAccessType
== INTERNET_OPEN_TYPE_PROXY
) {
4495 if(hIC
->lpszProxyBypass
)
4496 FIXME("Proxy bypass is ignored.\n");
4498 lpwhs
->lpszServerName
= heap_strdupW(lpszServerName
);
4499 lpwhs
->lpszHostName
= heap_strdupW(lpszServerName
);
4500 if (lpszUserName
&& lpszUserName
[0])
4501 lpwhs
->lpszUserName
= heap_strdupW(lpszUserName
);
4502 if (lpszPassword
&& lpszPassword
[0])
4503 lpwhs
->lpszPassword
= heap_strdupW(lpszPassword
);
4504 lpwhs
->nServerPort
= nServerPort
;
4505 lpwhs
->nHostPort
= nServerPort
;
4507 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4508 if (!(lpwhs
->hdr
.dwInternalFlags
& INET_OPENURL
))
4510 INTERNET_SendCallback(&hIC
->hdr
, dwContext
,
4511 INTERNET_STATUS_HANDLE_CREATED
, &handle
,
4517 WININET_Release( &lpwhs
->hdr
);
4520 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4524 TRACE("%p --> %p (%p)\n", hIC
, handle
, lpwhs
);
4526 if(res
== ERROR_SUCCESS
)
4532 /***********************************************************************
4533 * HTTP_OpenConnection (internal)
4535 * Connect to a web server
4542 static DWORD
HTTP_OpenConnection(http_request_t
*lpwhr
)
4544 http_session_t
*lpwhs
;
4545 appinfo_t
*hIC
= NULL
;
4546 char szaddr
[INET6_ADDRSTRLEN
];
4548 DWORD res
= ERROR_SUCCESS
;
4553 if (lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
4555 res
= ERROR_INVALID_PARAMETER
;
4559 if (NETCON_connected(&lpwhr
->netConnection
))
4561 if ((res
= HTTP_ResolveName(lpwhr
)) != ERROR_SUCCESS
) goto lend
;
4563 lpwhs
= lpwhr
->lpHttpSession
;
4565 hIC
= lpwhs
->lpAppInfo
;
4566 switch (lpwhs
->socketAddress
.ss_family
)
4569 addr
= &((struct sockaddr_in
*)&lpwhs
->socketAddress
)->sin_addr
;
4572 addr
= &((struct sockaddr_in6
*)&lpwhs
->socketAddress
)->sin6_addr
;
4575 WARN("unsupported family %d\n", lpwhs
->socketAddress
.ss_family
);
4576 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
4578 inet_ntop(lpwhs
->socketAddress
.ss_family
, addr
, szaddr
, sizeof(szaddr
));
4579 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
4580 INTERNET_STATUS_CONNECTING_TO_SERVER
,
4584 res
= NETCON_create(&lpwhr
->netConnection
, lpwhs
->socketAddress
.ss_family
, SOCK_STREAM
, 0);
4585 if (res
!= ERROR_SUCCESS
)
4587 WARN("Socket creation failed: %u\n", res
);
4591 res
= NETCON_connect(&lpwhr
->netConnection
, (struct sockaddr
*)&lpwhs
->socketAddress
,
4593 if(res
!= ERROR_SUCCESS
)
4596 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
4597 INTERNET_STATUS_CONNECTED_TO_SERVER
,
4598 szaddr
, strlen(szaddr
)+1);
4600 if (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
4602 /* Note: we differ from Microsoft's WinINet here. they seem to have
4603 * a bug that causes no status callbacks to be sent when starting
4604 * a tunnel to a proxy server using the CONNECT verb. i believe our
4605 * behaviour to be more correct and to not cause any incompatibilities
4606 * because using a secure connection through a proxy server is a rare
4607 * case that would be hard for anyone to depend on */
4608 if (hIC
->lpszProxy
&& (res
= HTTP_SecureProxyConnect(lpwhr
)) != ERROR_SUCCESS
) {
4609 HTTPREQ_CloseConnection(&lpwhr
->hdr
);
4613 res
= NETCON_secure_connect(&lpwhr
->netConnection
, lpwhs
->lpszHostName
);
4614 if(res
!= ERROR_SUCCESS
)
4616 WARN("Couldn't connect securely to host\n");
4618 if((lpwhr
->hdr
.ErrorMask
&INTERNET_ERROR_MASK_COMBINED_SEC_CERT
) && (
4619 res
== ERROR_INTERNET_SEC_CERT_DATE_INVALID
4620 || res
== ERROR_INTERNET_INVALID_CA
4621 || res
== ERROR_INTERNET_SEC_CERT_NO_REV
4622 || res
== ERROR_INTERNET_SEC_CERT_REV_FAILED
4623 || res
== ERROR_INTERNET_SEC_CERT_REVOKED
4624 || res
== ERROR_INTERNET_SEC_INVALID_CERT
4625 || res
== ERROR_INTERNET_SEC_CERT_CN_INVALID
))
4626 res
= ERROR_INTERNET_SEC_CERT_ERRORS
;
4628 HTTPREQ_CloseConnection(&lpwhr
->hdr
);
4635 lpwhr
->read_pos
= lpwhr
->read_size
= 0;
4636 lpwhr
->read_chunked
= FALSE
;
4638 TRACE("%d <--\n", res
);
4643 /***********************************************************************
4644 * HTTP_clear_response_headers (internal)
4646 * clear out any old response headers
4648 static void HTTP_clear_response_headers( http_request_t
*lpwhr
)
4652 for( i
=0; i
<lpwhr
->nCustHeaders
; i
++)
4654 if( !lpwhr
->pCustHeaders
[i
].lpszField
)
4656 if( !lpwhr
->pCustHeaders
[i
].lpszValue
)
4658 if ( lpwhr
->pCustHeaders
[i
].wFlags
& HDR_ISREQUEST
)
4660 HTTP_DeleteCustomHeader( lpwhr
, i
);
4665 /***********************************************************************
4666 * HTTP_GetResponseHeaders (internal)
4668 * Read server response
4675 static INT
HTTP_GetResponseHeaders(http_request_t
*lpwhr
, BOOL clear
)
4678 WCHAR buffer
[MAX_REPLY_LEN
];
4679 DWORD buflen
= MAX_REPLY_LEN
;
4680 BOOL bSuccess
= FALSE
;
4682 char bufferA
[MAX_REPLY_LEN
];
4683 LPWSTR status_code
= NULL
, status_text
= NULL
;
4684 DWORD cchMaxRawHeaders
= 1024;
4685 LPWSTR lpszRawHeaders
= NULL
;
4687 DWORD cchRawHeaders
= 0;
4688 BOOL codeHundred
= FALSE
;
4692 if (!NETCON_connected(&lpwhr
->netConnection
))
4696 static const WCHAR szHundred
[] = {'1','0','0',0};
4698 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4700 buflen
= MAX_REPLY_LEN
;
4701 if (!read_line(lpwhr
, bufferA
, &buflen
))
4704 /* clear old response headers (eg. from a redirect response) */
4706 HTTP_clear_response_headers( lpwhr
);
4711 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
4712 /* check is this a status code line? */
4713 if (!strncmpW(buffer
, g_szHttp1_0
, 4))
4715 /* split the version from the status code */
4716 status_code
= strchrW( buffer
, ' ' );
4721 /* split the status code from the status text */
4722 status_text
= strchrW( status_code
, ' ' );
4727 TRACE("version [%s] status code [%s] status text [%s]\n",
4728 debugstr_w(buffer
), debugstr_w(status_code
), debugstr_w(status_text
) );
4730 codeHundred
= (!strcmpW(status_code
, szHundred
));
4732 else if (!codeHundred
)
4734 WARN("No status line at head of response (%s)\n", debugstr_w(buffer
));
4736 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVersion
);
4737 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszStatusText
);
4739 lpwhr
->lpszVersion
= heap_strdupW(g_szHttp1_0
);
4740 lpwhr
->lpszStatusText
= heap_strdupW(szOK
);
4742 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszRawHeaders
);
4743 lpwhr
->lpszRawHeaders
= heap_strdupW(szDefaultHeader
);
4748 } while (codeHundred
);
4750 /* Add status code */
4751 HTTP_ProcessHeader(lpwhr
, szStatus
, status_code
,
4752 HTTP_ADDHDR_FLAG_REPLACE
);
4754 HeapFree(GetProcessHeap(),0,lpwhr
->lpszVersion
);
4755 HeapFree(GetProcessHeap(),0,lpwhr
->lpszStatusText
);
4757 lpwhr
->lpszVersion
= heap_strdupW(buffer
);
4758 lpwhr
->lpszStatusText
= heap_strdupW(status_text
);
4760 /* Restore the spaces */
4761 *(status_code
-1) = ' ';
4762 *(status_text
-1) = ' ';
4764 /* regenerate raw headers */
4765 lpszRawHeaders
= HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
4766 if (!lpszRawHeaders
) goto lend
;
4768 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
4769 cchMaxRawHeaders
*= 2;
4770 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
4771 if (temp
== NULL
) goto lend
;
4772 lpszRawHeaders
= temp
;
4773 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
4774 cchRawHeaders
+= (buflen
-1);
4775 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
4776 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
4777 lpszRawHeaders
[cchRawHeaders
] = '\0';
4779 /* Parse each response line */
4782 buflen
= MAX_REPLY_LEN
;
4783 if (read_line(lpwhr
, bufferA
, &buflen
))
4785 LPWSTR
* pFieldAndValue
;
4787 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA
));
4789 if (!bufferA
[0]) break;
4790 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
4792 pFieldAndValue
= HTTP_InterpretHttpHeader(buffer
);
4795 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
4796 cchMaxRawHeaders
*= 2;
4797 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
4798 if (temp
== NULL
) goto lend
;
4799 lpszRawHeaders
= temp
;
4800 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
4801 cchRawHeaders
+= (buflen
-1);
4802 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
4803 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
4804 lpszRawHeaders
[cchRawHeaders
] = '\0';
4806 HTTP_ProcessHeader(lpwhr
, pFieldAndValue
[0], pFieldAndValue
[1],
4807 HTTP_ADDREQ_FLAG_ADD
);
4809 HTTP_FreeTokens(pFieldAndValue
);
4820 /* make sure the response header is terminated with an empty line. Some apps really
4821 truly care about that empty line being there for some reason. Just add it to the
4823 if (cchRawHeaders
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
4825 cchMaxRawHeaders
= cchRawHeaders
+ strlenW(szCrLf
);
4826 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
4827 if (temp
== NULL
) goto lend
;
4828 lpszRawHeaders
= temp
;
4831 memcpy(&lpszRawHeaders
[cchRawHeaders
], szCrLf
, sizeof(szCrLf
));
4833 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszRawHeaders
);
4834 lpwhr
->lpszRawHeaders
= lpszRawHeaders
;
4835 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders
));
4845 HeapFree(GetProcessHeap(), 0, lpszRawHeaders
);
4850 /***********************************************************************
4851 * HTTP_InterpretHttpHeader (internal)
4853 * Parse server response
4857 * Pointer to array of field, value, NULL on success.
4860 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
)
4862 LPWSTR
* pTokenPair
;
4866 pTokenPair
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*pTokenPair
)*3);
4868 pszColon
= strchrW(buffer
, ':');
4869 /* must have two tokens */
4872 HTTP_FreeTokens(pTokenPair
);
4874 TRACE("No ':' in line: %s\n", debugstr_w(buffer
));
4878 pTokenPair
[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon
- buffer
+ 1) * sizeof(WCHAR
));
4881 HTTP_FreeTokens(pTokenPair
);
4884 memcpy(pTokenPair
[0], buffer
, (pszColon
- buffer
) * sizeof(WCHAR
));
4885 pTokenPair
[0][pszColon
- buffer
] = '\0';
4889 len
= strlenW(pszColon
);
4890 pTokenPair
[1] = HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
));
4893 HTTP_FreeTokens(pTokenPair
);
4896 memcpy(pTokenPair
[1], pszColon
, (len
+ 1) * sizeof(WCHAR
));
4898 strip_spaces(pTokenPair
[0]);
4899 strip_spaces(pTokenPair
[1]);
4901 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair
[0]), debugstr_w(pTokenPair
[1]));
4905 /***********************************************************************
4906 * HTTP_ProcessHeader (internal)
4908 * Stuff header into header tables according to <dwModifier>
4912 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4914 static DWORD
HTTP_ProcessHeader(http_request_t
*lpwhr
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
)
4916 LPHTTPHEADERW lphttpHdr
= NULL
;
4918 BOOL request_only
= dwModifier
& HTTP_ADDHDR_FLAG_REQ
;
4919 DWORD res
= ERROR_HTTP_INVALID_HEADER
;
4921 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field
), debugstr_w(value
), dwModifier
);
4923 /* REPLACE wins out over ADD */
4924 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
4925 dwModifier
&= ~HTTP_ADDHDR_FLAG_ADD
;
4927 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD
)
4930 index
= HTTP_GetCustomHeaderIndex(lpwhr
, field
, 0, request_only
);
4934 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD_IF_NEW
)
4935 return ERROR_HTTP_INVALID_HEADER
;
4936 lphttpHdr
= &lpwhr
->pCustHeaders
[index
];
4942 hdr
.lpszField
= (LPWSTR
)field
;
4943 hdr
.lpszValue
= (LPWSTR
)value
;
4944 hdr
.wFlags
= hdr
.wCount
= 0;
4946 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
4947 hdr
.wFlags
|= HDR_ISREQUEST
;
4949 return HTTP_InsertCustomHeader(lpwhr
, &hdr
);
4951 /* no value to delete */
4952 else return ERROR_SUCCESS
;
4954 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
4955 lphttpHdr
->wFlags
|= HDR_ISREQUEST
;
4957 lphttpHdr
->wFlags
&= ~HDR_ISREQUEST
;
4959 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
4961 HTTP_DeleteCustomHeader( lpwhr
, index
);
4967 hdr
.lpszField
= (LPWSTR
)field
;
4968 hdr
.lpszValue
= (LPWSTR
)value
;
4969 hdr
.wFlags
= hdr
.wCount
= 0;
4971 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
4972 hdr
.wFlags
|= HDR_ISREQUEST
;
4974 return HTTP_InsertCustomHeader(lpwhr
, &hdr
);
4977 return ERROR_SUCCESS
;
4979 else if (dwModifier
& COALESCEFLAGS
)
4984 INT origlen
= strlenW(lphttpHdr
->lpszValue
);
4985 INT valuelen
= strlenW(value
);
4987 if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
)
4990 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
4992 else if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON
)
4995 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
4998 len
= origlen
+ valuelen
+ ((ch
> 0) ? 2 : 0);
5000 lpsztmp
= HeapReAlloc(GetProcessHeap(), 0, lphttpHdr
->lpszValue
, (len
+1)*sizeof(WCHAR
));
5003 lphttpHdr
->lpszValue
= lpsztmp
;
5004 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5007 lphttpHdr
->lpszValue
[origlen
] = ch
;
5009 lphttpHdr
->lpszValue
[origlen
] = ' ';
5013 memcpy(&lphttpHdr
->lpszValue
[origlen
], value
, valuelen
*sizeof(WCHAR
));
5014 lphttpHdr
->lpszValue
[len
] = '\0';
5015 res
= ERROR_SUCCESS
;
5019 WARN("HeapReAlloc (%d bytes) failed\n",len
+1);
5020 res
= ERROR_OUTOFMEMORY
;
5023 TRACE("<-- %d\n", res
);
5028 /***********************************************************************
5029 * HTTP_FinishedReading (internal)
5031 * Called when all content from server has been read by client.
5034 static BOOL
HTTP_FinishedReading(http_request_t
*lpwhr
)
5036 BOOL keepalive
= HTTP_KeepAlive(lpwhr
);
5043 HTTPREQ_CloseConnection(&lpwhr
->hdr
);
5046 /* FIXME: store data in the URL cache here */
5052 /***********************************************************************
5053 * HTTP_GetCustomHeaderIndex (internal)
5055 * Return index of custom header from header array
5058 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*lpwhr
, LPCWSTR lpszField
,
5059 int requested_index
, BOOL request_only
)
5063 TRACE("%s, %d, %d\n", debugstr_w(lpszField
), requested_index
, request_only
);
5065 for (index
= 0; index
< lpwhr
->nCustHeaders
; index
++)
5067 if (strcmpiW(lpwhr
->pCustHeaders
[index
].lpszField
, lpszField
))
5070 if (request_only
&& !(lpwhr
->pCustHeaders
[index
].wFlags
& HDR_ISREQUEST
))
5073 if (!request_only
&& (lpwhr
->pCustHeaders
[index
].wFlags
& HDR_ISREQUEST
))
5076 if (requested_index
== 0)
5081 if (index
>= lpwhr
->nCustHeaders
)
5084 TRACE("Return: %d\n", index
);
5089 /***********************************************************************
5090 * HTTP_InsertCustomHeader (internal)
5092 * Insert header into array
5095 static DWORD
HTTP_InsertCustomHeader(http_request_t
*lpwhr
, LPHTTPHEADERW lpHdr
)
5098 LPHTTPHEADERW lph
= NULL
;
5100 TRACE("--> %s: %s\n", debugstr_w(lpHdr
->lpszField
), debugstr_w(lpHdr
->lpszValue
));
5101 count
= lpwhr
->nCustHeaders
+ 1;
5103 lph
= HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, lpwhr
->pCustHeaders
, sizeof(HTTPHEADERW
) * count
);
5105 lph
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(HTTPHEADERW
) * count
);
5108 return ERROR_OUTOFMEMORY
;
5110 lpwhr
->pCustHeaders
= lph
;
5111 lpwhr
->pCustHeaders
[count
-1].lpszField
= heap_strdupW(lpHdr
->lpszField
);
5112 lpwhr
->pCustHeaders
[count
-1].lpszValue
= heap_strdupW(lpHdr
->lpszValue
);
5113 lpwhr
->pCustHeaders
[count
-1].wFlags
= lpHdr
->wFlags
;
5114 lpwhr
->pCustHeaders
[count
-1].wCount
= lpHdr
->wCount
;
5115 lpwhr
->nCustHeaders
++;
5117 return ERROR_SUCCESS
;
5121 /***********************************************************************
5122 * HTTP_DeleteCustomHeader (internal)
5124 * Delete header from array
5125 * If this function is called, the indexs may change.
5127 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*lpwhr
, DWORD index
)
5129 if( lpwhr
->nCustHeaders
<= 0 )
5131 if( index
>= lpwhr
->nCustHeaders
)
5133 lpwhr
->nCustHeaders
--;
5135 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[index
].lpszField
);
5136 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[index
].lpszValue
);
5138 memmove( &lpwhr
->pCustHeaders
[index
], &lpwhr
->pCustHeaders
[index
+1],
5139 (lpwhr
->nCustHeaders
- index
)* sizeof(HTTPHEADERW
) );
5140 memset( &lpwhr
->pCustHeaders
[lpwhr
->nCustHeaders
], 0, sizeof(HTTPHEADERW
) );
5146 /***********************************************************************
5147 * HTTP_VerifyValidHeader (internal)
5149 * Verify the given header is not invalid for the given http request
5152 static BOOL
HTTP_VerifyValidHeader(http_request_t
*lpwhr
, LPCWSTR field
)
5154 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5155 if (!strcmpW(lpwhr
->lpszVersion
, g_szHttp1_0
) && !strcmpiW(field
, szAccept_Encoding
))
5156 return ERROR_HTTP_INVALID_HEADER
;
5158 return ERROR_SUCCESS
;
5161 /***********************************************************************
5162 * IsHostInProxyBypassList (@)
5167 BOOL WINAPI
IsHostInProxyBypassList(DWORD flags
, LPCSTR szHost
, DWORD length
)
5169 FIXME("STUB: flags=%d host=%s length=%d\n",flags
,szHost
,length
);
5173 /***********************************************************************
5174 * InternetShowSecurityInfoByURLA (@)
5176 BOOL WINAPI
InternetShowSecurityInfoByURLA(LPCSTR url
, HWND window
)
5178 FIXME("stub: %s %p\n", url
, window
);
5182 /***********************************************************************
5183 * InternetShowSecurityInfoByURLW (@)
5185 BOOL WINAPI
InternetShowSecurityInfoByURLW(LPCWSTR url
, HWND window
)
5187 FIXME("stub: %s %p\n", debugstr_w(url
), window
);