2 * Wininet - Http Implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2002 CodeWeavers Inc.
6 * Copyright 2002 TransGaming Technologies Inc.
7 * Copyright 2004 Mike McCormack for CodeWeavers
8 * Copyright 2005 Aric Stewart for CodeWeavers
9 * Copyright 2006 Robert Shearman for CodeWeavers
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "wine/port.h"
32 #if defined(__MINGW32__) || defined (_MSC_VER)
36 #include <sys/types.h>
37 #ifdef HAVE_SYS_SOCKET_H
38 # include <sys/socket.h>
40 #ifdef HAVE_ARPA_INET_H
41 # include <arpa/inet.h>
59 #define NO_SHLWAPI_STREAM
60 #define NO_SHLWAPI_REG
61 #define NO_SHLWAPI_STRFCNS
62 #define NO_SHLWAPI_GDI
68 #include "wine/debug.h"
69 #include "wine/exception.h"
70 #include "wine/unicode.h"
72 WINE_DEFAULT_DEBUG_CHANNEL(wininet
);
74 static const WCHAR g_szHttp1_0
[] = {'H','T','T','P','/','1','.','0',0};
75 static const WCHAR g_szHttp1_1
[] = {'H','T','T','P','/','1','.','1',0};
76 static const WCHAR szOK
[] = {'O','K',0};
77 static const WCHAR szDefaultHeader
[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
78 static const WCHAR hostW
[] = { 'H','o','s','t',0 };
79 static const WCHAR szAuthorization
[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
80 static const WCHAR szProxy_Authorization
[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
81 static const WCHAR szStatus
[] = { 'S','t','a','t','u','s',0 };
82 static const WCHAR szKeepAlive
[] = {'K','e','e','p','-','A','l','i','v','e',0};
83 static const WCHAR szGET
[] = { 'G','E','T', 0 };
84 static const WCHAR szHEAD
[] = { 'H','E','A','D', 0 };
85 static const WCHAR szCrLf
[] = {'\r','\n', 0};
87 static const WCHAR szAccept
[] = { 'A','c','c','e','p','t',0 };
88 static const WCHAR szAccept_Charset
[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
89 static const WCHAR szAccept_Encoding
[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
90 static const WCHAR szAccept_Language
[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
91 static const WCHAR szAccept_Ranges
[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
92 static const WCHAR szAge
[] = { 'A','g','e',0 };
93 static const WCHAR szAllow
[] = { 'A','l','l','o','w',0 };
94 static const WCHAR szCache_Control
[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
95 static const WCHAR szConnection
[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
96 static const WCHAR szContent_Base
[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
97 static const WCHAR szContent_Encoding
[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
98 static const WCHAR szContent_ID
[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
99 static const WCHAR szContent_Language
[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
100 static const WCHAR szContent_Length
[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
101 static const WCHAR szContent_Location
[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
102 static const WCHAR szContent_MD5
[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
103 static const WCHAR szContent_Range
[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
104 static const WCHAR szContent_Transfer_Encoding
[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
105 static const WCHAR szContent_Type
[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
106 static const WCHAR szCookie
[] = { 'C','o','o','k','i','e',0 };
107 static const WCHAR szDate
[] = { 'D','a','t','e',0 };
108 static const WCHAR szFrom
[] = { 'F','r','o','m',0 };
109 static const WCHAR szETag
[] = { 'E','T','a','g',0 };
110 static const WCHAR szExpect
[] = { 'E','x','p','e','c','t',0 };
111 static const WCHAR szExpires
[] = { 'E','x','p','i','r','e','s',0 };
112 static const WCHAR szIf_Match
[] = { 'I','f','-','M','a','t','c','h',0 };
113 static const WCHAR szIf_Modified_Since
[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
114 static const WCHAR szIf_None_Match
[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
115 static const WCHAR szIf_Range
[] = { 'I','f','-','R','a','n','g','e',0 };
116 static const WCHAR szIf_Unmodified_Since
[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
117 static const WCHAR szLast_Modified
[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
118 static const WCHAR szLocation
[] = { 'L','o','c','a','t','i','o','n',0 };
119 static const WCHAR szMax_Forwards
[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
120 static const WCHAR szMime_Version
[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
121 static const WCHAR szPragma
[] = { 'P','r','a','g','m','a',0 };
122 static const WCHAR szProxy_Authenticate
[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
123 static const WCHAR szProxy_Connection
[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
124 static const WCHAR szPublic
[] = { 'P','u','b','l','i','c',0 };
125 static const WCHAR szRange
[] = { 'R','a','n','g','e',0 };
126 static const WCHAR szReferer
[] = { 'R','e','f','e','r','e','r',0 };
127 static const WCHAR szRetry_After
[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
128 static const WCHAR szServer
[] = { 'S','e','r','v','e','r',0 };
129 static const WCHAR szSet_Cookie
[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
130 static const WCHAR szTransfer_Encoding
[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
131 static const WCHAR szUnless_Modified_Since
[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
132 static const WCHAR szUpgrade
[] = { 'U','p','g','r','a','d','e',0 };
133 static const WCHAR szURI
[] = { 'U','R','I',0 };
134 static const WCHAR szUser_Agent
[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
135 static const WCHAR szVary
[] = { 'V','a','r','y',0 };
136 static const WCHAR szVia
[] = { 'V','i','a',0 };
137 static const WCHAR szWarning
[] = { 'W','a','r','n','i','n','g',0 };
138 static const WCHAR szWWW_Authenticate
[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
140 #define MAXHOSTNAME 100
141 #define MAX_FIELD_VALUE_LEN 256
142 #define MAX_FIELD_LEN 256
144 #define HTTP_REFERER szReferer
145 #define HTTP_ACCEPT szAccept
146 #define HTTP_USERAGENT szUser_Agent
148 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
149 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
150 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
151 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
152 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
153 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
154 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
156 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
167 unsigned int auth_data_len
;
168 BOOL finished
; /* finished authenticating */
172 struct gzip_stream_t
{
182 typedef struct _basicAuthorizationData
188 LPSTR lpszAuthorization
;
189 UINT AuthorizationLen
;
190 } basicAuthorizationData
;
192 typedef struct _authorizationData
206 static struct list basicAuthorizationCache
= LIST_INIT(basicAuthorizationCache
);
207 static struct list authorizationCache
= LIST_INIT(authorizationCache
);
209 static CRITICAL_SECTION authcache_cs
;
210 static CRITICAL_SECTION_DEBUG critsect_debug
=
213 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
214 0, 0, { (DWORD_PTR
)(__FILE__
": authcache_cs") }
216 static CRITICAL_SECTION authcache_cs
= { &critsect_debug
, -1, 0, 0, 0, 0 };
218 static DWORD
HTTP_OpenConnection(http_request_t
*req
);
219 static BOOL
HTTP_GetResponseHeaders(http_request_t
*req
, BOOL clear
);
220 static DWORD
HTTP_ProcessHeader(http_request_t
*req
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
);
221 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
);
222 static DWORD
HTTP_InsertCustomHeader(http_request_t
*req
, LPHTTPHEADERW lpHdr
);
223 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*req
, LPCWSTR lpszField
, INT index
, BOOL Request
);
224 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*req
, DWORD index
);
225 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
);
226 static DWORD
HTTP_HttpQueryInfoW(http_request_t
*, DWORD
, LPVOID
, LPDWORD
, LPDWORD
);
227 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*req
, LPCWSTR lpszUrl
);
228 static UINT
HTTP_DecodeBase64(LPCWSTR base64
, LPSTR bin
);
229 static BOOL
HTTP_VerifyValidHeader(http_request_t
*req
, LPCWSTR field
);
230 static void HTTP_DrainContent(http_request_t
*req
);
231 static BOOL
HTTP_FinishedReading(http_request_t
*req
);
233 static LPHTTPHEADERW
HTTP_GetHeader(http_request_t
*req
, LPCWSTR head
)
236 HeaderIndex
= HTTP_GetCustomHeaderIndex(req
, head
, 0, TRUE
);
237 if (HeaderIndex
== -1)
240 return &req
->pCustHeaders
[HeaderIndex
];
245 static voidpf
wininet_zalloc(voidpf opaque
, uInt items
, uInt size
)
247 return HeapAlloc(GetProcessHeap(), 0, items
*size
);
250 static void wininet_zfree(voidpf opaque
, voidpf address
)
252 HeapFree(GetProcessHeap(), 0, address
);
255 static void init_gzip_stream(http_request_t
*req
)
257 gzip_stream_t
*gzip_stream
;
260 gzip_stream
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(gzip_stream_t
));
261 gzip_stream
->zstream
.zalloc
= wininet_zalloc
;
262 gzip_stream
->zstream
.zfree
= wininet_zfree
;
264 zres
= inflateInit2(&gzip_stream
->zstream
, 0x1f);
266 ERR("inflateInit failed: %d\n", zres
);
267 HeapFree(GetProcessHeap(), 0, gzip_stream
);
271 req
->gzip_stream
= gzip_stream
;
273 index
= HTTP_GetCustomHeaderIndex(req
, szContent_Length
, 0, FALSE
);
275 HTTP_DeleteCustomHeader(req
, index
);
280 static void init_gzip_stream(http_request_t
*req
)
282 ERR("gzip stream not supported, missing zlib.\n");
287 /* set the request content length based on the headers */
288 static DWORD
set_content_length( http_request_t
*lpwhr
)
290 static const WCHAR szChunked
[] = {'c','h','u','n','k','e','d',0};
294 size
= sizeof(lpwhr
->dwContentLength
);
295 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_CONTENT_LENGTH
,
296 &lpwhr
->dwContentLength
, &size
, NULL
) != ERROR_SUCCESS
)
297 lpwhr
->dwContentLength
= ~0u;
299 size
= sizeof(encoding
);
300 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_TRANSFER_ENCODING
, encoding
, &size
, NULL
) == ERROR_SUCCESS
&&
301 !strcmpiW(encoding
, szChunked
))
303 lpwhr
->dwContentLength
= ~0u;
304 lpwhr
->read_chunked
= TRUE
;
307 if(lpwhr
->decoding
) {
310 static const WCHAR gzipW
[] = {'g','z','i','p',0};
312 encoding_idx
= HTTP_GetCustomHeaderIndex(lpwhr
, szContent_Encoding
, 0, FALSE
);
313 if(encoding_idx
!= -1 && !strcmpiW(lpwhr
->pCustHeaders
[encoding_idx
].lpszValue
, gzipW
))
314 init_gzip_stream(lpwhr
);
317 return lpwhr
->dwContentLength
;
320 /***********************************************************************
321 * HTTP_Tokenize (internal)
323 * Tokenize a string, allocating memory for the tokens.
325 static LPWSTR
* HTTP_Tokenize(LPCWSTR string
, LPCWSTR token_string
)
327 LPWSTR
* token_array
;
334 /* empty string has no tokens */
338 for (i
= 0; string
[i
]; i
++)
340 if (!strncmpW(string
+i
, token_string
, strlenW(token_string
)))
344 /* we want to skip over separators, but not the null terminator */
345 for (j
= 0; j
< strlenW(token_string
) - 1; j
++)
353 /* add 1 for terminating NULL */
354 token_array
= HeapAlloc(GetProcessHeap(), 0, (tokens
+1) * sizeof(*token_array
));
355 token_array
[tokens
] = NULL
;
358 for (i
= 0; i
< tokens
; i
++)
361 next_token
= strstrW(string
, token_string
);
362 if (!next_token
) next_token
= string
+strlenW(string
);
363 len
= next_token
- string
;
364 token_array
[i
] = HeapAlloc(GetProcessHeap(), 0, (len
+1)*sizeof(WCHAR
));
365 memcpy(token_array
[i
], string
, len
*sizeof(WCHAR
));
366 token_array
[i
][len
] = '\0';
367 string
= next_token
+strlenW(token_string
);
372 /***********************************************************************
373 * HTTP_FreeTokens (internal)
375 * Frees memory returned from HTTP_Tokenize.
377 static void HTTP_FreeTokens(LPWSTR
* token_array
)
380 for (i
= 0; token_array
[i
]; i
++)
381 HeapFree(GetProcessHeap(), 0, token_array
[i
]);
382 HeapFree(GetProcessHeap(), 0, token_array
);
385 static void HTTP_FixURL(http_request_t
*lpwhr
)
387 static const WCHAR szSlash
[] = { '/',0 };
388 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/', 0 };
390 /* If we don't have a path we set it to root */
391 if (NULL
== lpwhr
->lpszPath
)
392 lpwhr
->lpszPath
= heap_strdupW(szSlash
);
393 else /* remove \r and \n*/
395 int nLen
= strlenW(lpwhr
->lpszPath
);
396 while ((nLen
>0 ) && ((lpwhr
->lpszPath
[nLen
-1] == '\r')||(lpwhr
->lpszPath
[nLen
-1] == '\n')))
399 lpwhr
->lpszPath
[nLen
]='\0';
401 /* Replace '\' with '/' */
404 if (lpwhr
->lpszPath
[nLen
] == '\\') lpwhr
->lpszPath
[nLen
]='/';
408 if(CSTR_EQUAL
!= CompareStringW( LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
409 lpwhr
->lpszPath
, strlenW(lpwhr
->lpszPath
), szHttp
, strlenW(szHttp
) )
410 && lpwhr
->lpszPath
[0] != '/') /* not an absolute path ?? --> fix it !! */
412 WCHAR
*fixurl
= HeapAlloc(GetProcessHeap(), 0,
413 (strlenW(lpwhr
->lpszPath
) + 2)*sizeof(WCHAR
));
415 strcpyW(fixurl
+ 1, lpwhr
->lpszPath
);
416 HeapFree( GetProcessHeap(), 0, lpwhr
->lpszPath
);
417 lpwhr
->lpszPath
= fixurl
;
421 static LPWSTR
HTTP_BuildHeaderRequestString( http_request_t
*lpwhr
, LPCWSTR verb
, LPCWSTR path
, LPCWSTR version
)
423 LPWSTR requestString
;
429 static const WCHAR szSpace
[] = { ' ',0 };
430 static const WCHAR szColon
[] = { ':',' ',0 };
431 static const WCHAR sztwocrlf
[] = {'\r','\n','\r','\n', 0};
433 /* allocate space for an array of all the string pointers to be added */
434 len
= (lpwhr
->nCustHeaders
)*4 + 10;
435 req
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(LPCWSTR
) );
437 /* add the verb, path and HTTP version string */
445 /* Append custom request headers */
446 for (i
= 0; i
< lpwhr
->nCustHeaders
; i
++)
448 if (lpwhr
->pCustHeaders
[i
].wFlags
& HDR_ISREQUEST
)
451 req
[n
++] = lpwhr
->pCustHeaders
[i
].lpszField
;
453 req
[n
++] = lpwhr
->pCustHeaders
[i
].lpszValue
;
455 TRACE("Adding custom header %s (%s)\n",
456 debugstr_w(lpwhr
->pCustHeaders
[i
].lpszField
),
457 debugstr_w(lpwhr
->pCustHeaders
[i
].lpszValue
));
462 ERR("oops. buffer overrun\n");
465 requestString
= HTTP_build_req( req
, 4 );
466 HeapFree( GetProcessHeap(), 0, req
);
469 * Set (header) termination string for request
470 * Make sure there's exactly two new lines at the end of the request
472 p
= &requestString
[strlenW(requestString
)-1];
473 while ( (*p
== '\n') || (*p
== '\r') )
475 strcpyW( p
+1, sztwocrlf
);
477 return requestString
;
480 static void HTTP_ProcessCookies( http_request_t
*lpwhr
)
484 LPHTTPHEADERW setCookieHeader
;
486 while((HeaderIndex
= HTTP_GetCustomHeaderIndex(lpwhr
, szSet_Cookie
, numCookies
, FALSE
)) != -1)
488 setCookieHeader
= &lpwhr
->pCustHeaders
[HeaderIndex
];
490 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
) && setCookieHeader
->lpszValue
)
493 static const WCHAR szFmt
[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
497 Host
= HTTP_GetHeader(lpwhr
, hostW
);
498 len
= lstrlenW(Host
->lpszValue
) + 9 + lstrlenW(lpwhr
->lpszPath
);
499 buf_url
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
500 sprintfW(buf_url
, szFmt
, Host
->lpszValue
, lpwhr
->lpszPath
);
501 InternetSetCookieW(buf_url
, NULL
, setCookieHeader
->lpszValue
);
503 HeapFree(GetProcessHeap(), 0, buf_url
);
509 static void strip_spaces(LPWSTR start
)
514 while (*str
== ' ' && *str
!= '\0')
518 memmove(start
, str
, sizeof(WCHAR
) * (strlenW(str
) + 1));
520 end
= start
+ strlenW(start
) - 1;
521 while (end
>= start
&& *end
== ' ')
528 static inline BOOL
is_basic_auth_value( LPCWSTR pszAuthValue
, LPWSTR
*pszRealm
)
530 static const WCHAR szBasic
[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
531 static const WCHAR szRealm
[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
533 is_basic
= !strncmpiW(pszAuthValue
, szBasic
, ARRAYSIZE(szBasic
)) &&
534 ((pszAuthValue
[ARRAYSIZE(szBasic
)] == ' ') || !pszAuthValue
[ARRAYSIZE(szBasic
)]);
535 if (is_basic
&& pszRealm
)
538 LPCWSTR ptr
= &pszAuthValue
[ARRAYSIZE(szBasic
)];
542 token
= strchrW(ptr
,'=');
546 while (*realm
== ' ' && *realm
!= '\0')
548 if(!strncmpiW(realm
, szRealm
, ARRAYSIZE(szRealm
)) &&
549 (realm
[ARRAYSIZE(szRealm
)] == ' ' || realm
[ARRAYSIZE(szRealm
)] == '='))
552 while (*token
== ' ' && *token
!= '\0')
556 *pszRealm
= heap_strdupW(token
);
557 strip_spaces(*pszRealm
);
564 static void destroy_authinfo( struct HttpAuthInfo
*authinfo
)
566 if (!authinfo
) return;
568 if (SecIsValidHandle(&authinfo
->ctx
))
569 DeleteSecurityContext(&authinfo
->ctx
);
570 if (SecIsValidHandle(&authinfo
->cred
))
571 FreeCredentialsHandle(&authinfo
->cred
);
573 HeapFree(GetProcessHeap(), 0, authinfo
->auth_data
);
574 HeapFree(GetProcessHeap(), 0, authinfo
->scheme
);
575 HeapFree(GetProcessHeap(), 0, authinfo
);
578 static UINT
retrieve_cached_basic_authorization(LPWSTR host
, LPWSTR realm
, LPSTR
*auth_data
)
580 basicAuthorizationData
*ad
;
583 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host
),debugstr_w(realm
));
585 EnterCriticalSection(&authcache_cs
);
586 LIST_FOR_EACH_ENTRY(ad
, &basicAuthorizationCache
, basicAuthorizationData
, entry
)
588 if (!strcmpiW(host
,ad
->lpszwHost
) && !strcmpW(realm
,ad
->lpszwRealm
))
590 TRACE("Authorization found in cache\n");
591 *auth_data
= HeapAlloc(GetProcessHeap(),0,ad
->AuthorizationLen
);
592 memcpy(*auth_data
,ad
->lpszAuthorization
,ad
->AuthorizationLen
);
593 rc
= ad
->AuthorizationLen
;
597 LeaveCriticalSection(&authcache_cs
);
601 static void cache_basic_authorization(LPWSTR host
, LPWSTR realm
, LPSTR auth_data
, UINT auth_data_len
)
604 basicAuthorizationData
* ad
= NULL
;
606 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host
),debugstr_w(realm
),debugstr_an(auth_data
,auth_data_len
));
608 EnterCriticalSection(&authcache_cs
);
609 LIST_FOR_EACH(cursor
, &basicAuthorizationCache
)
611 basicAuthorizationData
*check
= LIST_ENTRY(cursor
,basicAuthorizationData
,entry
);
612 if (!strcmpiW(host
,check
->lpszwHost
) && !strcmpW(realm
,check
->lpszwRealm
))
621 TRACE("Found match in cache, replacing\n");
622 HeapFree(GetProcessHeap(),0,ad
->lpszAuthorization
);
623 ad
->lpszAuthorization
= HeapAlloc(GetProcessHeap(),0,auth_data_len
);
624 memcpy(ad
->lpszAuthorization
, auth_data
, auth_data_len
);
625 ad
->AuthorizationLen
= auth_data_len
;
629 ad
= HeapAlloc(GetProcessHeap(),0,sizeof(basicAuthorizationData
));
630 ad
->lpszwHost
= heap_strdupW(host
);
631 ad
->lpszwRealm
= heap_strdupW(realm
);
632 ad
->lpszAuthorization
= HeapAlloc(GetProcessHeap(),0,auth_data_len
);
633 memcpy(ad
->lpszAuthorization
, auth_data
, auth_data_len
);
634 ad
->AuthorizationLen
= auth_data_len
;
635 list_add_head(&basicAuthorizationCache
,&ad
->entry
);
636 TRACE("authorization cached\n");
638 LeaveCriticalSection(&authcache_cs
);
641 static BOOL
retrieve_cached_authorization(LPWSTR host
, LPWSTR scheme
,
642 SEC_WINNT_AUTH_IDENTITY_W
*nt_auth_identity
)
644 authorizationData
*ad
;
646 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host
), debugstr_w(scheme
));
648 EnterCriticalSection(&authcache_cs
);
649 LIST_FOR_EACH_ENTRY(ad
, &authorizationCache
, authorizationData
, entry
) {
650 if(!strcmpiW(host
, ad
->host
) && !strcmpiW(scheme
, ad
->scheme
)) {
651 TRACE("Authorization found in cache\n");
653 nt_auth_identity
->User
= heap_strdupW(ad
->user
);
654 nt_auth_identity
->Password
= heap_strdupW(ad
->password
);
655 nt_auth_identity
->Domain
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*ad
->domain_len
);
656 if(!nt_auth_identity
->User
|| !nt_auth_identity
->Password
||
657 (!nt_auth_identity
->Domain
&& ad
->domain_len
)) {
658 HeapFree(GetProcessHeap(), 0, nt_auth_identity
->User
);
659 HeapFree(GetProcessHeap(), 0, nt_auth_identity
->Password
);
660 HeapFree(GetProcessHeap(), 0, nt_auth_identity
->Domain
);
664 nt_auth_identity
->Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
665 nt_auth_identity
->UserLength
= ad
->user_len
;
666 nt_auth_identity
->PasswordLength
= ad
->password_len
;
667 memcpy(nt_auth_identity
->Domain
, ad
->domain
, sizeof(WCHAR
)*ad
->domain_len
);
668 nt_auth_identity
->DomainLength
= ad
->domain_len
;
669 LeaveCriticalSection(&authcache_cs
);
673 LeaveCriticalSection(&authcache_cs
);
678 static void cache_authorization(LPWSTR host
, LPWSTR scheme
,
679 SEC_WINNT_AUTH_IDENTITY_W
*nt_auth_identity
)
681 authorizationData
*ad
;
684 TRACE("Caching authorization for %s:%s\n", debugstr_w(host
), debugstr_w(scheme
));
686 EnterCriticalSection(&authcache_cs
);
687 LIST_FOR_EACH_ENTRY(ad
, &authorizationCache
, authorizationData
, entry
)
688 if(!strcmpiW(host
, ad
->host
) && !strcmpiW(scheme
, ad
->scheme
)) {
694 HeapFree(GetProcessHeap(), 0, ad
->user
);
695 HeapFree(GetProcessHeap(), 0, ad
->password
);
696 HeapFree(GetProcessHeap(), 0, ad
->domain
);
698 ad
= HeapAlloc(GetProcessHeap(), 0, sizeof(authorizationData
));
700 LeaveCriticalSection(&authcache_cs
);
704 ad
->host
= heap_strdupW(host
);
705 ad
->scheme
= heap_strdupW(scheme
);
706 list_add_head(&authorizationCache
, &ad
->entry
);
709 ad
->user
= heap_strndupW(nt_auth_identity
->User
, nt_auth_identity
->UserLength
);
710 ad
->password
= heap_strndupW(nt_auth_identity
->Password
, nt_auth_identity
->PasswordLength
);
711 ad
->domain
= heap_strndupW(nt_auth_identity
->Domain
, nt_auth_identity
->DomainLength
);
712 ad
->user_len
= nt_auth_identity
->UserLength
;
713 ad
->password_len
= nt_auth_identity
->PasswordLength
;
714 ad
->domain_len
= nt_auth_identity
->DomainLength
;
716 if(!ad
->host
|| !ad
->scheme
|| !ad
->user
|| !ad
->password
717 || (nt_auth_identity
->Domain
&& !ad
->domain
)) {
718 HeapFree(GetProcessHeap(), 0, ad
->host
);
719 HeapFree(GetProcessHeap(), 0, ad
->scheme
);
720 HeapFree(GetProcessHeap(), 0, ad
->user
);
721 HeapFree(GetProcessHeap(), 0, ad
->password
);
722 HeapFree(GetProcessHeap(), 0, ad
->domain
);
723 list_remove(&ad
->entry
);
724 HeapFree(GetProcessHeap(), 0, ad
);
727 LeaveCriticalSection(&authcache_cs
);
730 static BOOL
HTTP_DoAuthorization( http_request_t
*lpwhr
, LPCWSTR pszAuthValue
,
731 struct HttpAuthInfo
**ppAuthInfo
,
732 LPWSTR domain_and_username
, LPWSTR password
,
735 SECURITY_STATUS sec_status
;
736 struct HttpAuthInfo
*pAuthInfo
= *ppAuthInfo
;
738 LPWSTR szRealm
= NULL
;
740 TRACE("%s\n", debugstr_w(pszAuthValue
));
747 pAuthInfo
= HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo
));
751 SecInvalidateHandle(&pAuthInfo
->cred
);
752 SecInvalidateHandle(&pAuthInfo
->ctx
);
753 memset(&pAuthInfo
->exp
, 0, sizeof(pAuthInfo
->exp
));
755 pAuthInfo
->auth_data
= NULL
;
756 pAuthInfo
->auth_data_len
= 0;
757 pAuthInfo
->finished
= FALSE
;
759 if (is_basic_auth_value(pszAuthValue
,NULL
))
761 static const WCHAR szBasic
[] = {'B','a','s','i','c',0};
762 pAuthInfo
->scheme
= heap_strdupW(szBasic
);
763 if (!pAuthInfo
->scheme
)
765 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
772 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity
;
774 pAuthInfo
->scheme
= heap_strdupW(pszAuthValue
);
775 if (!pAuthInfo
->scheme
)
777 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
781 if (domain_and_username
)
783 WCHAR
*user
= strchrW(domain_and_username
, '\\');
784 WCHAR
*domain
= domain_and_username
;
786 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
788 pAuthData
= &nt_auth_identity
;
793 user
= domain_and_username
;
797 nt_auth_identity
.Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
798 nt_auth_identity
.User
= user
;
799 nt_auth_identity
.UserLength
= strlenW(nt_auth_identity
.User
);
800 nt_auth_identity
.Domain
= domain
;
801 nt_auth_identity
.DomainLength
= domain
? user
- domain
- 1 : 0;
802 nt_auth_identity
.Password
= password
;
803 nt_auth_identity
.PasswordLength
= strlenW(nt_auth_identity
.Password
);
805 cache_authorization(host
, pAuthInfo
->scheme
, &nt_auth_identity
);
807 else if(retrieve_cached_authorization(host
, pAuthInfo
->scheme
, &nt_auth_identity
))
808 pAuthData
= &nt_auth_identity
;
810 /* use default credentials */
813 sec_status
= AcquireCredentialsHandleW(NULL
, pAuthInfo
->scheme
,
814 SECPKG_CRED_OUTBOUND
, NULL
,
816 NULL
, &pAuthInfo
->cred
,
819 if(pAuthData
&& !domain_and_username
) {
820 HeapFree(GetProcessHeap(), 0, nt_auth_identity
.User
);
821 HeapFree(GetProcessHeap(), 0, nt_auth_identity
.Domain
);
822 HeapFree(GetProcessHeap(), 0, nt_auth_identity
.Password
);
825 if (sec_status
== SEC_E_OK
)
827 PSecPkgInfoW sec_pkg_info
;
828 sec_status
= QuerySecurityPackageInfoW(pAuthInfo
->scheme
, &sec_pkg_info
);
829 if (sec_status
== SEC_E_OK
)
831 pAuthInfo
->max_token
= sec_pkg_info
->cbMaxToken
;
832 FreeContextBuffer(sec_pkg_info
);
835 if (sec_status
!= SEC_E_OK
)
837 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
838 debugstr_w(pAuthInfo
->scheme
), sec_status
);
839 HeapFree(GetProcessHeap(), 0, pAuthInfo
->scheme
);
840 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
844 *ppAuthInfo
= pAuthInfo
;
846 else if (pAuthInfo
->finished
)
849 if ((strlenW(pszAuthValue
) < strlenW(pAuthInfo
->scheme
)) ||
850 strncmpiW(pszAuthValue
, pAuthInfo
->scheme
, strlenW(pAuthInfo
->scheme
)))
852 ERR("authentication scheme changed from %s to %s\n",
853 debugstr_w(pAuthInfo
->scheme
), debugstr_w(pszAuthValue
));
857 if (is_basic_auth_value(pszAuthValue
,&szRealm
))
861 char *auth_data
= NULL
;
862 UINT auth_data_len
= 0;
864 TRACE("basic authentication realm %s\n",debugstr_w(szRealm
));
866 if (!domain_and_username
)
869 auth_data_len
= retrieve_cached_basic_authorization(host
, szRealm
,&auth_data
);
870 if (auth_data_len
== 0)
872 HeapFree(GetProcessHeap(),0,szRealm
);
878 userlen
= WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, lstrlenW(domain_and_username
), NULL
, 0, NULL
, NULL
);
879 passlen
= WideCharToMultiByte(CP_UTF8
, 0, password
, lstrlenW(password
), NULL
, 0, NULL
, NULL
);
881 /* length includes a nul terminator, which will be re-used for the ':' */
882 auth_data
= HeapAlloc(GetProcessHeap(), 0, userlen
+ 1 + passlen
);
885 HeapFree(GetProcessHeap(),0,szRealm
);
889 WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, -1, auth_data
, userlen
, NULL
, NULL
);
890 auth_data
[userlen
] = ':';
891 WideCharToMultiByte(CP_UTF8
, 0, password
, -1, &auth_data
[userlen
+1], passlen
, NULL
, NULL
);
892 auth_data_len
= userlen
+ 1 + passlen
;
894 cache_basic_authorization(host
, szRealm
, auth_data
, auth_data_len
);
897 pAuthInfo
->auth_data
= auth_data
;
898 pAuthInfo
->auth_data_len
= auth_data_len
;
899 pAuthInfo
->finished
= TRUE
;
900 HeapFree(GetProcessHeap(),0,szRealm
);
907 SecBufferDesc out_desc
, in_desc
;
909 unsigned char *buffer
;
910 ULONG context_req
= ISC_REQ_CONNECTION
| ISC_REQ_USE_DCE_STYLE
|
911 ISC_REQ_MUTUAL_AUTH
| ISC_REQ_DELEGATE
;
913 in
.BufferType
= SECBUFFER_TOKEN
;
917 in_desc
.ulVersion
= 0;
918 in_desc
.cBuffers
= 1;
919 in_desc
.pBuffers
= &in
;
921 pszAuthData
= pszAuthValue
+ strlenW(pAuthInfo
->scheme
);
922 if (*pszAuthData
== ' ')
925 in
.cbBuffer
= HTTP_DecodeBase64(pszAuthData
, NULL
);
926 in
.pvBuffer
= HeapAlloc(GetProcessHeap(), 0, in
.cbBuffer
);
927 HTTP_DecodeBase64(pszAuthData
, in
.pvBuffer
);
930 buffer
= HeapAlloc(GetProcessHeap(), 0, pAuthInfo
->max_token
);
932 out
.BufferType
= SECBUFFER_TOKEN
;
933 out
.cbBuffer
= pAuthInfo
->max_token
;
934 out
.pvBuffer
= buffer
;
936 out_desc
.ulVersion
= 0;
937 out_desc
.cBuffers
= 1;
938 out_desc
.pBuffers
= &out
;
940 sec_status
= InitializeSecurityContextW(first
? &pAuthInfo
->cred
: NULL
,
941 first
? NULL
: &pAuthInfo
->ctx
,
942 first
? lpwhr
->lpHttpSession
->lpszServerName
: NULL
,
943 context_req
, 0, SECURITY_NETWORK_DREP
,
944 in
.pvBuffer
? &in_desc
: NULL
,
945 0, &pAuthInfo
->ctx
, &out_desc
,
946 &pAuthInfo
->attr
, &pAuthInfo
->exp
);
947 if (sec_status
== SEC_E_OK
)
949 pAuthInfo
->finished
= TRUE
;
950 pAuthInfo
->auth_data
= out
.pvBuffer
;
951 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
952 TRACE("sending last auth packet\n");
954 else if (sec_status
== SEC_I_CONTINUE_NEEDED
)
956 pAuthInfo
->auth_data
= out
.pvBuffer
;
957 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
958 TRACE("sending next auth packet\n");
962 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status
);
963 HeapFree(GetProcessHeap(), 0, out
.pvBuffer
);
964 destroy_authinfo(pAuthInfo
);
973 /***********************************************************************
974 * HTTP_HttpAddRequestHeadersW (internal)
976 static DWORD
HTTP_HttpAddRequestHeadersW(http_request_t
*lpwhr
,
977 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
982 DWORD len
, res
= ERROR_HTTP_INVALID_HEADER
;
984 TRACE("copying header: %s\n", debugstr_wn(lpszHeader
, dwHeaderLength
));
986 if( dwHeaderLength
== ~0U )
987 len
= strlenW(lpszHeader
);
989 len
= dwHeaderLength
;
990 buffer
= HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR
)*(len
+1) );
991 lstrcpynW( buffer
, lpszHeader
, len
+ 1);
997 LPWSTR
* pFieldAndValue
;
1001 while (*lpszEnd
!= '\0')
1003 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
1008 if (*lpszStart
== '\0')
1011 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
1014 lpszEnd
++; /* Jump over newline */
1016 TRACE("interpreting header %s\n", debugstr_w(lpszStart
));
1017 if (*lpszStart
== '\0')
1019 /* Skip 0-length headers */
1020 lpszStart
= lpszEnd
;
1021 res
= ERROR_SUCCESS
;
1024 pFieldAndValue
= HTTP_InterpretHttpHeader(lpszStart
);
1027 res
= HTTP_VerifyValidHeader(lpwhr
, pFieldAndValue
[0]);
1028 if (res
== ERROR_SUCCESS
)
1029 res
= HTTP_ProcessHeader(lpwhr
, pFieldAndValue
[0],
1030 pFieldAndValue
[1], dwModifier
| HTTP_ADDHDR_FLAG_REQ
);
1031 HTTP_FreeTokens(pFieldAndValue
);
1034 lpszStart
= lpszEnd
;
1035 } while (res
== ERROR_SUCCESS
);
1037 HeapFree(GetProcessHeap(), 0, buffer
);
1042 /***********************************************************************
1043 * HttpAddRequestHeadersW (WININET.@)
1045 * Adds one or more HTTP header to the request handler
1048 * On Windows if dwHeaderLength includes the trailing '\0', then
1049 * HttpAddRequestHeadersW() adds it too. However this results in an
1050 * invalid Http header which is rejected by some servers so we probably
1051 * don't need to match Windows on that point.
1058 BOOL WINAPI
HttpAddRequestHeadersW(HINTERNET hHttpRequest
,
1059 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1061 http_request_t
*lpwhr
;
1062 DWORD res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
1064 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_wn(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
1069 lpwhr
= (http_request_t
*) get_handle_object( hHttpRequest
);
1070 if (lpwhr
&& lpwhr
->hdr
.htype
== WH_HHTTPREQ
)
1071 res
= HTTP_HttpAddRequestHeadersW( lpwhr
, lpszHeader
, dwHeaderLength
, dwModifier
);
1073 WININET_Release( &lpwhr
->hdr
);
1075 if(res
!= ERROR_SUCCESS
)
1077 return res
== ERROR_SUCCESS
;
1080 /***********************************************************************
1081 * HttpAddRequestHeadersA (WININET.@)
1083 * Adds one or more HTTP header to the request handler
1090 BOOL WINAPI
HttpAddRequestHeadersA(HINTERNET hHttpRequest
,
1091 LPCSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1097 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_an(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
1099 len
= MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, NULL
, 0 );
1100 hdr
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
1101 MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, hdr
, len
);
1102 if( dwHeaderLength
!= ~0U )
1103 dwHeaderLength
= len
;
1105 r
= HttpAddRequestHeadersW( hHttpRequest
, hdr
, dwHeaderLength
, dwModifier
);
1107 HeapFree( GetProcessHeap(), 0, hdr
);
1112 /***********************************************************************
1113 * HttpOpenRequestA (WININET.@)
1115 * Open a HTTP request handle
1118 * HINTERNET a HTTP request handle on success
1122 HINTERNET WINAPI
HttpOpenRequestA(HINTERNET hHttpSession
,
1123 LPCSTR lpszVerb
, LPCSTR lpszObjectName
, LPCSTR lpszVersion
,
1124 LPCSTR lpszReferrer
, LPCSTR
*lpszAcceptTypes
,
1125 DWORD dwFlags
, DWORD_PTR dwContext
)
1127 LPWSTR szVerb
= NULL
, szObjectName
= NULL
;
1128 LPWSTR szVersion
= NULL
, szReferrer
= NULL
, *szAcceptTypes
= NULL
;
1129 INT acceptTypesCount
;
1130 HINTERNET rc
= FALSE
;
1133 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
1134 debugstr_a(lpszVerb
), debugstr_a(lpszObjectName
),
1135 debugstr_a(lpszVersion
), debugstr_a(lpszReferrer
), lpszAcceptTypes
,
1136 dwFlags
, dwContext
);
1140 szVerb
= heap_strdupAtoW(lpszVerb
);
1147 szObjectName
= heap_strdupAtoW(lpszObjectName
);
1148 if ( !szObjectName
)
1154 szVersion
= heap_strdupAtoW(lpszVersion
);
1161 szReferrer
= heap_strdupAtoW(lpszReferrer
);
1166 if (lpszAcceptTypes
)
1168 acceptTypesCount
= 0;
1169 types
= lpszAcceptTypes
;
1174 /* find out how many there are */
1175 if (*types
&& **types
)
1177 TRACE("accept type: %s\n", debugstr_a(*types
));
1183 WARN("invalid accept type pointer\n");
1188 szAcceptTypes
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
*) * (acceptTypesCount
+1));
1189 if (!szAcceptTypes
) goto end
;
1191 acceptTypesCount
= 0;
1192 types
= lpszAcceptTypes
;
1197 if (*types
&& **types
)
1198 szAcceptTypes
[acceptTypesCount
++] = heap_strdupAtoW(*types
);
1202 /* ignore invalid pointer */
1207 szAcceptTypes
[acceptTypesCount
] = NULL
;
1210 rc
= HttpOpenRequestW(hHttpSession
, szVerb
, szObjectName
,
1211 szVersion
, szReferrer
,
1212 (LPCWSTR
*)szAcceptTypes
, dwFlags
, dwContext
);
1217 acceptTypesCount
= 0;
1218 while (szAcceptTypes
[acceptTypesCount
])
1220 HeapFree(GetProcessHeap(), 0, szAcceptTypes
[acceptTypesCount
]);
1223 HeapFree(GetProcessHeap(), 0, szAcceptTypes
);
1225 HeapFree(GetProcessHeap(), 0, szReferrer
);
1226 HeapFree(GetProcessHeap(), 0, szVersion
);
1227 HeapFree(GetProcessHeap(), 0, szObjectName
);
1228 HeapFree(GetProcessHeap(), 0, szVerb
);
1233 /***********************************************************************
1236 static UINT
HTTP_EncodeBase64( LPCSTR bin
, unsigned int len
, LPWSTR base64
)
1239 static const CHAR HTTP_Base64Enc
[] =
1240 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1244 /* first 6 bits, all from bin[0] */
1245 base64
[n
++] = HTTP_Base64Enc
[(bin
[0] & 0xfc) >> 2];
1246 x
= (bin
[0] & 3) << 4;
1248 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1251 base64
[n
++] = HTTP_Base64Enc
[x
];
1256 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[1]&0xf0) >> 4 ) ];
1257 x
= ( bin
[1] & 0x0f ) << 2;
1259 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1262 base64
[n
++] = HTTP_Base64Enc
[x
];
1266 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[2]&0xc0 ) >> 6 ) ];
1268 /* last 6 bits, all from bin [2] */
1269 base64
[n
++] = HTTP_Base64Enc
[ bin
[2] & 0x3f ];
1277 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1278 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1279 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1280 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1281 static const signed char HTTP_Base64Dec
[256] =
1283 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1284 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1285 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1286 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1287 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1288 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1289 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1290 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1291 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1292 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1293 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1294 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1295 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1296 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1297 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1298 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1299 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1300 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1301 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1302 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1303 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1304 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1305 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1306 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1307 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1308 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1312 /***********************************************************************
1315 static UINT
HTTP_DecodeBase64( LPCWSTR base64
, LPSTR bin
)
1323 if (base64
[0] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1324 ((in
[0] = HTTP_Base64Dec
[base64
[0]]) == -1) ||
1325 base64
[1] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1326 ((in
[1] = HTTP_Base64Dec
[base64
[1]]) == -1))
1328 WARN("invalid base64: %s\n", debugstr_w(base64
));
1332 bin
[n
] = (unsigned char) (in
[0] << 2 | in
[1] >> 4);
1335 if ((base64
[2] == '=') && (base64
[3] == '='))
1337 if (base64
[2] > ARRAYSIZE(HTTP_Base64Dec
) ||
1338 ((in
[2] = HTTP_Base64Dec
[base64
[2]]) == -1))
1340 WARN("invalid base64: %s\n", debugstr_w(&base64
[2]));
1344 bin
[n
] = (unsigned char) (in
[1] << 4 | in
[2] >> 2);
1347 if (base64
[3] == '=')
1349 if (base64
[3] > ARRAYSIZE(HTTP_Base64Dec
) ||
1350 ((in
[3] = HTTP_Base64Dec
[base64
[3]]) == -1))
1352 WARN("invalid base64: %s\n", debugstr_w(&base64
[3]));
1356 bin
[n
] = (unsigned char) (((in
[2] << 6) & 0xc0) | in
[3]);
1365 /***********************************************************************
1366 * HTTP_InsertAuthorization
1368 * Insert or delete the authorization field in the request header.
1370 static BOOL
HTTP_InsertAuthorization( http_request_t
*lpwhr
, struct HttpAuthInfo
*pAuthInfo
, LPCWSTR header
)
1374 static const WCHAR wszSpace
[] = {' ',0};
1375 static const WCHAR wszBasic
[] = {'B','a','s','i','c',0};
1377 WCHAR
*authorization
= NULL
;
1379 if (pAuthInfo
->auth_data_len
)
1381 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1382 len
= strlenW(pAuthInfo
->scheme
)+1+((pAuthInfo
->auth_data_len
+2)*4)/3;
1383 authorization
= HeapAlloc(GetProcessHeap(), 0, (len
+1)*sizeof(WCHAR
));
1387 strcpyW(authorization
, pAuthInfo
->scheme
);
1388 strcatW(authorization
, wszSpace
);
1389 HTTP_EncodeBase64(pAuthInfo
->auth_data
,
1390 pAuthInfo
->auth_data_len
,
1391 authorization
+strlenW(authorization
));
1393 /* clear the data as it isn't valid now that it has been sent to the
1394 * server, unless it's Basic authentication which doesn't do
1395 * connection tracking */
1396 if (strcmpiW(pAuthInfo
->scheme
, wszBasic
))
1398 HeapFree(GetProcessHeap(), 0, pAuthInfo
->auth_data
);
1399 pAuthInfo
->auth_data
= NULL
;
1400 pAuthInfo
->auth_data_len
= 0;
1404 TRACE("Inserting authorization: %s\n", debugstr_w(authorization
));
1406 HTTP_ProcessHeader(lpwhr
, header
, authorization
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
1408 HeapFree(GetProcessHeap(), 0, authorization
);
1413 static WCHAR
*HTTP_BuildProxyRequestUrl(http_request_t
*req
)
1415 WCHAR new_location
[INTERNET_MAX_URL_LENGTH
], *url
;
1418 size
= sizeof(new_location
);
1419 if (HTTP_HttpQueryInfoW(req
, HTTP_QUERY_LOCATION
, new_location
, &size
, NULL
) == ERROR_SUCCESS
)
1421 if (!(url
= HeapAlloc( GetProcessHeap(), 0, size
+ sizeof(WCHAR
) ))) return NULL
;
1422 strcpyW( url
, new_location
);
1426 static const WCHAR slash
[] = { '/',0 };
1427 static const WCHAR format
[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1428 static const WCHAR formatSSL
[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1429 http_session_t
*session
= req
->lpHttpSession
;
1431 size
= 16; /* "https://" + sizeof(port#) + ":/\0" */
1432 size
+= strlenW( session
->lpszHostName
) + strlenW( req
->lpszPath
);
1434 if (!(url
= HeapAlloc( GetProcessHeap(), 0, size
* sizeof(WCHAR
) ))) return NULL
;
1436 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1437 sprintfW( url
, formatSSL
, session
->lpszHostName
, session
->nHostPort
);
1439 sprintfW( url
, format
, session
->lpszHostName
, session
->nHostPort
);
1440 if (req
->lpszPath
[0] != '/') strcatW( url
, slash
);
1441 strcatW( url
, req
->lpszPath
);
1443 TRACE("url=%s\n", debugstr_w(url
));
1447 /***********************************************************************
1448 * HTTP_DealWithProxy
1450 static BOOL
HTTP_DealWithProxy(appinfo_t
*hIC
, http_session_t
*lpwhs
, http_request_t
*lpwhr
)
1452 WCHAR buf
[MAXHOSTNAME
];
1453 WCHAR protoProxy
[MAXHOSTNAME
+ 15];
1454 DWORD protoProxyLen
= sizeof(protoProxy
) / sizeof(protoProxy
[0]);
1455 WCHAR proxy
[MAXHOSTNAME
+ 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1456 static WCHAR szNul
[] = { 0 };
1457 URL_COMPONENTSW UrlComponents
;
1458 static const WCHAR protoHttp
[] = { 'h','t','t','p',0 };
1459 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/',0 };
1460 static const WCHAR szFormat
[] = { 'h','t','t','p',':','/','/','%','s',0 };
1462 memset( &UrlComponents
, 0, sizeof UrlComponents
);
1463 UrlComponents
.dwStructSize
= sizeof UrlComponents
;
1464 UrlComponents
.lpszHostName
= buf
;
1465 UrlComponents
.dwHostNameLength
= MAXHOSTNAME
;
1467 if (!INTERNET_FindProxyForProtocol(hIC
->lpszProxy
, protoHttp
, protoProxy
, &protoProxyLen
))
1469 if( CSTR_EQUAL
!= CompareStringW(LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
1470 protoProxy
,strlenW(szHttp
),szHttp
,strlenW(szHttp
)) )
1471 sprintfW(proxy
, szFormat
, protoProxy
);
1473 strcpyW(proxy
, protoProxy
);
1474 if( !InternetCrackUrlW(proxy
, 0, 0, &UrlComponents
) )
1476 if( UrlComponents
.dwHostNameLength
== 0 )
1479 if( !lpwhr
->lpszPath
)
1480 lpwhr
->lpszPath
= szNul
;
1482 if(UrlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
1483 UrlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
1485 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszServerName
);
1486 lpwhs
->lpszServerName
= heap_strdupW(UrlComponents
.lpszHostName
);
1487 lpwhs
->nServerPort
= UrlComponents
.nPort
;
1489 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs
->lpszServerName
), lpwhs
->nServerPort
);
1493 #ifndef INET6_ADDRSTRLEN
1494 #define INET6_ADDRSTRLEN 46
1497 static DWORD
HTTP_ResolveName(http_request_t
*lpwhr
)
1499 char szaddr
[INET6_ADDRSTRLEN
];
1500 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
1503 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1504 INTERNET_STATUS_RESOLVING_NAME
,
1505 lpwhs
->lpszServerName
,
1506 (strlenW(lpwhs
->lpszServerName
)+1) * sizeof(WCHAR
));
1508 lpwhs
->sa_len
= sizeof(lpwhs
->socketAddress
);
1509 if (!GetAddress(lpwhs
->lpszServerName
, lpwhs
->nServerPort
,
1510 (struct sockaddr
*)&lpwhs
->socketAddress
, &lpwhs
->sa_len
))
1511 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
1513 switch (lpwhs
->socketAddress
.ss_family
)
1516 addr
= &((struct sockaddr_in
*)&lpwhs
->socketAddress
)->sin_addr
;
1519 addr
= &((struct sockaddr_in6
*)&lpwhs
->socketAddress
)->sin6_addr
;
1522 WARN("unsupported family %d\n", lpwhs
->socketAddress
.ss_family
);
1523 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
1525 inet_ntop(lpwhs
->socketAddress
.ss_family
, addr
, szaddr
, sizeof(szaddr
));
1526 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1527 INTERNET_STATUS_NAME_RESOLVED
,
1528 szaddr
, strlen(szaddr
)+1);
1530 TRACE("resolved %s to %s\n", debugstr_w(lpwhs
->lpszServerName
), szaddr
);
1531 return ERROR_SUCCESS
;
1534 static BOOL
HTTP_GetRequestURL(http_request_t
*req
, LPWSTR buf
)
1536 LPHTTPHEADERW host_header
;
1538 static const WCHAR formatW
[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1540 host_header
= HTTP_GetHeader(req
, hostW
);
1544 sprintfW(buf
, formatW
, host_header
->lpszValue
, req
->lpszPath
); /* FIXME */
1549 /***********************************************************************
1550 * HTTPREQ_Destroy (internal)
1552 * Deallocate request handle
1555 static void HTTPREQ_Destroy(object_header_t
*hdr
)
1557 http_request_t
*lpwhr
= (http_request_t
*) hdr
;
1562 if(lpwhr
->hCacheFile
) {
1563 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1566 CloseHandle(lpwhr
->hCacheFile
);
1568 memset(&ft
, 0, sizeof(FILETIME
));
1569 if(HTTP_GetRequestURL(lpwhr
, url
)) {
1570 CommitUrlCacheEntryW(url
, lpwhr
->lpszCacheFile
, ft
, ft
,
1571 NORMAL_CACHE_ENTRY
, NULL
, 0, NULL
, 0);
1575 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszCacheFile
);
1577 DeleteCriticalSection( &lpwhr
->read_section
);
1578 WININET_Release(&lpwhr
->lpHttpSession
->hdr
);
1580 destroy_authinfo(lpwhr
->pAuthInfo
);
1581 destroy_authinfo(lpwhr
->pProxyAuthInfo
);
1583 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszPath
);
1584 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVerb
);
1585 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszRawHeaders
);
1586 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVersion
);
1587 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszStatusText
);
1589 for (i
= 0; i
< lpwhr
->nCustHeaders
; i
++)
1591 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[i
].lpszField
);
1592 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[i
].lpszValue
);
1596 if(lpwhr
->gzip_stream
) {
1597 if(!lpwhr
->gzip_stream
->end_of_data
)
1598 inflateEnd(&lpwhr
->gzip_stream
->zstream
);
1599 HeapFree(GetProcessHeap(), 0, lpwhr
->gzip_stream
);
1603 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
);
1604 HeapFree(GetProcessHeap(), 0, lpwhr
);
1607 static void HTTPREQ_CloseConnection(object_header_t
*hdr
)
1609 http_request_t
*lpwhr
= (http_request_t
*) hdr
;
1611 TRACE("%p\n",lpwhr
);
1613 if (!NETCON_connected(&lpwhr
->netConnection
))
1616 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1617 INTERNET_STATUS_CLOSING_CONNECTION
, 0, 0);
1619 NETCON_close(&lpwhr
->netConnection
);
1621 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1622 INTERNET_STATUS_CONNECTION_CLOSED
, 0, 0);
1625 static BOOL
HTTP_KeepAlive(http_request_t
*lpwhr
)
1627 WCHAR szVersion
[10];
1628 WCHAR szConnectionResponse
[20];
1629 DWORD dwBufferSize
= sizeof(szVersion
);
1630 BOOL keepalive
= FALSE
;
1632 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1633 * the connection is keep-alive by default */
1634 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_VERSION
, szVersion
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1635 && !strcmpiW(szVersion
, g_szHttp1_1
))
1640 dwBufferSize
= sizeof(szConnectionResponse
);
1641 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_PROXY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1642 || HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
)
1644 keepalive
= !strcmpiW(szConnectionResponse
, szKeepAlive
);
1650 static DWORD
HTTPREQ_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
1652 http_request_t
*req
= (http_request_t
*)hdr
;
1655 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO
:
1657 http_session_t
*lpwhs
= req
->lpHttpSession
;
1658 INTERNET_DIAGNOSTIC_SOCKET_INFO
*info
= buffer
;
1660 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1662 if (*size
< sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
))
1663 return ERROR_INSUFFICIENT_BUFFER
;
1664 *size
= sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
);
1665 /* FIXME: can't get a SOCKET from our connection since we don't use
1669 /* FIXME: get source port from req->netConnection */
1670 info
->SourcePort
= 0;
1671 info
->DestPort
= lpwhs
->nHostPort
;
1673 if (HTTP_KeepAlive(req
))
1674 info
->Flags
|= IDSI_FLAG_KEEP_ALIVE
;
1675 if (lpwhs
->lpAppInfo
->lpszProxy
&& lpwhs
->lpAppInfo
->lpszProxy
[0] != 0)
1676 info
->Flags
|= IDSI_FLAG_PROXY
;
1677 if (req
->netConnection
.useSSL
)
1678 info
->Flags
|= IDSI_FLAG_SECURE
;
1680 return ERROR_SUCCESS
;
1683 case INTERNET_OPTION_SECURITY_FLAGS
:
1688 if (*size
< sizeof(ULONG
))
1689 return ERROR_INSUFFICIENT_BUFFER
;
1691 *size
= sizeof(DWORD
);
1693 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1694 flags
|= SECURITY_FLAG_SECURE
;
1695 flags
|= req
->netConnection
.security_flags
;
1696 bits
= NETCON_GetCipherStrength(&req
->netConnection
);
1698 flags
|= SECURITY_FLAG_STRENGTH_STRONG
;
1699 else if (bits
>= 56)
1700 flags
|= SECURITY_FLAG_STRENGTH_MEDIUM
;
1702 flags
|= SECURITY_FLAG_STRENGTH_WEAK
;
1703 *(DWORD
*)buffer
= flags
;
1704 return ERROR_SUCCESS
;
1707 case INTERNET_OPTION_HANDLE_TYPE
:
1708 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1710 if (*size
< sizeof(ULONG
))
1711 return ERROR_INSUFFICIENT_BUFFER
;
1713 *size
= sizeof(DWORD
);
1714 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_HTTP_REQUEST
;
1715 return ERROR_SUCCESS
;
1717 case INTERNET_OPTION_URL
: {
1718 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1723 static const WCHAR httpW
[] = {'h','t','t','p',':','/','/',0};
1725 TRACE("INTERNET_OPTION_URL\n");
1727 host
= HTTP_GetHeader(req
, hostW
);
1728 strcpyW(url
, httpW
);
1729 strcatW(url
, host
->lpszValue
);
1730 if (NULL
!= (pch
= strchrW(url
+ strlenW(httpW
), ':')))
1732 strcatW(url
, req
->lpszPath
);
1734 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url
));
1737 len
= (strlenW(url
)+1) * sizeof(WCHAR
);
1739 return ERROR_INSUFFICIENT_BUFFER
;
1742 strcpyW(buffer
, url
);
1743 return ERROR_SUCCESS
;
1745 len
= WideCharToMultiByte(CP_ACP
, 0, url
, -1, buffer
, *size
, NULL
, NULL
);
1747 return ERROR_INSUFFICIENT_BUFFER
;
1750 return ERROR_SUCCESS
;
1754 case INTERNET_OPTION_CACHE_TIMESTAMPS
: {
1755 INTERNET_CACHE_ENTRY_INFOW
*info
;
1756 INTERNET_CACHE_TIMESTAMPS
*ts
= buffer
;
1757 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1758 DWORD nbytes
, error
;
1761 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1763 if (*size
< sizeof(*ts
))
1765 *size
= sizeof(*ts
);
1766 return ERROR_INSUFFICIENT_BUFFER
;
1769 HTTP_GetRequestURL(req
, url
);
1770 ret
= GetUrlCacheEntryInfoW(url
, NULL
, &nbytes
);
1771 error
= GetLastError();
1772 if (!ret
&& error
== ERROR_INSUFFICIENT_BUFFER
)
1774 if (!(info
= HeapAlloc(GetProcessHeap(), 0, nbytes
)))
1775 return ERROR_OUTOFMEMORY
;
1777 GetUrlCacheEntryInfoW(url
, info
, &nbytes
);
1779 ts
->ftExpires
= info
->ExpireTime
;
1780 ts
->ftLastModified
= info
->LastModifiedTime
;
1782 HeapFree(GetProcessHeap(), 0, info
);
1783 *size
= sizeof(*ts
);
1784 return ERROR_SUCCESS
;
1789 case INTERNET_OPTION_DATAFILE_NAME
: {
1792 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1794 if(!req
->lpszCacheFile
) {
1796 return ERROR_INTERNET_ITEM_NOT_FOUND
;
1800 req_size
= (lstrlenW(req
->lpszCacheFile
)+1) * sizeof(WCHAR
);
1801 if(*size
< req_size
)
1802 return ERROR_INSUFFICIENT_BUFFER
;
1805 memcpy(buffer
, req
->lpszCacheFile
, *size
);
1806 return ERROR_SUCCESS
;
1808 req_size
= WideCharToMultiByte(CP_ACP
, 0, req
->lpszCacheFile
, -1, NULL
, 0, NULL
, NULL
);
1809 if (req_size
> *size
)
1810 return ERROR_INSUFFICIENT_BUFFER
;
1812 *size
= WideCharToMultiByte(CP_ACP
, 0, req
->lpszCacheFile
,
1813 -1, buffer
, *size
, NULL
, NULL
);
1814 return ERROR_SUCCESS
;
1818 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT
: {
1819 PCCERT_CONTEXT context
;
1821 if(*size
< sizeof(INTERNET_CERTIFICATE_INFOA
)) {
1822 *size
= sizeof(INTERNET_CERTIFICATE_INFOA
);
1823 return ERROR_INSUFFICIENT_BUFFER
;
1826 context
= (PCCERT_CONTEXT
)NETCON_GetCert(&(req
->netConnection
));
1828 INTERNET_CERTIFICATE_INFOA
*info
= (INTERNET_CERTIFICATE_INFOA
*)buffer
;
1831 memset(info
, 0, sizeof(INTERNET_CERTIFICATE_INFOW
));
1832 info
->ftExpiry
= context
->pCertInfo
->NotAfter
;
1833 info
->ftStart
= context
->pCertInfo
->NotBefore
;
1834 len
= CertNameToStrA(context
->dwCertEncodingType
,
1835 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1836 info
->lpszSubjectInfo
= LocalAlloc(0, len
);
1837 if(info
->lpszSubjectInfo
)
1838 CertNameToStrA(context
->dwCertEncodingType
,
1839 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
,
1840 info
->lpszSubjectInfo
, len
);
1841 len
= CertNameToStrA(context
->dwCertEncodingType
,
1842 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1843 info
->lpszIssuerInfo
= LocalAlloc(0, len
);
1844 if(info
->lpszIssuerInfo
)
1845 CertNameToStrA(context
->dwCertEncodingType
,
1846 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
,
1847 info
->lpszIssuerInfo
, len
);
1848 info
->dwKeySize
= NETCON_GetCipherStrength(&req
->netConnection
);
1849 CertFreeCertificateContext(context
);
1850 return ERROR_SUCCESS
;
1855 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
1858 static DWORD
HTTPREQ_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
1860 http_request_t
*req
= (http_request_t
*)hdr
;
1863 case INTERNET_OPTION_SECURITY_FLAGS
:
1867 if (!buffer
|| size
!= sizeof(DWORD
))
1868 return ERROR_INVALID_PARAMETER
;
1869 flags
= *(DWORD
*)buffer
;
1870 TRACE("%08x\n", flags
);
1871 req
->netConnection
.security_flags
= flags
;
1872 return ERROR_SUCCESS
;
1874 case INTERNET_OPTION_SEND_TIMEOUT
:
1875 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
1876 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1878 if (size
!= sizeof(DWORD
))
1879 return ERROR_INVALID_PARAMETER
;
1881 return NETCON_set_timeout(&req
->netConnection
, option
== INTERNET_OPTION_SEND_TIMEOUT
,
1884 case INTERNET_OPTION_USERNAME
:
1885 HeapFree(GetProcessHeap(), 0, req
->lpHttpSession
->lpszUserName
);
1886 if (!(req
->lpHttpSession
->lpszUserName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
1887 return ERROR_SUCCESS
;
1889 case INTERNET_OPTION_PASSWORD
:
1890 HeapFree(GetProcessHeap(), 0, req
->lpHttpSession
->lpszPassword
);
1891 if (!(req
->lpHttpSession
->lpszPassword
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
1892 return ERROR_SUCCESS
;
1893 case INTERNET_OPTION_HTTP_DECODING
:
1894 if(size
!= sizeof(BOOL
))
1895 return ERROR_INVALID_PARAMETER
;
1896 req
->decoding
= *(BOOL
*)buffer
;
1897 return ERROR_SUCCESS
;
1900 return ERROR_INTERNET_INVALID_OPTION
;
1903 /* read some more data into the read buffer (the read section must be held) */
1904 static DWORD
read_more_data( http_request_t
*req
, int maxlen
)
1911 /* move existing data to the start of the buffer */
1913 memmove( req
->read_buf
, req
->read_buf
+ req
->read_pos
, req
->read_size
);
1917 if (maxlen
== -1) maxlen
= sizeof(req
->read_buf
);
1919 res
= NETCON_recv( &req
->netConnection
, req
->read_buf
+ req
->read_size
,
1920 maxlen
- req
->read_size
, 0, &len
);
1921 if(res
== ERROR_SUCCESS
)
1922 req
->read_size
+= len
;
1927 /* remove some amount of data from the read buffer (the read section must be held) */
1928 static void remove_data( http_request_t
*req
, int count
)
1930 if (!(req
->read_size
-= count
)) req
->read_pos
= 0;
1931 else req
->read_pos
+= count
;
1934 static BOOL
read_line( http_request_t
*req
, LPSTR buffer
, DWORD
*len
)
1936 int count
, bytes_read
, pos
= 0;
1939 EnterCriticalSection( &req
->read_section
);
1942 BYTE
*eol
= memchr( req
->read_buf
+ req
->read_pos
, '\n', req
->read_size
);
1946 count
= eol
- (req
->read_buf
+ req
->read_pos
);
1947 bytes_read
= count
+ 1;
1949 else count
= bytes_read
= req
->read_size
;
1951 count
= min( count
, *len
- pos
);
1952 memcpy( buffer
+ pos
, req
->read_buf
+ req
->read_pos
, count
);
1954 remove_data( req
, bytes_read
);
1957 if ((res
= read_more_data( req
, -1 )) != ERROR_SUCCESS
|| !req
->read_size
)
1960 TRACE( "returning empty string\n" );
1961 LeaveCriticalSection( &req
->read_section
);
1962 INTERNET_SetLastError(res
);
1966 LeaveCriticalSection( &req
->read_section
);
1970 if (pos
&& buffer
[pos
- 1] == '\r') pos
--;
1973 buffer
[*len
- 1] = 0;
1974 TRACE( "returning %s\n", debugstr_a(buffer
));
1978 /* discard data contents until we reach end of line (the read section must be held) */
1979 static DWORD
discard_eol( http_request_t
*req
)
1985 BYTE
*eol
= memchr( req
->read_buf
+ req
->read_pos
, '\n', req
->read_size
);
1988 remove_data( req
, (eol
+ 1) - (req
->read_buf
+ req
->read_pos
) );
1991 req
->read_pos
= req
->read_size
= 0; /* discard everything */
1992 if ((res
= read_more_data( req
, -1 )) != ERROR_SUCCESS
) return res
;
1993 } while (req
->read_size
);
1994 return ERROR_SUCCESS
;
1997 /* read the size of the next chunk (the read section must be held) */
1998 static DWORD
start_next_chunk( http_request_t
*req
)
2000 DWORD chunk_size
= 0, res
;
2002 if (!req
->dwContentLength
) return ERROR_SUCCESS
;
2003 if (req
->dwContentLength
== req
->dwContentRead
)
2005 /* read terminator for the previous chunk */
2006 if ((res
= discard_eol( req
)) != ERROR_SUCCESS
) return res
;
2007 req
->dwContentLength
= ~0u;
2008 req
->dwContentRead
= 0;
2012 while (req
->read_size
)
2014 char ch
= req
->read_buf
[req
->read_pos
];
2015 if (ch
>= '0' && ch
<= '9') chunk_size
= chunk_size
* 16 + ch
- '0';
2016 else if (ch
>= 'a' && ch
<= 'f') chunk_size
= chunk_size
* 16 + ch
- 'a' + 10;
2017 else if (ch
>= 'A' && ch
<= 'F') chunk_size
= chunk_size
* 16 + ch
- 'A' + 10;
2018 else if (ch
== ';' || ch
== '\r' || ch
== '\n')
2020 TRACE( "reading %u byte chunk\n", chunk_size
);
2021 req
->dwContentLength
= chunk_size
;
2022 req
->dwContentRead
= 0;
2023 return discard_eol( req
);
2025 remove_data( req
, 1 );
2027 if ((res
= read_more_data( req
, -1 )) != ERROR_SUCCESS
) return res
;
2028 if (!req
->read_size
)
2030 req
->dwContentLength
= req
->dwContentRead
= 0;
2031 return ERROR_SUCCESS
;
2036 /* check if we have reached the end of the data to read (the read section must be held) */
2037 static BOOL
end_of_read_data( http_request_t
*req
)
2039 if (req
->gzip_stream
) return req
->gzip_stream
->end_of_data
&& !req
->gzip_stream
->buf_size
;
2040 if (req
->read_chunked
) return (req
->dwContentLength
== 0);
2041 if (req
->dwContentLength
== ~0u) return FALSE
;
2042 return (req
->dwContentLength
== req
->dwContentRead
);
2045 /* fetch some more data into the read buffer (the read section must be held) */
2046 static DWORD
refill_buffer( http_request_t
*req
)
2048 int len
= sizeof(req
->read_buf
);
2051 if (req
->read_chunked
&& (req
->dwContentLength
== ~0u || req
->dwContentLength
== req
->dwContentRead
))
2053 if ((res
= start_next_chunk( req
)) != ERROR_SUCCESS
) return res
;
2056 if (req
->dwContentLength
!= ~0u) len
= min( len
, req
->dwContentLength
- req
->dwContentRead
);
2057 if (len
<= req
->read_size
) return ERROR_SUCCESS
;
2059 if ((res
= read_more_data( req
, len
)) != ERROR_SUCCESS
) return res
;
2060 if (!req
->read_size
) req
->dwContentLength
= req
->dwContentRead
= 0;
2061 return ERROR_SUCCESS
;
2064 static DWORD
read_gzip_data(http_request_t
*req
, BYTE
*buf
, int size
, BOOL sync
, int *read_ret
)
2066 DWORD ret
= ERROR_SUCCESS
;
2070 z_stream
*zstream
= &req
->gzip_stream
->zstream
;
2074 while(read
< size
&& !req
->gzip_stream
->end_of_data
) {
2075 if(!req
->read_size
) {
2076 if(!sync
|| refill_buffer(req
) != ERROR_SUCCESS
)
2080 if(req
->dwContentRead
== req
->dwContentLength
)
2083 buf_avail
= req
->dwContentLength
== ~0 ? req
->read_size
: min(req
->read_size
, req
->dwContentLength
-req
->dwContentRead
);
2085 zstream
->next_in
= req
->read_buf
+req
->read_pos
;
2086 zstream
->avail_in
= buf_avail
;
2087 zstream
->next_out
= buf
+read
;
2088 zstream
->avail_out
= size
-read
;
2089 zres
= inflate(zstream
, Z_FULL_FLUSH
);
2090 read
= size
- zstream
->avail_out
;
2091 req
->dwContentRead
+= buf_avail
-zstream
->avail_in
;
2092 remove_data(req
, buf_avail
-zstream
->avail_in
);
2093 if(zres
== Z_STREAM_END
) {
2094 TRACE("end of data\n");
2095 req
->gzip_stream
->end_of_data
= TRUE
;
2096 inflateEnd(&req
->gzip_stream
->zstream
);
2097 }else if(zres
!= Z_OK
) {
2098 WARN("inflate failed %d\n", zres
);
2100 ret
= ERROR_INTERNET_DECODING_FAILED
;
2110 static void refill_gzip_buffer(http_request_t
*req
)
2115 if(!req
->gzip_stream
|| !req
->read_size
|| req
->gzip_stream
->buf_size
== sizeof(req
->gzip_stream
->buf
))
2118 if(req
->gzip_stream
->buf_pos
) {
2119 if(req
->gzip_stream
->buf_size
)
2120 memmove(req
->gzip_stream
->buf
, req
->gzip_stream
->buf
+ req
->gzip_stream
->buf_pos
, req
->gzip_stream
->buf_size
);
2121 req
->gzip_stream
->buf_pos
= 0;
2124 res
= read_gzip_data(req
, req
->gzip_stream
->buf
+ req
->gzip_stream
->buf_size
,
2125 sizeof(req
->gzip_stream
->buf
) - req
->gzip_stream
->buf_size
, FALSE
, &len
);
2126 if(res
== ERROR_SUCCESS
)
2127 req
->gzip_stream
->buf_size
+= len
;
2130 /* return the size of data available to be read immediately (the read section must be held) */
2131 static DWORD
get_avail_data( http_request_t
*req
)
2133 if (req
->gzip_stream
) {
2134 refill_gzip_buffer(req
);
2135 return req
->gzip_stream
->buf_size
;
2137 if (req
->read_chunked
&& (req
->dwContentLength
== ~0u || req
->dwContentLength
== req
->dwContentRead
))
2139 return min( req
->read_size
, req
->dwContentLength
- req
->dwContentRead
);
2142 static void HTTP_ReceiveRequestData(http_request_t
*req
, BOOL first_notif
)
2144 INTERNET_ASYNC_RESULT iar
;
2149 EnterCriticalSection( &req
->read_section
);
2150 if ((res
= refill_buffer( req
)) == ERROR_SUCCESS
) {
2151 iar
.dwResult
= (DWORD_PTR
)req
->hdr
.hInternet
;
2152 iar
.dwError
= first_notif
? 0 : get_avail_data(req
);
2157 LeaveCriticalSection( &req
->read_section
);
2159 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2160 sizeof(INTERNET_ASYNC_RESULT
));
2163 /* read data from the http connection (the read section must be held) */
2164 static DWORD
HTTPREQ_Read(http_request_t
*req
, void *buffer
, DWORD size
, DWORD
*read
, BOOL sync
)
2166 BOOL finished_reading
= FALSE
;
2167 int len
, bytes_read
= 0;
2168 DWORD ret
= ERROR_SUCCESS
;
2170 EnterCriticalSection( &req
->read_section
);
2172 if (req
->read_chunked
&& (req
->dwContentLength
== ~0u || req
->dwContentLength
== req
->dwContentRead
))
2174 if (start_next_chunk( req
) != ERROR_SUCCESS
) goto done
;
2177 if(req
->gzip_stream
) {
2178 if(req
->gzip_stream
->buf_size
) {
2179 bytes_read
= min(req
->gzip_stream
->buf_size
, size
);
2180 memcpy(buffer
, req
->gzip_stream
->buf
+ req
->gzip_stream
->buf_pos
, bytes_read
);
2181 req
->gzip_stream
->buf_pos
+= bytes_read
;
2182 req
->gzip_stream
->buf_size
-= bytes_read
;
2183 }else if(!req
->read_size
&& !req
->gzip_stream
->end_of_data
) {
2187 if(size
> bytes_read
) {
2188 ret
= read_gzip_data(req
, (BYTE
*)buffer
+bytes_read
, size
-bytes_read
, sync
, &len
);
2189 if(ret
== ERROR_SUCCESS
)
2193 finished_reading
= req
->gzip_stream
->end_of_data
&& !req
->gzip_stream
->buf_size
;
2195 if (req
->dwContentLength
!= ~0u) size
= min( size
, req
->dwContentLength
- req
->dwContentRead
);
2197 if (req
->read_size
) {
2198 bytes_read
= min( req
->read_size
, size
);
2199 memcpy( buffer
, req
->read_buf
+ req
->read_pos
, bytes_read
);
2200 remove_data( req
, bytes_read
);
2203 if (size
> bytes_read
&& (!bytes_read
|| sync
)) {
2204 if (NETCON_recv( &req
->netConnection
, (char *)buffer
+ bytes_read
, size
- bytes_read
,
2205 sync
? MSG_WAITALL
: 0, &len
) == ERROR_SUCCESS
)
2207 /* always return success, even if the network layer returns an error */
2210 finished_reading
= !bytes_read
&& req
->dwContentRead
== req
->dwContentLength
;
2211 req
->dwContentRead
+= bytes_read
;
2216 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read
, req
->dwContentRead
, req
->dwContentLength
);
2217 LeaveCriticalSection( &req
->read_section
);
2219 if(ret
== ERROR_SUCCESS
&& req
->lpszCacheFile
) {
2221 DWORD dwBytesWritten
;
2223 res
= WriteFile(req
->hCacheFile
, buffer
, bytes_read
, &dwBytesWritten
, NULL
);
2225 WARN("WriteFile failed: %u\n", GetLastError());
2228 if(finished_reading
)
2229 HTTP_FinishedReading(req
);
2235 static DWORD
HTTPREQ_ReadFile(object_header_t
*hdr
, void *buffer
, DWORD size
, DWORD
*read
)
2237 http_request_t
*req
= (http_request_t
*)hdr
;
2240 EnterCriticalSection( &req
->read_section
);
2241 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2242 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2244 res
= HTTPREQ_Read(req
, buffer
, size
, read
, TRUE
);
2245 if(res
== ERROR_SUCCESS
)
2247 LeaveCriticalSection( &req
->read_section
);
2252 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST
*workRequest
)
2254 struct WORKREQ_INTERNETREADFILEEXA
const *data
= &workRequest
->u
.InternetReadFileExA
;
2255 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2256 INTERNET_ASYNC_RESULT iar
;
2259 TRACE("INTERNETREADFILEEXA %p\n", workRequest
->hdr
);
2261 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2262 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2264 iar
.dwResult
= res
== ERROR_SUCCESS
;
2267 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2268 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2269 sizeof(INTERNET_ASYNC_RESULT
));
2272 static DWORD
HTTPREQ_ReadFileExA(object_header_t
*hdr
, INTERNET_BUFFERSA
*buffers
,
2273 DWORD flags
, DWORD_PTR context
)
2275 http_request_t
*req
= (http_request_t
*)hdr
;
2276 DWORD res
, size
, read
, error
= ERROR_SUCCESS
;
2278 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2279 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2281 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2282 return ERROR_INVALID_PARAMETER
;
2284 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2286 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2288 WORKREQUEST workRequest
;
2290 if (TryEnterCriticalSection( &req
->read_section
))
2292 if (get_avail_data(req
))
2294 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2295 &buffers
->dwBufferLength
, FALSE
);
2296 size
= buffers
->dwBufferLength
;
2297 LeaveCriticalSection( &req
->read_section
);
2300 LeaveCriticalSection( &req
->read_section
);
2303 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExAProc
;
2304 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2305 workRequest
.u
.InternetReadFileExA
.lpBuffersOut
= buffers
;
2307 INTERNET_AsyncCall(&workRequest
);
2309 return ERROR_IO_PENDING
;
2313 size
= buffers
->dwBufferLength
;
2315 EnterCriticalSection( &req
->read_section
);
2316 if(hdr
->dwError
== ERROR_SUCCESS
)
2317 hdr
->dwError
= INTERNET_HANDLE_IN_USE
;
2318 else if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2319 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2322 res
= HTTPREQ_Read(req
, (char*)buffers
->lpvBuffer
+read
, size
-read
,
2323 &buffers
->dwBufferLength
, !(flags
& IRF_NO_WAIT
));
2324 if(res
== ERROR_SUCCESS
)
2325 read
+= buffers
->dwBufferLength
;
2329 if(!req
->read_chunked
|| read
==size
|| req
->dwContentLength
!=req
->dwContentRead
2330 || !req
->dwContentLength
|| (req
->gzip_stream
&& req
->gzip_stream
->end_of_data
))
2332 LeaveCriticalSection( &req
->read_section
);
2334 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2335 &buffers
->dwBufferLength
, sizeof(buffers
->dwBufferLength
));
2336 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2337 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2339 EnterCriticalSection( &req
->read_section
);
2342 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2343 hdr
->dwError
= ERROR_SUCCESS
;
2345 error
= hdr
->dwError
;
2347 LeaveCriticalSection( &req
->read_section
);
2348 size
= buffers
->dwBufferLength
;
2349 buffers
->dwBufferLength
= read
;
2352 if (res
== ERROR_SUCCESS
) {
2353 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2354 &size
, sizeof(size
));
2357 return res
==ERROR_SUCCESS
? error
: res
;
2360 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST
*workRequest
)
2362 struct WORKREQ_INTERNETREADFILEEXW
const *data
= &workRequest
->u
.InternetReadFileExW
;
2363 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2364 INTERNET_ASYNC_RESULT iar
;
2367 TRACE("INTERNETREADFILEEXW %p\n", workRequest
->hdr
);
2369 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2370 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2372 iar
.dwResult
= res
== ERROR_SUCCESS
;
2375 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2376 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2377 sizeof(INTERNET_ASYNC_RESULT
));
2380 static DWORD
HTTPREQ_ReadFileExW(object_header_t
*hdr
, INTERNET_BUFFERSW
*buffers
,
2381 DWORD flags
, DWORD_PTR context
)
2384 http_request_t
*req
= (http_request_t
*)hdr
;
2385 DWORD res
, size
, read
, error
= ERROR_SUCCESS
;
2387 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2388 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2390 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2391 return ERROR_INVALID_PARAMETER
;
2393 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2395 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2397 WORKREQUEST workRequest
;
2399 if (TryEnterCriticalSection( &req
->read_section
))
2401 if (get_avail_data(req
))
2403 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2404 &buffers
->dwBufferLength
, FALSE
);
2405 size
= buffers
->dwBufferLength
;
2406 LeaveCriticalSection( &req
->read_section
);
2409 LeaveCriticalSection( &req
->read_section
);
2412 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExWProc
;
2413 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2414 workRequest
.u
.InternetReadFileExW
.lpBuffersOut
= buffers
;
2416 INTERNET_AsyncCall(&workRequest
);
2418 return ERROR_IO_PENDING
;
2422 size
= buffers
->dwBufferLength
;
2424 EnterCriticalSection( &req
->read_section
);
2425 if(hdr
->dwError
== ERROR_SUCCESS
)
2426 hdr
->dwError
= INTERNET_HANDLE_IN_USE
;
2427 else if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2428 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2431 res
= HTTPREQ_Read(req
, (char*)buffers
->lpvBuffer
+read
, size
-read
,
2432 &buffers
->dwBufferLength
, !(flags
& IRF_NO_WAIT
));
2433 if(res
== ERROR_SUCCESS
)
2434 read
+= buffers
->dwBufferLength
;
2438 if(!req
->read_chunked
|| read
==size
|| req
->dwContentLength
!=req
->dwContentRead
2439 || !req
->dwContentLength
|| (req
->gzip_stream
&& req
->gzip_stream
->end_of_data
))
2441 LeaveCriticalSection( &req
->read_section
);
2443 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2444 &buffers
->dwBufferLength
, sizeof(buffers
->dwBufferLength
));
2445 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2446 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2448 EnterCriticalSection( &req
->read_section
);
2451 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2452 hdr
->dwError
= ERROR_SUCCESS
;
2454 error
= hdr
->dwError
;
2456 LeaveCriticalSection( &req
->read_section
);
2457 size
= buffers
->dwBufferLength
;
2458 buffers
->dwBufferLength
= read
;
2461 if (res
== ERROR_SUCCESS
) {
2462 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2463 &size
, sizeof(size
));
2466 return res
==ERROR_SUCCESS
? error
: res
;
2469 static DWORD
HTTPREQ_WriteFile(object_header_t
*hdr
, const void *buffer
, DWORD size
, DWORD
*written
)
2472 http_request_t
*lpwhr
= (http_request_t
*)hdr
;
2474 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
2477 res
= NETCON_send(&lpwhr
->netConnection
, buffer
, size
, 0, (LPINT
)written
);
2478 if (res
== ERROR_SUCCESS
)
2479 lpwhr
->dwBytesWritten
+= *written
;
2481 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_SENT
, written
, sizeof(DWORD
));
2485 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST
*workRequest
)
2487 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2489 HTTP_ReceiveRequestData(req
, FALSE
);
2492 static DWORD
HTTPREQ_QueryDataAvailable(object_header_t
*hdr
, DWORD
*available
, DWORD flags
, DWORD_PTR ctx
)
2494 http_request_t
*req
= (http_request_t
*)hdr
;
2496 TRACE("(%p %p %x %lx)\n", req
, available
, flags
, ctx
);
2498 if (req
->lpHttpSession
->lpAppInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
2500 WORKREQUEST workRequest
;
2502 /* never wait, if we can't enter the section we queue an async request right away */
2503 if (TryEnterCriticalSection( &req
->read_section
))
2505 if ((*available
= get_avail_data( req
))) goto done
;
2506 if (end_of_read_data( req
)) goto done
;
2507 LeaveCriticalSection( &req
->read_section
);
2510 workRequest
.asyncproc
= HTTPREQ_AsyncQueryDataAvailableProc
;
2511 workRequest
.hdr
= WININET_AddRef( &req
->hdr
);
2513 INTERNET_AsyncCall(&workRequest
);
2515 return ERROR_IO_PENDING
;
2518 EnterCriticalSection( &req
->read_section
);
2520 if (!(*available
= get_avail_data( req
)) && !end_of_read_data( req
))
2522 refill_buffer( req
);
2523 *available
= get_avail_data( req
);
2527 if (*available
== sizeof(req
->read_buf
) && !req
->gzip_stream
) /* check if we have even more pending in the socket */
2530 if (NETCON_query_data_available(&req
->netConnection
, &extra
))
2531 *available
= min( *available
+ extra
, req
->dwContentLength
- req
->dwContentRead
);
2533 LeaveCriticalSection( &req
->read_section
);
2535 TRACE( "returning %u\n", *available
);
2536 return ERROR_SUCCESS
;
2539 static const object_vtbl_t HTTPREQVtbl
= {
2541 HTTPREQ_CloseConnection
,
2542 HTTPREQ_QueryOption
,
2545 HTTPREQ_ReadFileExA
,
2546 HTTPREQ_ReadFileExW
,
2548 HTTPREQ_QueryDataAvailable
,
2552 /***********************************************************************
2553 * HTTP_HttpOpenRequestW (internal)
2555 * Open a HTTP request handle
2558 * HINTERNET a HTTP request handle on success
2562 static DWORD
HTTP_HttpOpenRequestW(http_session_t
*lpwhs
,
2563 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
2564 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
2565 DWORD dwFlags
, DWORD_PTR dwContext
, HINTERNET
*ret
)
2567 appinfo_t
*hIC
= NULL
;
2568 http_request_t
*lpwhr
;
2569 LPWSTR lpszHostName
= NULL
;
2570 static const WCHAR szHostForm
[] = {'%','s',':','%','u',0};
2575 assert( lpwhs
->hdr
.htype
== WH_HHTTPSESSION
);
2576 hIC
= lpwhs
->lpAppInfo
;
2578 lpwhr
= alloc_object(&lpwhs
->hdr
, &HTTPREQVtbl
, sizeof(http_request_t
));
2580 return ERROR_OUTOFMEMORY
;
2582 lpwhr
->hdr
.htype
= WH_HHTTPREQ
;
2583 lpwhr
->hdr
.dwFlags
= dwFlags
;
2584 lpwhr
->hdr
.dwContext
= dwContext
;
2585 lpwhr
->dwContentLength
= ~0u;
2587 InitializeCriticalSection( &lpwhr
->read_section
);
2589 WININET_AddRef( &lpwhs
->hdr
);
2590 lpwhr
->lpHttpSession
= lpwhs
;
2591 list_add_head( &lpwhs
->hdr
.children
, &lpwhr
->hdr
.entry
);
2593 lpszHostName
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
) *
2594 (strlenW(lpwhs
->lpszHostName
) + 7 /* length of ":65535" + 1 */));
2595 if (NULL
== lpszHostName
)
2597 res
= ERROR_OUTOFMEMORY
;
2601 if ((res
= NETCON_init(&lpwhr
->netConnection
, dwFlags
& INTERNET_FLAG_SECURE
)) != ERROR_SUCCESS
)
2604 if (lpszObjectName
&& *lpszObjectName
) {
2608 rc
= UrlEscapeW(lpszObjectName
, NULL
, &len
, URL_ESCAPE_SPACES_ONLY
);
2609 if (rc
!= E_POINTER
)
2610 len
= strlenW(lpszObjectName
)+1;
2611 lpwhr
->lpszPath
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
2612 rc
= UrlEscapeW(lpszObjectName
, lpwhr
->lpszPath
, &len
,
2613 URL_ESCAPE_SPACES_ONLY
);
2616 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName
),rc
);
2617 strcpyW(lpwhr
->lpszPath
,lpszObjectName
);
2620 static const WCHAR slashW
[] = {'/',0};
2622 lpwhr
->lpszPath
= heap_strdupW(slashW
);
2625 if (lpszReferrer
&& *lpszReferrer
)
2626 HTTP_ProcessHeader(lpwhr
, HTTP_REFERER
, lpszReferrer
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2628 if (lpszAcceptTypes
)
2631 for (i
= 0; lpszAcceptTypes
[i
]; i
++)
2633 if (!*lpszAcceptTypes
[i
]) continue;
2634 HTTP_ProcessHeader(lpwhr
, HTTP_ACCEPT
, lpszAcceptTypes
[i
],
2635 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
|
2636 HTTP_ADDHDR_FLAG_REQ
|
2637 (i
== 0 ? HTTP_ADDHDR_FLAG_REPLACE
: 0));
2641 lpwhr
->lpszVerb
= heap_strdupW(lpszVerb
&& *lpszVerb
? lpszVerb
: szGET
);
2642 lpwhr
->lpszVersion
= heap_strdupW(lpszVersion
? lpszVersion
: g_szHttp1_1
);
2644 if (lpwhs
->nHostPort
!= INTERNET_INVALID_PORT_NUMBER
&&
2645 lpwhs
->nHostPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
2646 lpwhs
->nHostPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
2648 sprintfW(lpszHostName
, szHostForm
, lpwhs
->lpszHostName
, lpwhs
->nHostPort
);
2649 HTTP_ProcessHeader(lpwhr
, hostW
, lpszHostName
,
2650 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2653 HTTP_ProcessHeader(lpwhr
, hostW
, lpwhs
->lpszHostName
,
2654 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2656 if (lpwhs
->nServerPort
== INTERNET_INVALID_PORT_NUMBER
)
2657 lpwhs
->nServerPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
2658 INTERNET_DEFAULT_HTTPS_PORT
:
2659 INTERNET_DEFAULT_HTTP_PORT
);
2661 if (lpwhs
->nHostPort
== INTERNET_INVALID_PORT_NUMBER
)
2662 lpwhs
->nHostPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
2663 INTERNET_DEFAULT_HTTPS_PORT
:
2664 INTERNET_DEFAULT_HTTP_PORT
);
2666 if (NULL
!= hIC
->lpszProxy
&& hIC
->lpszProxy
[0] != 0)
2667 HTTP_DealWithProxy( hIC
, lpwhs
, lpwhr
);
2669 INTERNET_SendCallback(&lpwhs
->hdr
, dwContext
,
2670 INTERNET_STATUS_HANDLE_CREATED
, &lpwhr
->hdr
.hInternet
,
2674 TRACE("<-- %u (%p)\n", res
, lpwhr
);
2676 HeapFree(GetProcessHeap(), 0, lpszHostName
);
2677 if(res
!= ERROR_SUCCESS
) {
2678 WININET_Release( &lpwhr
->hdr
);
2683 *ret
= lpwhr
->hdr
.hInternet
;
2684 return ERROR_SUCCESS
;
2687 /***********************************************************************
2688 * HttpOpenRequestW (WININET.@)
2690 * Open a HTTP request handle
2693 * HINTERNET a HTTP request handle on success
2697 HINTERNET WINAPI
HttpOpenRequestW(HINTERNET hHttpSession
,
2698 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
2699 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
2700 DWORD dwFlags
, DWORD_PTR dwContext
)
2702 http_session_t
*lpwhs
;
2703 HINTERNET handle
= NULL
;
2706 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
2707 debugstr_w(lpszVerb
), debugstr_w(lpszObjectName
),
2708 debugstr_w(lpszVersion
), debugstr_w(lpszReferrer
), lpszAcceptTypes
,
2709 dwFlags
, dwContext
);
2710 if(lpszAcceptTypes
!=NULL
)
2713 for(i
=0;lpszAcceptTypes
[i
]!=NULL
;i
++)
2714 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes
[i
]));
2717 lpwhs
= (http_session_t
*) get_handle_object( hHttpSession
);
2718 if (NULL
== lpwhs
|| lpwhs
->hdr
.htype
!= WH_HHTTPSESSION
)
2720 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
2725 * My tests seem to show that the windows version does not
2726 * become asynchronous until after this point. And anyhow
2727 * if this call was asynchronous then how would you get the
2728 * necessary HINTERNET pointer returned by this function.
2731 res
= HTTP_HttpOpenRequestW(lpwhs
, lpszVerb
, lpszObjectName
,
2732 lpszVersion
, lpszReferrer
, lpszAcceptTypes
,
2733 dwFlags
, dwContext
, &handle
);
2736 WININET_Release( &lpwhs
->hdr
);
2737 TRACE("returning %p\n", handle
);
2738 if(res
!= ERROR_SUCCESS
)
2743 /* read any content returned by the server so that the connection can be
2745 static void HTTP_DrainContent(http_request_t
*req
)
2749 if (!NETCON_connected(&req
->netConnection
)) return;
2751 if (req
->dwContentLength
== -1)
2753 NETCON_close(&req
->netConnection
);
2756 if (!strcmpW(req
->lpszVerb
, szHEAD
)) return;
2761 if (HTTPREQ_Read(req
, buffer
, sizeof(buffer
), &bytes_read
, TRUE
) != ERROR_SUCCESS
)
2763 } while (bytes_read
);
2766 static const LPCWSTR header_lookup
[] = {
2767 szMime_Version
, /* HTTP_QUERY_MIME_VERSION = 0 */
2768 szContent_Type
, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2769 szContent_Transfer_Encoding
,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2770 szContent_ID
, /* HTTP_QUERY_CONTENT_ID = 3 */
2771 NULL
, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2772 szContent_Length
, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2773 szContent_Language
, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2774 szAllow
, /* HTTP_QUERY_ALLOW = 7 */
2775 szPublic
, /* HTTP_QUERY_PUBLIC = 8 */
2776 szDate
, /* HTTP_QUERY_DATE = 9 */
2777 szExpires
, /* HTTP_QUERY_EXPIRES = 10 */
2778 szLast_Modified
, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2779 NULL
, /* HTTP_QUERY_MESSAGE_ID = 12 */
2780 szURI
, /* HTTP_QUERY_URI = 13 */
2781 szFrom
, /* HTTP_QUERY_DERIVED_FROM = 14 */
2782 NULL
, /* HTTP_QUERY_COST = 15 */
2783 NULL
, /* HTTP_QUERY_LINK = 16 */
2784 szPragma
, /* HTTP_QUERY_PRAGMA = 17 */
2785 NULL
, /* HTTP_QUERY_VERSION = 18 */
2786 szStatus
, /* HTTP_QUERY_STATUS_CODE = 19 */
2787 NULL
, /* HTTP_QUERY_STATUS_TEXT = 20 */
2788 NULL
, /* HTTP_QUERY_RAW_HEADERS = 21 */
2789 NULL
, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2790 szConnection
, /* HTTP_QUERY_CONNECTION = 23 */
2791 szAccept
, /* HTTP_QUERY_ACCEPT = 24 */
2792 szAccept_Charset
, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2793 szAccept_Encoding
, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2794 szAccept_Language
, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2795 szAuthorization
, /* HTTP_QUERY_AUTHORIZATION = 28 */
2796 szContent_Encoding
, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2797 NULL
, /* HTTP_QUERY_FORWARDED = 30 */
2798 NULL
, /* HTTP_QUERY_FROM = 31 */
2799 szIf_Modified_Since
, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2800 szLocation
, /* HTTP_QUERY_LOCATION = 33 */
2801 NULL
, /* HTTP_QUERY_ORIG_URI = 34 */
2802 szReferer
, /* HTTP_QUERY_REFERER = 35 */
2803 szRetry_After
, /* HTTP_QUERY_RETRY_AFTER = 36 */
2804 szServer
, /* HTTP_QUERY_SERVER = 37 */
2805 NULL
, /* HTTP_TITLE = 38 */
2806 szUser_Agent
, /* HTTP_QUERY_USER_AGENT = 39 */
2807 szWWW_Authenticate
, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2808 szProxy_Authenticate
, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2809 szAccept_Ranges
, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2810 szSet_Cookie
, /* HTTP_QUERY_SET_COOKIE = 43 */
2811 szCookie
, /* HTTP_QUERY_COOKIE = 44 */
2812 NULL
, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2813 NULL
, /* HTTP_QUERY_REFRESH = 46 */
2814 NULL
, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2815 szAge
, /* HTTP_QUERY_AGE = 48 */
2816 szCache_Control
, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2817 szContent_Base
, /* HTTP_QUERY_CONTENT_BASE = 50 */
2818 szContent_Location
, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2819 szContent_MD5
, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2820 szContent_Range
, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2821 szETag
, /* HTTP_QUERY_ETAG = 54 */
2822 hostW
, /* HTTP_QUERY_HOST = 55 */
2823 szIf_Match
, /* HTTP_QUERY_IF_MATCH = 56 */
2824 szIf_None_Match
, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2825 szIf_Range
, /* HTTP_QUERY_IF_RANGE = 58 */
2826 szIf_Unmodified_Since
, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2827 szMax_Forwards
, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2828 szProxy_Authorization
, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2829 szRange
, /* HTTP_QUERY_RANGE = 62 */
2830 szTransfer_Encoding
, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2831 szUpgrade
, /* HTTP_QUERY_UPGRADE = 64 */
2832 szVary
, /* HTTP_QUERY_VARY = 65 */
2833 szVia
, /* HTTP_QUERY_VIA = 66 */
2834 szWarning
, /* HTTP_QUERY_WARNING = 67 */
2835 szExpect
, /* HTTP_QUERY_EXPECT = 68 */
2836 szProxy_Connection
, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2837 szUnless_Modified_Since
, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2840 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2842 /***********************************************************************
2843 * HTTP_HttpQueryInfoW (internal)
2845 static DWORD
HTTP_HttpQueryInfoW(http_request_t
*lpwhr
, DWORD dwInfoLevel
,
2846 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
2848 LPHTTPHEADERW lphttpHdr
= NULL
;
2849 BOOL request_only
= dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
;
2850 INT requested_index
= lpdwIndex
? *lpdwIndex
: 0;
2851 DWORD level
= (dwInfoLevel
& ~HTTP_QUERY_MODIFIER_FLAGS_MASK
);
2854 /* Find requested header structure */
2857 case HTTP_QUERY_CUSTOM
:
2858 if (!lpBuffer
) return ERROR_INVALID_PARAMETER
;
2859 index
= HTTP_GetCustomHeaderIndex(lpwhr
, lpBuffer
, requested_index
, request_only
);
2861 case HTTP_QUERY_RAW_HEADERS_CRLF
:
2865 DWORD res
= ERROR_INVALID_PARAMETER
;
2868 headers
= HTTP_BuildHeaderRequestString(lpwhr
, lpwhr
->lpszVerb
, lpwhr
->lpszPath
, lpwhr
->lpszVersion
);
2870 headers
= lpwhr
->lpszRawHeaders
;
2873 len
= strlenW(headers
) * sizeof(WCHAR
);
2875 if (len
+ sizeof(WCHAR
) > *lpdwBufferLength
)
2877 len
+= sizeof(WCHAR
);
2878 res
= ERROR_INSUFFICIENT_BUFFER
;
2883 memcpy(lpBuffer
, headers
, len
+ sizeof(WCHAR
));
2886 len
= strlenW(szCrLf
) * sizeof(WCHAR
);
2887 memcpy(lpBuffer
, szCrLf
, sizeof(szCrLf
));
2889 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
/ sizeof(WCHAR
)));
2890 res
= ERROR_SUCCESS
;
2892 *lpdwBufferLength
= len
;
2895 HeapFree(GetProcessHeap(), 0, headers
);
2898 case HTTP_QUERY_RAW_HEADERS
:
2900 LPWSTR
* ppszRawHeaderLines
= HTTP_Tokenize(lpwhr
->lpszRawHeaders
, szCrLf
);
2902 LPWSTR pszString
= lpBuffer
;
2904 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
2905 size
+= strlenW(ppszRawHeaderLines
[i
]) + 1;
2907 if (size
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
2909 HTTP_FreeTokens(ppszRawHeaderLines
);
2910 *lpdwBufferLength
= (size
+ 1) * sizeof(WCHAR
);
2911 return ERROR_INSUFFICIENT_BUFFER
;
2915 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
2917 DWORD len
= strlenW(ppszRawHeaderLines
[i
]);
2918 memcpy(pszString
, ppszRawHeaderLines
[i
], (len
+1)*sizeof(WCHAR
));
2922 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, size
));
2924 *lpdwBufferLength
= size
* sizeof(WCHAR
);
2925 HTTP_FreeTokens(ppszRawHeaderLines
);
2927 return ERROR_SUCCESS
;
2929 case HTTP_QUERY_STATUS_TEXT
:
2930 if (lpwhr
->lpszStatusText
)
2932 DWORD len
= strlenW(lpwhr
->lpszStatusText
);
2933 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
2935 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
2936 return ERROR_INSUFFICIENT_BUFFER
;
2940 memcpy(lpBuffer
, lpwhr
->lpszStatusText
, (len
+ 1) * sizeof(WCHAR
));
2941 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
2943 *lpdwBufferLength
= len
* sizeof(WCHAR
);
2944 return ERROR_SUCCESS
;
2947 case HTTP_QUERY_VERSION
:
2948 if (lpwhr
->lpszVersion
)
2950 DWORD len
= strlenW(lpwhr
->lpszVersion
);
2951 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
2953 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
2954 return ERROR_INSUFFICIENT_BUFFER
;
2958 memcpy(lpBuffer
, lpwhr
->lpszVersion
, (len
+ 1) * sizeof(WCHAR
));
2959 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
2961 *lpdwBufferLength
= len
* sizeof(WCHAR
);
2962 return ERROR_SUCCESS
;
2965 case HTTP_QUERY_CONTENT_ENCODING
:
2966 index
= HTTP_GetCustomHeaderIndex(lpwhr
, header_lookup
[lpwhr
->gzip_stream
? HTTP_QUERY_CONTENT_TYPE
: level
],
2967 requested_index
,request_only
);
2970 assert (LAST_TABLE_HEADER
== (HTTP_QUERY_UNLESS_MODIFIED_SINCE
+ 1));
2972 if (level
< LAST_TABLE_HEADER
&& header_lookup
[level
])
2973 index
= HTTP_GetCustomHeaderIndex(lpwhr
, header_lookup
[level
],
2974 requested_index
,request_only
);
2978 lphttpHdr
= &lpwhr
->pCustHeaders
[index
];
2980 /* Ensure header satisfies requested attributes */
2982 ((dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
) &&
2983 (~lphttpHdr
->wFlags
& HDR_ISREQUEST
)))
2985 return ERROR_HTTP_HEADER_NOT_FOUND
;
2988 if (lpdwIndex
&& level
!= HTTP_QUERY_STATUS_CODE
) (*lpdwIndex
)++;
2990 /* coalesce value to requested type */
2991 if (dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
&& lpBuffer
)
2993 *(int *)lpBuffer
= atoiW(lphttpHdr
->lpszValue
);
2994 TRACE(" returning number: %d\n", *(int *)lpBuffer
);
2996 else if (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
&& lpBuffer
)
3002 tmpTime
= ConvertTimeString(lphttpHdr
->lpszValue
);
3004 tmpTM
= *gmtime(&tmpTime
);
3005 STHook
= (SYSTEMTIME
*)lpBuffer
;
3006 STHook
->wDay
= tmpTM
.tm_mday
;
3007 STHook
->wHour
= tmpTM
.tm_hour
;
3008 STHook
->wMilliseconds
= 0;
3009 STHook
->wMinute
= tmpTM
.tm_min
;
3010 STHook
->wDayOfWeek
= tmpTM
.tm_wday
;
3011 STHook
->wMonth
= tmpTM
.tm_mon
+ 1;
3012 STHook
->wSecond
= tmpTM
.tm_sec
;
3013 STHook
->wYear
= tmpTM
.tm_year
;
3015 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3016 STHook
->wYear
, STHook
->wMonth
, STHook
->wDay
, STHook
->wDayOfWeek
,
3017 STHook
->wHour
, STHook
->wMinute
, STHook
->wSecond
, STHook
->wMilliseconds
);
3019 else if (lphttpHdr
->lpszValue
)
3021 DWORD len
= (strlenW(lphttpHdr
->lpszValue
) + 1) * sizeof(WCHAR
);
3023 if (len
> *lpdwBufferLength
)
3025 *lpdwBufferLength
= len
;
3026 return ERROR_INSUFFICIENT_BUFFER
;
3030 memcpy(lpBuffer
, lphttpHdr
->lpszValue
, len
);
3031 TRACE("! returning string: %s\n", debugstr_w(lpBuffer
));
3033 *lpdwBufferLength
= len
- sizeof(WCHAR
);
3035 return ERROR_SUCCESS
;
3038 /***********************************************************************
3039 * HttpQueryInfoW (WININET.@)
3041 * Queries for information about an HTTP request
3048 BOOL WINAPI
HttpQueryInfoW(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3049 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3051 http_request_t
*lpwhr
;
3054 if (TRACE_ON(wininet
)) {
3055 #define FE(x) { x, #x }
3056 static const wininet_flag_info query_flags
[] = {
3057 FE(HTTP_QUERY_MIME_VERSION
),
3058 FE(HTTP_QUERY_CONTENT_TYPE
),
3059 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING
),
3060 FE(HTTP_QUERY_CONTENT_ID
),
3061 FE(HTTP_QUERY_CONTENT_DESCRIPTION
),
3062 FE(HTTP_QUERY_CONTENT_LENGTH
),
3063 FE(HTTP_QUERY_CONTENT_LANGUAGE
),
3064 FE(HTTP_QUERY_ALLOW
),
3065 FE(HTTP_QUERY_PUBLIC
),
3066 FE(HTTP_QUERY_DATE
),
3067 FE(HTTP_QUERY_EXPIRES
),
3068 FE(HTTP_QUERY_LAST_MODIFIED
),
3069 FE(HTTP_QUERY_MESSAGE_ID
),
3071 FE(HTTP_QUERY_DERIVED_FROM
),
3072 FE(HTTP_QUERY_COST
),
3073 FE(HTTP_QUERY_LINK
),
3074 FE(HTTP_QUERY_PRAGMA
),
3075 FE(HTTP_QUERY_VERSION
),
3076 FE(HTTP_QUERY_STATUS_CODE
),
3077 FE(HTTP_QUERY_STATUS_TEXT
),
3078 FE(HTTP_QUERY_RAW_HEADERS
),
3079 FE(HTTP_QUERY_RAW_HEADERS_CRLF
),
3080 FE(HTTP_QUERY_CONNECTION
),
3081 FE(HTTP_QUERY_ACCEPT
),
3082 FE(HTTP_QUERY_ACCEPT_CHARSET
),
3083 FE(HTTP_QUERY_ACCEPT_ENCODING
),
3084 FE(HTTP_QUERY_ACCEPT_LANGUAGE
),
3085 FE(HTTP_QUERY_AUTHORIZATION
),
3086 FE(HTTP_QUERY_CONTENT_ENCODING
),
3087 FE(HTTP_QUERY_FORWARDED
),
3088 FE(HTTP_QUERY_FROM
),
3089 FE(HTTP_QUERY_IF_MODIFIED_SINCE
),
3090 FE(HTTP_QUERY_LOCATION
),
3091 FE(HTTP_QUERY_ORIG_URI
),
3092 FE(HTTP_QUERY_REFERER
),
3093 FE(HTTP_QUERY_RETRY_AFTER
),
3094 FE(HTTP_QUERY_SERVER
),
3095 FE(HTTP_QUERY_TITLE
),
3096 FE(HTTP_QUERY_USER_AGENT
),
3097 FE(HTTP_QUERY_WWW_AUTHENTICATE
),
3098 FE(HTTP_QUERY_PROXY_AUTHENTICATE
),
3099 FE(HTTP_QUERY_ACCEPT_RANGES
),
3100 FE(HTTP_QUERY_SET_COOKIE
),
3101 FE(HTTP_QUERY_COOKIE
),
3102 FE(HTTP_QUERY_REQUEST_METHOD
),
3103 FE(HTTP_QUERY_REFRESH
),
3104 FE(HTTP_QUERY_CONTENT_DISPOSITION
),
3106 FE(HTTP_QUERY_CACHE_CONTROL
),
3107 FE(HTTP_QUERY_CONTENT_BASE
),
3108 FE(HTTP_QUERY_CONTENT_LOCATION
),
3109 FE(HTTP_QUERY_CONTENT_MD5
),
3110 FE(HTTP_QUERY_CONTENT_RANGE
),
3111 FE(HTTP_QUERY_ETAG
),
3112 FE(HTTP_QUERY_HOST
),
3113 FE(HTTP_QUERY_IF_MATCH
),
3114 FE(HTTP_QUERY_IF_NONE_MATCH
),
3115 FE(HTTP_QUERY_IF_RANGE
),
3116 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE
),
3117 FE(HTTP_QUERY_MAX_FORWARDS
),
3118 FE(HTTP_QUERY_PROXY_AUTHORIZATION
),
3119 FE(HTTP_QUERY_RANGE
),
3120 FE(HTTP_QUERY_TRANSFER_ENCODING
),
3121 FE(HTTP_QUERY_UPGRADE
),
3122 FE(HTTP_QUERY_VARY
),
3124 FE(HTTP_QUERY_WARNING
),
3125 FE(HTTP_QUERY_CUSTOM
)
3127 static const wininet_flag_info modifier_flags
[] = {
3128 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS
),
3129 FE(HTTP_QUERY_FLAG_SYSTEMTIME
),
3130 FE(HTTP_QUERY_FLAG_NUMBER
),
3131 FE(HTTP_QUERY_FLAG_COALESCE
)
3134 DWORD info_mod
= dwInfoLevel
& HTTP_QUERY_MODIFIER_FLAGS_MASK
;
3135 DWORD info
= dwInfoLevel
& HTTP_QUERY_HEADER_MASK
;
3138 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest
, dwInfoLevel
, info
);
3139 TRACE(" Attribute:");
3140 for (i
= 0; i
< (sizeof(query_flags
) / sizeof(query_flags
[0])); i
++) {
3141 if (query_flags
[i
].val
== info
) {
3142 TRACE(" %s", query_flags
[i
].name
);
3146 if (i
== (sizeof(query_flags
) / sizeof(query_flags
[0]))) {
3147 TRACE(" Unknown (%08x)", info
);
3150 TRACE(" Modifier:");
3151 for (i
= 0; i
< (sizeof(modifier_flags
) / sizeof(modifier_flags
[0])); i
++) {
3152 if (modifier_flags
[i
].val
& info_mod
) {
3153 TRACE(" %s", modifier_flags
[i
].name
);
3154 info_mod
&= ~ modifier_flags
[i
].val
;
3159 TRACE(" Unknown (%08x)", info_mod
);
3164 lpwhr
= (http_request_t
*) get_handle_object( hHttpRequest
);
3165 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
3167 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
3171 if (lpBuffer
== NULL
)
3172 *lpdwBufferLength
= 0;
3173 res
= HTTP_HttpQueryInfoW( lpwhr
, dwInfoLevel
,
3174 lpBuffer
, lpdwBufferLength
, lpdwIndex
);
3178 WININET_Release( &lpwhr
->hdr
);
3180 TRACE("%u <--\n", res
);
3181 if(res
!= ERROR_SUCCESS
)
3183 return res
== ERROR_SUCCESS
;
3186 /***********************************************************************
3187 * HttpQueryInfoA (WININET.@)
3189 * Queries for information about an HTTP request
3196 BOOL WINAPI
HttpQueryInfoA(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3197 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3203 if((dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
) ||
3204 (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
))
3206 return HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, lpBuffer
,
3207 lpdwBufferLength
, lpdwIndex
);
3213 len
= (*lpdwBufferLength
)*sizeof(WCHAR
);
3214 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3216 alloclen
= MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, NULL
, 0 ) * sizeof(WCHAR
);
3222 bufferW
= HeapAlloc( GetProcessHeap(), 0, alloclen
);
3223 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3224 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3225 MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, bufferW
, alloclen
/ sizeof(WCHAR
) );
3232 result
= HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, bufferW
,
3236 len
= WideCharToMultiByte( CP_ACP
,0, bufferW
, len
/ sizeof(WCHAR
) + 1,
3237 lpBuffer
, *lpdwBufferLength
, NULL
, NULL
);
3238 *lpdwBufferLength
= len
- 1;
3240 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer
));
3243 /* since the strings being returned from HttpQueryInfoW should be
3244 * only ASCII characters, it is reasonable to assume that all of
3245 * the Unicode characters can be reduced to a single byte */
3246 *lpdwBufferLength
= len
/ sizeof(WCHAR
);
3248 HeapFree(GetProcessHeap(), 0, bufferW
);
3253 /***********************************************************************
3254 * HTTP_GetRedirectURL (internal)
3256 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*lpwhr
, LPCWSTR lpszUrl
)
3258 static WCHAR szHttp
[] = {'h','t','t','p',0};
3259 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3260 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
3261 URL_COMPONENTSW urlComponents
;
3262 DWORD url_length
= 0;
3264 LPWSTR combined_url
;
3266 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3267 urlComponents
.lpszScheme
= (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) ? szHttps
: szHttp
;
3268 urlComponents
.dwSchemeLength
= 0;
3269 urlComponents
.lpszHostName
= lpwhs
->lpszHostName
;
3270 urlComponents
.dwHostNameLength
= 0;
3271 urlComponents
.nPort
= lpwhs
->nHostPort
;
3272 urlComponents
.lpszUserName
= lpwhs
->lpszUserName
;
3273 urlComponents
.dwUserNameLength
= 0;
3274 urlComponents
.lpszPassword
= NULL
;
3275 urlComponents
.dwPasswordLength
= 0;
3276 urlComponents
.lpszUrlPath
= lpwhr
->lpszPath
;
3277 urlComponents
.dwUrlPathLength
= 0;
3278 urlComponents
.lpszExtraInfo
= NULL
;
3279 urlComponents
.dwExtraInfoLength
= 0;
3281 if (!InternetCreateUrlW(&urlComponents
, 0, NULL
, &url_length
) &&
3282 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3285 orig_url
= HeapAlloc(GetProcessHeap(), 0, url_length
);
3287 /* convert from bytes to characters */
3288 url_length
= url_length
/ sizeof(WCHAR
) - 1;
3289 if (!InternetCreateUrlW(&urlComponents
, 0, orig_url
, &url_length
))
3291 HeapFree(GetProcessHeap(), 0, orig_url
);
3296 if (!InternetCombineUrlW(orig_url
, lpszUrl
, NULL
, &url_length
, ICU_ENCODE_SPACES_ONLY
) &&
3297 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3299 HeapFree(GetProcessHeap(), 0, orig_url
);
3302 combined_url
= HeapAlloc(GetProcessHeap(), 0, url_length
* sizeof(WCHAR
));
3304 if (!InternetCombineUrlW(orig_url
, lpszUrl
, combined_url
, &url_length
, ICU_ENCODE_SPACES_ONLY
))
3306 HeapFree(GetProcessHeap(), 0, orig_url
);
3307 HeapFree(GetProcessHeap(), 0, combined_url
);
3310 HeapFree(GetProcessHeap(), 0, orig_url
);
3311 return combined_url
;
3315 /***********************************************************************
3316 * HTTP_HandleRedirect (internal)
3318 static DWORD
HTTP_HandleRedirect(http_request_t
*lpwhr
, LPCWSTR lpszUrl
)
3320 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
3321 appinfo_t
*hIC
= lpwhs
->lpAppInfo
;
3322 BOOL using_proxy
= hIC
->lpszProxy
&& hIC
->lpszProxy
[0];
3323 WCHAR path
[INTERNET_MAX_URL_LENGTH
];
3328 /* if it's an absolute path, keep the same session info */
3329 lstrcpynW(path
, lpszUrl
, INTERNET_MAX_URL_LENGTH
);
3333 URL_COMPONENTSW urlComponents
;
3334 WCHAR protocol
[32], hostName
[MAXHOSTNAME
], userName
[1024];
3335 static WCHAR szHttp
[] = {'h','t','t','p',0};
3336 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3342 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3343 urlComponents
.lpszScheme
= protocol
;
3344 urlComponents
.dwSchemeLength
= 32;
3345 urlComponents
.lpszHostName
= hostName
;
3346 urlComponents
.dwHostNameLength
= MAXHOSTNAME
;
3347 urlComponents
.lpszUserName
= userName
;
3348 urlComponents
.dwUserNameLength
= 1024;
3349 urlComponents
.lpszPassword
= NULL
;
3350 urlComponents
.dwPasswordLength
= 0;
3351 urlComponents
.lpszUrlPath
= path
;
3352 urlComponents
.dwUrlPathLength
= 2048;
3353 urlComponents
.lpszExtraInfo
= NULL
;
3354 urlComponents
.dwExtraInfoLength
= 0;
3355 if(!InternetCrackUrlW(lpszUrl
, strlenW(lpszUrl
), 0, &urlComponents
))
3356 return INTERNET_GetLastError();
3358 if (!strncmpW(szHttp
, urlComponents
.lpszScheme
, strlenW(szHttp
)) &&
3359 (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
))
3361 TRACE("redirect from secure page to non-secure page\n");
3362 /* FIXME: warn about from secure redirect to non-secure page */
3363 lpwhr
->hdr
.dwFlags
&= ~INTERNET_FLAG_SECURE
;
3365 if (!strncmpW(szHttps
, urlComponents
.lpszScheme
, strlenW(szHttps
)) &&
3366 !(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
))
3368 TRACE("redirect from non-secure page to secure page\n");
3369 /* FIXME: notify about redirect to secure page */
3370 lpwhr
->hdr
.dwFlags
|= INTERNET_FLAG_SECURE
;
3373 if (urlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
3375 if (lstrlenW(protocol
)>4) /*https*/
3376 urlComponents
.nPort
= INTERNET_DEFAULT_HTTPS_PORT
;
3378 urlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
3383 * This upsets redirects to binary files on sourceforge.net
3384 * and gives an html page instead of the target file
3385 * Examination of the HTTP request sent by native wininet.dll
3386 * reveals that it doesn't send a referrer in that case.
3387 * Maybe there's a flag that enables this, or maybe a referrer
3388 * shouldn't be added in case of a redirect.
3391 /* consider the current host as the referrer */
3392 if (lpwhs
->lpszServerName
&& *lpwhs
->lpszServerName
)
3393 HTTP_ProcessHeader(lpwhr
, HTTP_REFERER
, lpwhs
->lpszServerName
,
3394 HTTP_ADDHDR_FLAG_REQ
|HTTP_ADDREQ_FLAG_REPLACE
|
3395 HTTP_ADDHDR_FLAG_ADD_IF_NEW
);
3398 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszHostName
);
3399 if (urlComponents
.nPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
3400 urlComponents
.nPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
3403 static const WCHAR fmt
[] = {'%','s',':','%','i',0};
3404 len
= lstrlenW(hostName
);
3405 len
+= 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3406 lpwhs
->lpszHostName
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
3407 sprintfW(lpwhs
->lpszHostName
, fmt
, hostName
, urlComponents
.nPort
);
3410 lpwhs
->lpszHostName
= heap_strdupW(hostName
);
3412 HTTP_ProcessHeader(lpwhr
, hostW
, lpwhs
->lpszHostName
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDREQ_FLAG_REPLACE
| HTTP_ADDHDR_FLAG_REQ
);
3414 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszUserName
);
3415 lpwhs
->lpszUserName
= NULL
;
3417 lpwhs
->lpszUserName
= heap_strdupW(userName
);
3421 if (strcmpiW(lpwhs
->lpszServerName
, hostName
) || lpwhs
->nServerPort
!= urlComponents
.nPort
)
3425 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszServerName
);
3426 lpwhs
->lpszServerName
= heap_strdupW(hostName
);
3427 lpwhs
->nServerPort
= urlComponents
.nPort
;
3429 NETCON_close(&lpwhr
->netConnection
);
3430 if ((res
= HTTP_ResolveName(lpwhr
)) != ERROR_SUCCESS
)
3433 res
= NETCON_init(&lpwhr
->netConnection
, lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
);
3434 if (res
!= ERROR_SUCCESS
)
3437 lpwhr
->read_pos
= lpwhr
->read_size
= 0;
3438 lpwhr
->read_chunked
= FALSE
;
3442 TRACE("Redirect through proxy\n");
3445 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszPath
);
3446 lpwhr
->lpszPath
=NULL
;
3452 rc
= UrlEscapeW(path
, NULL
, &needed
, URL_ESCAPE_SPACES_ONLY
);
3453 if (rc
!= E_POINTER
)
3454 needed
= strlenW(path
)+1;
3455 lpwhr
->lpszPath
= HeapAlloc(GetProcessHeap(), 0, needed
*sizeof(WCHAR
));
3456 rc
= UrlEscapeW(path
, lpwhr
->lpszPath
, &needed
,
3457 URL_ESCAPE_SPACES_ONLY
);
3460 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path
),rc
);
3461 strcpyW(lpwhr
->lpszPath
,path
);
3465 /* Remove custom content-type/length headers on redirects. */
3466 index
= HTTP_GetCustomHeaderIndex(lpwhr
, szContent_Type
, 0, TRUE
);
3468 HTTP_DeleteCustomHeader(lpwhr
, index
);
3469 index
= HTTP_GetCustomHeaderIndex(lpwhr
, szContent_Length
, 0, TRUE
);
3471 HTTP_DeleteCustomHeader(lpwhr
, index
);
3473 return ERROR_SUCCESS
;
3476 /***********************************************************************
3477 * HTTP_build_req (internal)
3479 * concatenate all the strings in the request together
3481 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
)
3486 for( t
= list
; *t
; t
++ )
3487 len
+= strlenW( *t
);
3490 str
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
3493 for( t
= list
; *t
; t
++ )
3499 static DWORD
HTTP_SecureProxyConnect(http_request_t
*lpwhr
)
3502 LPWSTR requestString
;
3508 static const WCHAR szConnect
[] = {'C','O','N','N','E','C','T',0};
3509 static const WCHAR szFormat
[] = {'%','s',':','%','d',0};
3510 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
3514 lpszPath
= HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs
->lpszHostName
) + 13)*sizeof(WCHAR
) );
3515 sprintfW( lpszPath
, szFormat
, lpwhs
->lpszHostName
, lpwhs
->nHostPort
);
3516 requestString
= HTTP_BuildHeaderRequestString( lpwhr
, szConnect
, lpszPath
, g_szHttp1_1
);
3517 HeapFree( GetProcessHeap(), 0, lpszPath
);
3519 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3520 NULL
, 0, NULL
, NULL
);
3521 len
--; /* the nul terminator isn't needed */
3522 ascii_req
= HeapAlloc( GetProcessHeap(), 0, len
);
3523 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3524 ascii_req
, len
, NULL
, NULL
);
3525 HeapFree( GetProcessHeap(), 0, requestString
);
3527 TRACE("full request -> %s\n", debugstr_an( ascii_req
, len
) );
3529 res
= NETCON_send( &lpwhr
->netConnection
, ascii_req
, len
, 0, &cnt
);
3530 HeapFree( GetProcessHeap(), 0, ascii_req
);
3531 if (res
!= ERROR_SUCCESS
)
3534 responseLen
= HTTP_GetResponseHeaders( lpwhr
, TRUE
);
3536 return ERROR_HTTP_INVALID_HEADER
;
3538 return ERROR_SUCCESS
;
3541 static void HTTP_InsertCookies(http_request_t
*lpwhr
)
3543 static const WCHAR szUrlForm
[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3544 LPWSTR lpszCookies
, lpszUrl
= NULL
;
3545 DWORD nCookieSize
, size
;
3546 LPHTTPHEADERW Host
= HTTP_GetHeader(lpwhr
, hostW
);
3548 size
= (strlenW(Host
->lpszValue
) + strlenW(szUrlForm
) + strlenW(lpwhr
->lpszPath
)) * sizeof(WCHAR
);
3549 if (!(lpszUrl
= HeapAlloc(GetProcessHeap(), 0, size
))) return;
3550 sprintfW( lpszUrl
, szUrlForm
, Host
->lpszValue
, lpwhr
->lpszPath
);
3552 if (InternetGetCookieW(lpszUrl
, NULL
, NULL
, &nCookieSize
))
3555 static const WCHAR szCookie
[] = {'C','o','o','k','i','e',':',' ',0};
3557 size
= sizeof(szCookie
) + nCookieSize
* sizeof(WCHAR
) + sizeof(szCrLf
);
3558 if ((lpszCookies
= HeapAlloc(GetProcessHeap(), 0, size
)))
3560 cnt
+= sprintfW(lpszCookies
, szCookie
);
3561 InternetGetCookieW(lpszUrl
, NULL
, lpszCookies
+ cnt
, &nCookieSize
);
3562 strcatW(lpszCookies
, szCrLf
);
3564 HTTP_HttpAddRequestHeadersW(lpwhr
, lpszCookies
, strlenW(lpszCookies
), HTTP_ADDREQ_FLAG_REPLACE
);
3565 HeapFree(GetProcessHeap(), 0, lpszCookies
);
3568 HeapFree(GetProcessHeap(), 0, lpszUrl
);
3571 /***********************************************************************
3572 * HTTP_HttpSendRequestW (internal)
3574 * Sends the specified request to the HTTP server
3581 static DWORD
HTTP_HttpSendRequestW(http_request_t
*lpwhr
, LPCWSTR lpszHeaders
,
3582 DWORD dwHeaderLength
, LPVOID lpOptional
, DWORD dwOptionalLength
,
3583 DWORD dwContentLength
, BOOL bEndRequest
)
3586 BOOL redirected
= FALSE
;
3587 LPWSTR requestString
= NULL
;
3590 INTERNET_ASYNC_RESULT iar
;
3591 static const WCHAR szPost
[] = { 'P','O','S','T',0 };
3592 static const WCHAR szContentLength
[] =
3593 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3594 WCHAR contentLengthStr
[sizeof szContentLength
/2 /* includes \r\n */ + 20 /* int */ ];
3597 TRACE("--> %p\n", lpwhr
);
3599 assert(lpwhr
->hdr
.htype
== WH_HHTTPREQ
);
3601 /* if the verb is NULL default to GET */
3602 if (!lpwhr
->lpszVerb
)
3603 lpwhr
->lpszVerb
= heap_strdupW(szGET
);
3605 if (dwContentLength
|| strcmpW(lpwhr
->lpszVerb
, szGET
))
3607 sprintfW(contentLengthStr
, szContentLength
, dwContentLength
);
3608 HTTP_HttpAddRequestHeadersW(lpwhr
, contentLengthStr
, -1L, HTTP_ADDREQ_FLAG_REPLACE
);
3609 lpwhr
->dwBytesToWrite
= dwContentLength
;
3611 if (lpwhr
->lpHttpSession
->lpAppInfo
->lpszAgent
)
3613 WCHAR
*agent_header
;
3614 static const WCHAR user_agent
[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3617 len
= strlenW(lpwhr
->lpHttpSession
->lpAppInfo
->lpszAgent
) + strlenW(user_agent
);
3618 agent_header
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
3619 sprintfW(agent_header
, user_agent
, lpwhr
->lpHttpSession
->lpAppInfo
->lpszAgent
);
3621 HTTP_HttpAddRequestHeadersW(lpwhr
, agent_header
, strlenW(agent_header
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
3622 HeapFree(GetProcessHeap(), 0, agent_header
);
3624 if (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_PRAGMA_NOCACHE
)
3626 static const WCHAR pragma_nocache
[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3627 HTTP_HttpAddRequestHeadersW(lpwhr
, pragma_nocache
, strlenW(pragma_nocache
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
3629 if ((lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_CACHE_WRITE
) && !strcmpW(lpwhr
->lpszVerb
, szPost
))
3631 static const WCHAR cache_control
[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3632 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3633 HTTP_HttpAddRequestHeadersW(lpwhr
, cache_control
, strlenW(cache_control
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
3639 BOOL reusing_connection
;
3644 /* like native, just in case the caller forgot to call InternetReadFile
3645 * for all the data */
3646 HTTP_DrainContent(lpwhr
);
3647 lpwhr
->dwContentRead
= 0;
3649 lpwhr
->dwContentLength
= ~0u;
3650 lpwhr
->dwBytesToWrite
= 0;
3653 if (TRACE_ON(wininet
))
3655 LPHTTPHEADERW Host
= HTTP_GetHeader(lpwhr
, hostW
);
3656 TRACE("Going to url %s %s\n", debugstr_w(Host
->lpszValue
), debugstr_w(lpwhr
->lpszPath
));
3660 if (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_KEEP_CONNECTION
)
3662 HTTP_ProcessHeader(lpwhr
, szConnection
, szKeepAlive
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
3664 HTTP_InsertAuthorization(lpwhr
, lpwhr
->pAuthInfo
, szAuthorization
);
3665 HTTP_InsertAuthorization(lpwhr
, lpwhr
->pProxyAuthInfo
, szProxy_Authorization
);
3667 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
))
3668 HTTP_InsertCookies(lpwhr
);
3670 /* add the headers the caller supplied */
3671 if( lpszHeaders
&& dwHeaderLength
)
3673 HTTP_HttpAddRequestHeadersW(lpwhr
, lpszHeaders
, dwHeaderLength
,
3674 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REPLACE
);
3677 if (lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxy
&& lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxy
[0])
3679 WCHAR
*url
= HTTP_BuildProxyRequestUrl(lpwhr
);
3680 requestString
= HTTP_BuildHeaderRequestString(lpwhr
, lpwhr
->lpszVerb
, url
, lpwhr
->lpszVersion
);
3681 HeapFree(GetProcessHeap(), 0, url
);
3684 requestString
= HTTP_BuildHeaderRequestString(lpwhr
, lpwhr
->lpszVerb
, lpwhr
->lpszPath
, lpwhr
->lpszVersion
);
3687 TRACE("Request header -> %s\n", debugstr_w(requestString
) );
3689 /* Send the request and store the results */
3690 if(NETCON_connected(&lpwhr
->netConnection
))
3691 reusing_connection
= TRUE
;
3693 reusing_connection
= FALSE
;
3695 if ((res
= HTTP_OpenConnection(lpwhr
)) != ERROR_SUCCESS
)
3698 /* send the request as ASCII, tack on the optional data */
3699 if (!lpOptional
|| redirected
)
3700 dwOptionalLength
= 0;
3701 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3702 NULL
, 0, NULL
, NULL
);
3703 ascii_req
= HeapAlloc( GetProcessHeap(), 0, len
+ dwOptionalLength
);
3704 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3705 ascii_req
, len
, NULL
, NULL
);
3707 memcpy( &ascii_req
[len
-1], lpOptional
, dwOptionalLength
);
3708 len
= (len
+ dwOptionalLength
- 1);
3710 TRACE("full request -> %s\n", debugstr_a(ascii_req
) );
3712 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3713 INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
3715 res
= NETCON_send(&lpwhr
->netConnection
, ascii_req
, len
, 0, &cnt
);
3716 HeapFree( GetProcessHeap(), 0, ascii_req
);
3718 lpwhr
->dwBytesWritten
= dwOptionalLength
;
3720 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3721 INTERNET_STATUS_REQUEST_SENT
,
3722 &len
, sizeof(DWORD
));
3729 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3730 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
3732 if (res
!= ERROR_SUCCESS
)
3735 responseLen
= HTTP_GetResponseHeaders(lpwhr
, TRUE
);
3736 /* FIXME: We should know that connection is closed before sending
3737 * headers. Otherwise wrong callbacks are executed */
3738 if(!responseLen
&& reusing_connection
) {
3739 TRACE("Connection closed by server, reconnecting\n");
3744 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3745 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
,
3748 HTTP_ProcessCookies(lpwhr
);
3750 if (!set_content_length( lpwhr
)) HTTP_FinishedReading(lpwhr
);
3752 dwBufferSize
= sizeof(dwStatusCode
);
3753 if (HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_STATUS_CODE
,
3754 &dwStatusCode
,&dwBufferSize
,NULL
) != ERROR_SUCCESS
)
3757 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
) && responseLen
)
3759 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
3760 dwBufferSize
=sizeof(szNewLocation
);
3761 if ((dwStatusCode
== HTTP_STATUS_REDIRECT
||
3762 dwStatusCode
== HTTP_STATUS_MOVED
||
3763 dwStatusCode
== HTTP_STATUS_REDIRECT_METHOD
) &&
3764 HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_LOCATION
,szNewLocation
,&dwBufferSize
,NULL
) == ERROR_SUCCESS
)
3766 if (strcmpW(lpwhr
->lpszVerb
, szGET
) && strcmpW(lpwhr
->lpszVerb
, szHEAD
))
3768 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVerb
);
3769 lpwhr
->lpszVerb
= heap_strdupW(szGET
);
3771 HTTP_DrainContent(lpwhr
);
3772 if ((new_url
= HTTP_GetRedirectURL( lpwhr
, szNewLocation
)))
3774 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
3775 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
3776 res
= HTTP_HandleRedirect(lpwhr
, new_url
);
3777 if (res
== ERROR_SUCCESS
)
3779 HeapFree(GetProcessHeap(), 0, requestString
);
3782 HeapFree( GetProcessHeap(), 0, new_url
);
3787 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTH
) && res
== ERROR_SUCCESS
)
3789 WCHAR szAuthValue
[2048];
3791 if (dwStatusCode
== HTTP_STATUS_DENIED
)
3793 LPHTTPHEADERW Host
= HTTP_GetHeader(lpwhr
, hostW
);
3795 while (HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_WWW_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
3797 if (HTTP_DoAuthorization(lpwhr
, szAuthValue
,
3799 lpwhr
->lpHttpSession
->lpszUserName
,
3800 lpwhr
->lpHttpSession
->lpszPassword
,
3803 HeapFree(GetProcessHeap(), 0, requestString
);
3810 TRACE("Cleaning wrong authorization data\n");
3811 destroy_authinfo(lpwhr
->pAuthInfo
);
3812 lpwhr
->pAuthInfo
= NULL
;
3815 if (dwStatusCode
== HTTP_STATUS_PROXY_AUTH_REQ
)
3818 while (HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_PROXY_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
3820 if (HTTP_DoAuthorization(lpwhr
, szAuthValue
,
3821 &lpwhr
->pProxyAuthInfo
,
3822 lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxyUsername
,
3823 lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxyPassword
,
3832 TRACE("Cleaning wrong proxy authorization data\n");
3833 destroy_authinfo(lpwhr
->pProxyAuthInfo
);
3834 lpwhr
->pProxyAuthInfo
= NULL
;
3840 res
= ERROR_SUCCESS
;
3844 if(res
== ERROR_SUCCESS
) {
3845 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
3846 WCHAR cacheFileName
[MAX_PATH
+1];
3849 b
= HTTP_GetRequestURL(lpwhr
, url
);
3851 WARN("Could not get URL\n");
3855 b
= CreateUrlCacheEntryW(url
, lpwhr
->dwContentLength
> 0 ? lpwhr
->dwContentLength
: 0, NULL
, cacheFileName
, 0);
3857 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszCacheFile
);
3858 CloseHandle(lpwhr
->hCacheFile
);
3860 lpwhr
->lpszCacheFile
= heap_strdupW(cacheFileName
);
3861 lpwhr
->hCacheFile
= CreateFileW(lpwhr
->lpszCacheFile
, GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
3862 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
3863 if(lpwhr
->hCacheFile
== INVALID_HANDLE_VALUE
) {
3864 WARN("Could not create file: %u\n", GetLastError());
3865 lpwhr
->hCacheFile
= NULL
;
3868 WARN("Could not create cache entry: %08x\n", GetLastError());
3874 HeapFree(GetProcessHeap(), 0, requestString
);
3876 /* TODO: send notification for P3P header */
3878 if (lpwhr
->lpHttpSession
->lpAppInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
3880 if (res
== ERROR_SUCCESS
&& lpwhr
->dwBytesWritten
== lpwhr
->dwBytesToWrite
)
3881 HTTP_ReceiveRequestData(lpwhr
, TRUE
);
3884 iar
.dwResult
= (res
==ERROR_SUCCESS
? (DWORD_PTR
)lpwhr
->hdr
.hInternet
: 0);
3887 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3888 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
3889 sizeof(INTERNET_ASYNC_RESULT
));
3897 /***********************************************************************
3899 * Helper functions for the HttpSendRequest(Ex) functions
3902 static void AsyncHttpSendRequestProc(WORKREQUEST
*workRequest
)
3904 struct WORKREQ_HTTPSENDREQUESTW
const *req
= &workRequest
->u
.HttpSendRequestW
;
3905 http_request_t
*lpwhr
= (http_request_t
*) workRequest
->hdr
;
3907 TRACE("%p\n", lpwhr
);
3909 HTTP_HttpSendRequestW(lpwhr
, req
->lpszHeader
,
3910 req
->dwHeaderLength
, req
->lpOptional
, req
->dwOptionalLength
,
3911 req
->dwContentLength
, req
->bEndRequest
);
3913 HeapFree(GetProcessHeap(), 0, req
->lpszHeader
);
3917 static DWORD
HTTP_HttpEndRequestW(http_request_t
*lpwhr
, DWORD dwFlags
, DWORD_PTR dwContext
)
3921 INTERNET_ASYNC_RESULT iar
;
3922 DWORD res
= ERROR_SUCCESS
;
3924 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3925 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
3927 responseLen
= HTTP_GetResponseHeaders(lpwhr
, TRUE
);
3929 res
= ERROR_HTTP_HEADER_NOT_FOUND
;
3931 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3932 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
, sizeof(DWORD
));
3934 /* process cookies here. Is this right? */
3935 HTTP_ProcessCookies(lpwhr
);
3937 if (!set_content_length( lpwhr
)) HTTP_FinishedReading(lpwhr
);
3939 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
))
3941 DWORD dwCode
,dwCodeLength
= sizeof(DWORD
);
3942 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_STATUS_CODE
, &dwCode
, &dwCodeLength
, NULL
) == ERROR_SUCCESS
3943 && (dwCode
== 302 || dwCode
== 301 || dwCode
== 303))
3945 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
3946 dwBufferSize
=sizeof(szNewLocation
);
3947 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_LOCATION
, szNewLocation
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
)
3949 if (strcmpW(lpwhr
->lpszVerb
, szGET
) && strcmpW(lpwhr
->lpszVerb
, szHEAD
))
3951 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVerb
);
3952 lpwhr
->lpszVerb
= heap_strdupW(szGET
);
3954 HTTP_DrainContent(lpwhr
);
3955 if ((new_url
= HTTP_GetRedirectURL( lpwhr
, szNewLocation
)))
3957 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
3958 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
3959 res
= HTTP_HandleRedirect(lpwhr
, new_url
);
3960 if (res
== ERROR_SUCCESS
)
3961 res
= HTTP_HttpSendRequestW(lpwhr
, NULL
, 0, NULL
, 0, 0, TRUE
);
3962 HeapFree( GetProcessHeap(), 0, new_url
);
3968 iar
.dwResult
= (res
==ERROR_SUCCESS
? (DWORD_PTR
)lpwhr
->hdr
.hInternet
: 0);
3971 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3972 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
3973 sizeof(INTERNET_ASYNC_RESULT
));
3977 /***********************************************************************
3978 * HttpEndRequestA (WININET.@)
3980 * Ends an HTTP request that was started by HttpSendRequestEx
3983 * TRUE if successful
3987 BOOL WINAPI
HttpEndRequestA(HINTERNET hRequest
,
3988 LPINTERNET_BUFFERSA lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
3990 TRACE("(%p, %p, %08x, %08lx)\n", hRequest
, lpBuffersOut
, dwFlags
, dwContext
);
3994 SetLastError(ERROR_INVALID_PARAMETER
);
3998 return HttpEndRequestW(hRequest
, NULL
, dwFlags
, dwContext
);
4001 static void AsyncHttpEndRequestProc(WORKREQUEST
*work
)
4003 struct WORKREQ_HTTPENDREQUESTW
const *req
= &work
->u
.HttpEndRequestW
;
4004 http_request_t
*lpwhr
= (http_request_t
*)work
->hdr
;
4006 TRACE("%p\n", lpwhr
);
4008 HTTP_HttpEndRequestW(lpwhr
, req
->dwFlags
, req
->dwContext
);
4011 /***********************************************************************
4012 * HttpEndRequestW (WININET.@)
4014 * Ends an HTTP request that was started by HttpSendRequestEx
4017 * TRUE if successful
4021 BOOL WINAPI
HttpEndRequestW(HINTERNET hRequest
,
4022 LPINTERNET_BUFFERSW lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
4024 http_request_t
*lpwhr
;
4031 SetLastError(ERROR_INVALID_PARAMETER
);
4035 lpwhr
= (http_request_t
*) get_handle_object( hRequest
);
4037 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
4039 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
4041 WININET_Release( &lpwhr
->hdr
);
4044 lpwhr
->hdr
.dwFlags
|= dwFlags
;
4046 if (lpwhr
->lpHttpSession
->lpAppInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4049 struct WORKREQ_HTTPENDREQUESTW
*request
;
4051 work
.asyncproc
= AsyncHttpEndRequestProc
;
4052 work
.hdr
= WININET_AddRef( &lpwhr
->hdr
);
4054 request
= &work
.u
.HttpEndRequestW
;
4055 request
->dwFlags
= dwFlags
;
4056 request
->dwContext
= dwContext
;
4058 INTERNET_AsyncCall(&work
);
4059 res
= ERROR_IO_PENDING
;
4062 res
= HTTP_HttpEndRequestW(lpwhr
, dwFlags
, dwContext
);
4064 WININET_Release( &lpwhr
->hdr
);
4065 TRACE("%u <--\n", res
);
4066 if(res
!= ERROR_SUCCESS
)
4068 return res
== ERROR_SUCCESS
;
4071 /***********************************************************************
4072 * HttpSendRequestExA (WININET.@)
4074 * Sends the specified request to the HTTP server and allows chunked
4079 * Failure: FALSE, call GetLastError() for more information.
4081 BOOL WINAPI
HttpSendRequestExA(HINTERNET hRequest
,
4082 LPINTERNET_BUFFERSA lpBuffersIn
,
4083 LPINTERNET_BUFFERSA lpBuffersOut
,
4084 DWORD dwFlags
, DWORD_PTR dwContext
)
4086 INTERNET_BUFFERSW BuffersInW
;
4089 LPWSTR header
= NULL
;
4091 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
4092 lpBuffersOut
, dwFlags
, dwContext
);
4096 BuffersInW
.dwStructSize
= sizeof(LPINTERNET_BUFFERSW
);
4097 if (lpBuffersIn
->lpcszHeader
)
4099 headerlen
= MultiByteToWideChar(CP_ACP
,0,lpBuffersIn
->lpcszHeader
,
4100 lpBuffersIn
->dwHeadersLength
,0,0);
4101 header
= HeapAlloc(GetProcessHeap(),0,headerlen
*sizeof(WCHAR
));
4102 if (!(BuffersInW
.lpcszHeader
= header
))
4104 SetLastError(ERROR_OUTOFMEMORY
);
4107 BuffersInW
.dwHeadersLength
= MultiByteToWideChar(CP_ACP
, 0,
4108 lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
4112 BuffersInW
.lpcszHeader
= NULL
;
4113 BuffersInW
.dwHeadersTotal
= lpBuffersIn
->dwHeadersTotal
;
4114 BuffersInW
.lpvBuffer
= lpBuffersIn
->lpvBuffer
;
4115 BuffersInW
.dwBufferLength
= lpBuffersIn
->dwBufferLength
;
4116 BuffersInW
.dwBufferTotal
= lpBuffersIn
->dwBufferTotal
;
4117 BuffersInW
.Next
= NULL
;
4120 rc
= HttpSendRequestExW(hRequest
, lpBuffersIn
? &BuffersInW
: NULL
, NULL
, dwFlags
, dwContext
);
4122 HeapFree(GetProcessHeap(),0,header
);
4127 /***********************************************************************
4128 * HttpSendRequestExW (WININET.@)
4130 * Sends the specified request to the HTTP server and allows chunked
4135 * Failure: FALSE, call GetLastError() for more information.
4137 BOOL WINAPI
HttpSendRequestExW(HINTERNET hRequest
,
4138 LPINTERNET_BUFFERSW lpBuffersIn
,
4139 LPINTERNET_BUFFERSW lpBuffersOut
,
4140 DWORD dwFlags
, DWORD_PTR dwContext
)
4142 http_request_t
*lpwhr
;
4143 http_session_t
*lpwhs
;
4147 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
4148 lpBuffersOut
, dwFlags
, dwContext
);
4150 lpwhr
= (http_request_t
*) get_handle_object( hRequest
);
4152 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
4154 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
4158 lpwhs
= lpwhr
->lpHttpSession
;
4159 assert(lpwhs
->hdr
.htype
== WH_HHTTPSESSION
);
4160 hIC
= lpwhs
->lpAppInfo
;
4161 assert(hIC
->hdr
.htype
== WH_HINIT
);
4163 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4165 WORKREQUEST workRequest
;
4166 struct WORKREQ_HTTPSENDREQUESTW
*req
;
4168 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
4169 workRequest
.hdr
= WININET_AddRef( &lpwhr
->hdr
);
4170 req
= &workRequest
.u
.HttpSendRequestW
;
4175 if (lpBuffersIn
->lpcszHeader
)
4177 if (lpBuffersIn
->dwHeadersLength
== ~0u)
4178 size
= (strlenW( lpBuffersIn
->lpcszHeader
) + 1) * sizeof(WCHAR
);
4180 size
= lpBuffersIn
->dwHeadersLength
* sizeof(WCHAR
);
4182 req
->lpszHeader
= HeapAlloc( GetProcessHeap(), 0, size
);
4183 memcpy( req
->lpszHeader
, lpBuffersIn
->lpcszHeader
, size
);
4185 else req
->lpszHeader
= NULL
;
4187 req
->dwHeaderLength
= size
/ sizeof(WCHAR
);
4188 req
->lpOptional
= lpBuffersIn
->lpvBuffer
;
4189 req
->dwOptionalLength
= lpBuffersIn
->dwBufferLength
;
4190 req
->dwContentLength
= lpBuffersIn
->dwBufferTotal
;
4194 req
->lpszHeader
= NULL
;
4195 req
->dwHeaderLength
= 0;
4196 req
->lpOptional
= NULL
;
4197 req
->dwOptionalLength
= 0;
4198 req
->dwContentLength
= 0;
4201 req
->bEndRequest
= FALSE
;
4203 INTERNET_AsyncCall(&workRequest
);
4205 * This is from windows.
4207 res
= ERROR_IO_PENDING
;
4212 res
= HTTP_HttpSendRequestW(lpwhr
, lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
4213 lpBuffersIn
->lpvBuffer
, lpBuffersIn
->dwBufferLength
,
4214 lpBuffersIn
->dwBufferTotal
, FALSE
);
4216 res
= HTTP_HttpSendRequestW(lpwhr
, NULL
, 0, NULL
, 0, 0, FALSE
);
4221 WININET_Release( &lpwhr
->hdr
);
4225 return res
== ERROR_SUCCESS
;
4228 /***********************************************************************
4229 * HttpSendRequestW (WININET.@)
4231 * Sends the specified request to the HTTP server
4238 BOOL WINAPI
HttpSendRequestW(HINTERNET hHttpRequest
, LPCWSTR lpszHeaders
,
4239 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
4241 http_request_t
*lpwhr
;
4242 http_session_t
*lpwhs
= NULL
;
4243 appinfo_t
*hIC
= NULL
;
4244 DWORD res
= ERROR_SUCCESS
;
4246 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest
,
4247 debugstr_wn(lpszHeaders
, dwHeaderLength
), dwHeaderLength
, lpOptional
, dwOptionalLength
);
4249 lpwhr
= (http_request_t
*) get_handle_object( hHttpRequest
);
4250 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
4252 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
4256 lpwhs
= lpwhr
->lpHttpSession
;
4257 if (NULL
== lpwhs
|| lpwhs
->hdr
.htype
!= WH_HHTTPSESSION
)
4259 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
4263 hIC
= lpwhs
->lpAppInfo
;
4264 if (NULL
== hIC
|| hIC
->hdr
.htype
!= WH_HINIT
)
4266 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
4270 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4272 WORKREQUEST workRequest
;
4273 struct WORKREQ_HTTPSENDREQUESTW
*req
;
4275 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
4276 workRequest
.hdr
= WININET_AddRef( &lpwhr
->hdr
);
4277 req
= &workRequest
.u
.HttpSendRequestW
;
4282 if (dwHeaderLength
== ~0u) size
= (strlenW(lpszHeaders
) + 1) * sizeof(WCHAR
);
4283 else size
= dwHeaderLength
* sizeof(WCHAR
);
4285 req
->lpszHeader
= HeapAlloc(GetProcessHeap(), 0, size
);
4286 memcpy(req
->lpszHeader
, lpszHeaders
, size
);
4289 req
->lpszHeader
= 0;
4290 req
->dwHeaderLength
= dwHeaderLength
;
4291 req
->lpOptional
= lpOptional
;
4292 req
->dwOptionalLength
= dwOptionalLength
;
4293 req
->dwContentLength
= dwOptionalLength
;
4294 req
->bEndRequest
= TRUE
;
4296 INTERNET_AsyncCall(&workRequest
);
4298 * This is from windows.
4300 res
= ERROR_IO_PENDING
;
4304 res
= HTTP_HttpSendRequestW(lpwhr
, lpszHeaders
,
4305 dwHeaderLength
, lpOptional
, dwOptionalLength
,
4306 dwOptionalLength
, TRUE
);
4310 WININET_Release( &lpwhr
->hdr
);
4313 return res
== ERROR_SUCCESS
;
4316 /***********************************************************************
4317 * HttpSendRequestA (WININET.@)
4319 * Sends the specified request to the HTTP server
4326 BOOL WINAPI
HttpSendRequestA(HINTERNET hHttpRequest
, LPCSTR lpszHeaders
,
4327 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
4330 LPWSTR szHeaders
=NULL
;
4331 DWORD nLen
=dwHeaderLength
;
4332 if(lpszHeaders
!=NULL
)
4334 nLen
=MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,NULL
,0);
4335 szHeaders
=HeapAlloc(GetProcessHeap(),0,nLen
*sizeof(WCHAR
));
4336 MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,szHeaders
,nLen
);
4338 result
=HttpSendRequestW(hHttpRequest
, szHeaders
, nLen
, lpOptional
, dwOptionalLength
);
4339 HeapFree(GetProcessHeap(),0,szHeaders
);
4343 /***********************************************************************
4344 * HTTPSESSION_Destroy (internal)
4346 * Deallocate session handle
4349 static void HTTPSESSION_Destroy(object_header_t
*hdr
)
4351 http_session_t
*lpwhs
= (http_session_t
*) hdr
;
4353 TRACE("%p\n", lpwhs
);
4355 WININET_Release(&lpwhs
->lpAppInfo
->hdr
);
4357 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszHostName
);
4358 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszServerName
);
4359 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszPassword
);
4360 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszUserName
);
4361 HeapFree(GetProcessHeap(), 0, lpwhs
);
4364 static DWORD
HTTPSESSION_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
4367 case INTERNET_OPTION_HANDLE_TYPE
:
4368 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4370 if (*size
< sizeof(ULONG
))
4371 return ERROR_INSUFFICIENT_BUFFER
;
4373 *size
= sizeof(DWORD
);
4374 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_CONNECT_HTTP
;
4375 return ERROR_SUCCESS
;
4378 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
4381 static DWORD
HTTPSESSION_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
4383 http_session_t
*ses
= (http_session_t
*)hdr
;
4386 case INTERNET_OPTION_USERNAME
:
4388 HeapFree(GetProcessHeap(), 0, ses
->lpszUserName
);
4389 if (!(ses
->lpszUserName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
4390 return ERROR_SUCCESS
;
4392 case INTERNET_OPTION_PASSWORD
:
4394 HeapFree(GetProcessHeap(), 0, ses
->lpszPassword
);
4395 if (!(ses
->lpszPassword
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
4396 return ERROR_SUCCESS
;
4401 return ERROR_INTERNET_INVALID_OPTION
;
4404 static const object_vtbl_t HTTPSESSIONVtbl
= {
4405 HTTPSESSION_Destroy
,
4407 HTTPSESSION_QueryOption
,
4408 HTTPSESSION_SetOption
,
4417 /***********************************************************************
4418 * HTTP_Connect (internal)
4420 * Create http session handle
4423 * HINTERNET a session handle on success
4427 DWORD
HTTP_Connect(appinfo_t
*hIC
, LPCWSTR lpszServerName
,
4428 INTERNET_PORT nServerPort
, LPCWSTR lpszUserName
,
4429 LPCWSTR lpszPassword
, DWORD dwFlags
, DWORD_PTR dwContext
,
4430 DWORD dwInternalFlags
, HINTERNET
*ret
)
4432 http_session_t
*lpwhs
= NULL
;
4436 if (!lpszServerName
|| !lpszServerName
[0])
4437 return ERROR_INVALID_PARAMETER
;
4439 assert( hIC
->hdr
.htype
== WH_HINIT
);
4441 lpwhs
= alloc_object(&hIC
->hdr
, &HTTPSESSIONVtbl
, sizeof(http_session_t
));
4443 return ERROR_OUTOFMEMORY
;
4446 * According to my tests. The name is not resolved until a request is sent
4449 lpwhs
->hdr
.htype
= WH_HHTTPSESSION
;
4450 lpwhs
->hdr
.dwFlags
= dwFlags
;
4451 lpwhs
->hdr
.dwContext
= dwContext
;
4452 lpwhs
->hdr
.dwInternalFlags
|= dwInternalFlags
;
4454 WININET_AddRef( &hIC
->hdr
);
4455 lpwhs
->lpAppInfo
= hIC
;
4456 list_add_head( &hIC
->hdr
.children
, &lpwhs
->hdr
.entry
);
4458 if(hIC
->lpszProxy
&& hIC
->dwAccessType
== INTERNET_OPEN_TYPE_PROXY
) {
4459 if(hIC
->lpszProxyBypass
)
4460 FIXME("Proxy bypass is ignored.\n");
4462 lpwhs
->lpszServerName
= heap_strdupW(lpszServerName
);
4463 lpwhs
->lpszHostName
= heap_strdupW(lpszServerName
);
4464 if (lpszUserName
&& lpszUserName
[0])
4465 lpwhs
->lpszUserName
= heap_strdupW(lpszUserName
);
4466 if (lpszPassword
&& lpszPassword
[0])
4467 lpwhs
->lpszPassword
= heap_strdupW(lpszPassword
);
4468 lpwhs
->nServerPort
= nServerPort
;
4469 lpwhs
->nHostPort
= nServerPort
;
4471 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4472 if (!(lpwhs
->hdr
.dwInternalFlags
& INET_OPENURL
))
4474 INTERNET_SendCallback(&hIC
->hdr
, dwContext
,
4475 INTERNET_STATUS_HANDLE_CREATED
, &lpwhs
->hdr
.hInternet
,
4480 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4484 TRACE("%p --> %p\n", hIC
, lpwhs
);
4486 *ret
= lpwhs
->hdr
.hInternet
;
4487 return ERROR_SUCCESS
;
4491 /***********************************************************************
4492 * HTTP_OpenConnection (internal)
4494 * Connect to a web server
4501 static DWORD
HTTP_OpenConnection(http_request_t
*lpwhr
)
4503 http_session_t
*lpwhs
;
4504 appinfo_t
*hIC
= NULL
;
4505 char szaddr
[INET6_ADDRSTRLEN
];
4507 DWORD res
= ERROR_SUCCESS
;
4512 if (lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
4514 res
= ERROR_INVALID_PARAMETER
;
4518 if (NETCON_connected(&lpwhr
->netConnection
))
4520 if ((res
= HTTP_ResolveName(lpwhr
)) != ERROR_SUCCESS
) goto lend
;
4522 lpwhs
= lpwhr
->lpHttpSession
;
4524 hIC
= lpwhs
->lpAppInfo
;
4525 switch (lpwhs
->socketAddress
.ss_family
)
4528 addr
= &((struct sockaddr_in
*)&lpwhs
->socketAddress
)->sin_addr
;
4531 addr
= &((struct sockaddr_in6
*)&lpwhs
->socketAddress
)->sin6_addr
;
4534 WARN("unsupported family %d\n", lpwhs
->socketAddress
.ss_family
);
4535 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
4537 inet_ntop(lpwhs
->socketAddress
.ss_family
, addr
, szaddr
, sizeof(szaddr
));
4538 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
4539 INTERNET_STATUS_CONNECTING_TO_SERVER
,
4543 res
= NETCON_create(&lpwhr
->netConnection
, lpwhs
->socketAddress
.ss_family
, SOCK_STREAM
, 0);
4544 if (res
!= ERROR_SUCCESS
)
4546 WARN("Socket creation failed: %u\n", res
);
4550 res
= NETCON_connect(&lpwhr
->netConnection
, (struct sockaddr
*)&lpwhs
->socketAddress
,
4552 if(res
!= ERROR_SUCCESS
)
4555 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
4556 INTERNET_STATUS_CONNECTED_TO_SERVER
,
4557 szaddr
, strlen(szaddr
)+1);
4559 if (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
4561 /* Note: we differ from Microsoft's WinINet here. they seem to have
4562 * a bug that causes no status callbacks to be sent when starting
4563 * a tunnel to a proxy server using the CONNECT verb. i believe our
4564 * behaviour to be more correct and to not cause any incompatibilities
4565 * because using a secure connection through a proxy server is a rare
4566 * case that would be hard for anyone to depend on */
4567 if (hIC
->lpszProxy
&& (res
= HTTP_SecureProxyConnect(lpwhr
)) != ERROR_SUCCESS
) {
4568 HTTPREQ_CloseConnection(&lpwhr
->hdr
);
4572 res
= NETCON_secure_connect(&lpwhr
->netConnection
, lpwhs
->lpszHostName
);
4573 if(res
!= ERROR_SUCCESS
)
4575 WARN("Couldn't connect securely to host\n");
4577 if((lpwhr
->hdr
.ErrorMask
&INTERNET_ERROR_MASK_COMBINED_SEC_CERT
) && (
4578 res
== ERROR_INTERNET_SEC_CERT_DATE_INVALID
4579 || res
== ERROR_INTERNET_INVALID_CA
4580 || res
== ERROR_INTERNET_SEC_CERT_NO_REV
4581 || res
== ERROR_INTERNET_SEC_CERT_REV_FAILED
4582 || res
== ERROR_INTERNET_SEC_CERT_REVOKED
4583 || res
== ERROR_INTERNET_SEC_INVALID_CERT
4584 || res
== ERROR_INTERNET_SEC_CERT_CN_INVALID
))
4585 res
= ERROR_INTERNET_SEC_CERT_ERRORS
;
4587 HTTPREQ_CloseConnection(&lpwhr
->hdr
);
4594 lpwhr
->read_pos
= lpwhr
->read_size
= 0;
4595 lpwhr
->read_chunked
= FALSE
;
4597 TRACE("%d <--\n", res
);
4602 /***********************************************************************
4603 * HTTP_clear_response_headers (internal)
4605 * clear out any old response headers
4607 static void HTTP_clear_response_headers( http_request_t
*lpwhr
)
4611 for( i
=0; i
<lpwhr
->nCustHeaders
; i
++)
4613 if( !lpwhr
->pCustHeaders
[i
].lpszField
)
4615 if( !lpwhr
->pCustHeaders
[i
].lpszValue
)
4617 if ( lpwhr
->pCustHeaders
[i
].wFlags
& HDR_ISREQUEST
)
4619 HTTP_DeleteCustomHeader( lpwhr
, i
);
4624 /***********************************************************************
4625 * HTTP_GetResponseHeaders (internal)
4627 * Read server response
4634 static INT
HTTP_GetResponseHeaders(http_request_t
*lpwhr
, BOOL clear
)
4637 WCHAR buffer
[MAX_REPLY_LEN
];
4638 DWORD buflen
= MAX_REPLY_LEN
;
4639 BOOL bSuccess
= FALSE
;
4641 char bufferA
[MAX_REPLY_LEN
];
4642 LPWSTR status_code
= NULL
, status_text
= NULL
;
4643 DWORD cchMaxRawHeaders
= 1024;
4644 LPWSTR lpszRawHeaders
= NULL
;
4646 DWORD cchRawHeaders
= 0;
4647 BOOL codeHundred
= FALSE
;
4651 if (!NETCON_connected(&lpwhr
->netConnection
))
4655 static const WCHAR szHundred
[] = {'1','0','0',0};
4657 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4659 buflen
= MAX_REPLY_LEN
;
4660 if (!read_line(lpwhr
, bufferA
, &buflen
))
4663 /* clear old response headers (eg. from a redirect response) */
4665 HTTP_clear_response_headers( lpwhr
);
4670 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
4671 /* check is this a status code line? */
4672 if (!strncmpW(buffer
, g_szHttp1_0
, 4))
4674 /* split the version from the status code */
4675 status_code
= strchrW( buffer
, ' ' );
4680 /* split the status code from the status text */
4681 status_text
= strchrW( status_code
, ' ' );
4686 TRACE("version [%s] status code [%s] status text [%s]\n",
4687 debugstr_w(buffer
), debugstr_w(status_code
), debugstr_w(status_text
) );
4689 codeHundred
= (!strcmpW(status_code
, szHundred
));
4691 else if (!codeHundred
)
4693 WARN("No status line at head of response (%s)\n", debugstr_w(buffer
));
4695 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVersion
);
4696 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszStatusText
);
4698 lpwhr
->lpszVersion
= heap_strdupW(g_szHttp1_0
);
4699 lpwhr
->lpszStatusText
= heap_strdupW(szOK
);
4701 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszRawHeaders
);
4702 lpwhr
->lpszRawHeaders
= heap_strdupW(szDefaultHeader
);
4707 } while (codeHundred
);
4709 /* Add status code */
4710 HTTP_ProcessHeader(lpwhr
, szStatus
, status_code
,
4711 HTTP_ADDHDR_FLAG_REPLACE
);
4713 HeapFree(GetProcessHeap(),0,lpwhr
->lpszVersion
);
4714 HeapFree(GetProcessHeap(),0,lpwhr
->lpszStatusText
);
4716 lpwhr
->lpszVersion
= heap_strdupW(buffer
);
4717 lpwhr
->lpszStatusText
= heap_strdupW(status_text
);
4719 /* Restore the spaces */
4720 *(status_code
-1) = ' ';
4721 *(status_text
-1) = ' ';
4723 /* regenerate raw headers */
4724 lpszRawHeaders
= HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
4725 if (!lpszRawHeaders
) goto lend
;
4727 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
4728 cchMaxRawHeaders
*= 2;
4729 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
4730 if (temp
== NULL
) goto lend
;
4731 lpszRawHeaders
= temp
;
4732 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
4733 cchRawHeaders
+= (buflen
-1);
4734 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
4735 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
4736 lpszRawHeaders
[cchRawHeaders
] = '\0';
4738 /* Parse each response line */
4741 buflen
= MAX_REPLY_LEN
;
4742 if (read_line(lpwhr
, bufferA
, &buflen
))
4744 LPWSTR
* pFieldAndValue
;
4746 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA
));
4748 if (!bufferA
[0]) break;
4749 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
4751 pFieldAndValue
= HTTP_InterpretHttpHeader(buffer
);
4754 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
4755 cchMaxRawHeaders
*= 2;
4756 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
4757 if (temp
== NULL
) goto lend
;
4758 lpszRawHeaders
= temp
;
4759 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
4760 cchRawHeaders
+= (buflen
-1);
4761 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
4762 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
4763 lpszRawHeaders
[cchRawHeaders
] = '\0';
4765 HTTP_ProcessHeader(lpwhr
, pFieldAndValue
[0], pFieldAndValue
[1],
4766 HTTP_ADDREQ_FLAG_ADD
);
4768 HTTP_FreeTokens(pFieldAndValue
);
4779 /* make sure the response header is terminated with an empty line. Some apps really
4780 truly care about that empty line being there for some reason. Just add it to the
4782 if (cchRawHeaders
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
4784 cchMaxRawHeaders
= cchRawHeaders
+ strlenW(szCrLf
);
4785 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
4786 if (temp
== NULL
) goto lend
;
4787 lpszRawHeaders
= temp
;
4790 memcpy(&lpszRawHeaders
[cchRawHeaders
], szCrLf
, sizeof(szCrLf
));
4792 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszRawHeaders
);
4793 lpwhr
->lpszRawHeaders
= lpszRawHeaders
;
4794 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders
));
4804 HeapFree(GetProcessHeap(), 0, lpszRawHeaders
);
4809 /***********************************************************************
4810 * HTTP_InterpretHttpHeader (internal)
4812 * Parse server response
4816 * Pointer to array of field, value, NULL on success.
4819 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
)
4821 LPWSTR
* pTokenPair
;
4825 pTokenPair
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*pTokenPair
)*3);
4827 pszColon
= strchrW(buffer
, ':');
4828 /* must have two tokens */
4831 HTTP_FreeTokens(pTokenPair
);
4833 TRACE("No ':' in line: %s\n", debugstr_w(buffer
));
4837 pTokenPair
[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon
- buffer
+ 1) * sizeof(WCHAR
));
4840 HTTP_FreeTokens(pTokenPair
);
4843 memcpy(pTokenPair
[0], buffer
, (pszColon
- buffer
) * sizeof(WCHAR
));
4844 pTokenPair
[0][pszColon
- buffer
] = '\0';
4848 len
= strlenW(pszColon
);
4849 pTokenPair
[1] = HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
));
4852 HTTP_FreeTokens(pTokenPair
);
4855 memcpy(pTokenPair
[1], pszColon
, (len
+ 1) * sizeof(WCHAR
));
4857 strip_spaces(pTokenPair
[0]);
4858 strip_spaces(pTokenPair
[1]);
4860 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair
[0]), debugstr_w(pTokenPair
[1]));
4864 /***********************************************************************
4865 * HTTP_ProcessHeader (internal)
4867 * Stuff header into header tables according to <dwModifier>
4871 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4873 static DWORD
HTTP_ProcessHeader(http_request_t
*lpwhr
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
)
4875 LPHTTPHEADERW lphttpHdr
= NULL
;
4877 BOOL request_only
= dwModifier
& HTTP_ADDHDR_FLAG_REQ
;
4878 DWORD res
= ERROR_HTTP_INVALID_HEADER
;
4880 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field
), debugstr_w(value
), dwModifier
);
4882 /* REPLACE wins out over ADD */
4883 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
4884 dwModifier
&= ~HTTP_ADDHDR_FLAG_ADD
;
4886 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD
)
4889 index
= HTTP_GetCustomHeaderIndex(lpwhr
, field
, 0, request_only
);
4893 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD_IF_NEW
)
4894 return ERROR_HTTP_INVALID_HEADER
;
4895 lphttpHdr
= &lpwhr
->pCustHeaders
[index
];
4901 hdr
.lpszField
= (LPWSTR
)field
;
4902 hdr
.lpszValue
= (LPWSTR
)value
;
4903 hdr
.wFlags
= hdr
.wCount
= 0;
4905 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
4906 hdr
.wFlags
|= HDR_ISREQUEST
;
4908 return HTTP_InsertCustomHeader(lpwhr
, &hdr
);
4910 /* no value to delete */
4911 else return ERROR_SUCCESS
;
4913 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
4914 lphttpHdr
->wFlags
|= HDR_ISREQUEST
;
4916 lphttpHdr
->wFlags
&= ~HDR_ISREQUEST
;
4918 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
4920 HTTP_DeleteCustomHeader( lpwhr
, index
);
4926 hdr
.lpszField
= (LPWSTR
)field
;
4927 hdr
.lpszValue
= (LPWSTR
)value
;
4928 hdr
.wFlags
= hdr
.wCount
= 0;
4930 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
4931 hdr
.wFlags
|= HDR_ISREQUEST
;
4933 return HTTP_InsertCustomHeader(lpwhr
, &hdr
);
4936 return ERROR_SUCCESS
;
4938 else if (dwModifier
& COALESCEFLAGS
)
4943 INT origlen
= strlenW(lphttpHdr
->lpszValue
);
4944 INT valuelen
= strlenW(value
);
4946 if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
)
4949 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
4951 else if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON
)
4954 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
4957 len
= origlen
+ valuelen
+ ((ch
> 0) ? 2 : 0);
4959 lpsztmp
= HeapReAlloc(GetProcessHeap(), 0, lphttpHdr
->lpszValue
, (len
+1)*sizeof(WCHAR
));
4962 lphttpHdr
->lpszValue
= lpsztmp
;
4963 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4966 lphttpHdr
->lpszValue
[origlen
] = ch
;
4968 lphttpHdr
->lpszValue
[origlen
] = ' ';
4972 memcpy(&lphttpHdr
->lpszValue
[origlen
], value
, valuelen
*sizeof(WCHAR
));
4973 lphttpHdr
->lpszValue
[len
] = '\0';
4974 res
= ERROR_SUCCESS
;
4978 WARN("HeapReAlloc (%d bytes) failed\n",len
+1);
4979 res
= ERROR_OUTOFMEMORY
;
4982 TRACE("<-- %d\n", res
);
4987 /***********************************************************************
4988 * HTTP_FinishedReading (internal)
4990 * Called when all content from server has been read by client.
4993 static BOOL
HTTP_FinishedReading(http_request_t
*lpwhr
)
4995 BOOL keepalive
= HTTP_KeepAlive(lpwhr
);
5002 HTTPREQ_CloseConnection(&lpwhr
->hdr
);
5005 /* FIXME: store data in the URL cache here */
5011 /***********************************************************************
5012 * HTTP_GetCustomHeaderIndex (internal)
5014 * Return index of custom header from header array
5017 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*lpwhr
, LPCWSTR lpszField
,
5018 int requested_index
, BOOL request_only
)
5022 TRACE("%s, %d, %d\n", debugstr_w(lpszField
), requested_index
, request_only
);
5024 for (index
= 0; index
< lpwhr
->nCustHeaders
; index
++)
5026 if (strcmpiW(lpwhr
->pCustHeaders
[index
].lpszField
, lpszField
))
5029 if (request_only
&& !(lpwhr
->pCustHeaders
[index
].wFlags
& HDR_ISREQUEST
))
5032 if (!request_only
&& (lpwhr
->pCustHeaders
[index
].wFlags
& HDR_ISREQUEST
))
5035 if (requested_index
== 0)
5040 if (index
>= lpwhr
->nCustHeaders
)
5043 TRACE("Return: %d\n", index
);
5048 /***********************************************************************
5049 * HTTP_InsertCustomHeader (internal)
5051 * Insert header into array
5054 static DWORD
HTTP_InsertCustomHeader(http_request_t
*lpwhr
, LPHTTPHEADERW lpHdr
)
5057 LPHTTPHEADERW lph
= NULL
;
5059 TRACE("--> %s: %s\n", debugstr_w(lpHdr
->lpszField
), debugstr_w(lpHdr
->lpszValue
));
5060 count
= lpwhr
->nCustHeaders
+ 1;
5062 lph
= HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, lpwhr
->pCustHeaders
, sizeof(HTTPHEADERW
) * count
);
5064 lph
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(HTTPHEADERW
) * count
);
5067 return ERROR_OUTOFMEMORY
;
5069 lpwhr
->pCustHeaders
= lph
;
5070 lpwhr
->pCustHeaders
[count
-1].lpszField
= heap_strdupW(lpHdr
->lpszField
);
5071 lpwhr
->pCustHeaders
[count
-1].lpszValue
= heap_strdupW(lpHdr
->lpszValue
);
5072 lpwhr
->pCustHeaders
[count
-1].wFlags
= lpHdr
->wFlags
;
5073 lpwhr
->pCustHeaders
[count
-1].wCount
= lpHdr
->wCount
;
5074 lpwhr
->nCustHeaders
++;
5076 return ERROR_SUCCESS
;
5080 /***********************************************************************
5081 * HTTP_DeleteCustomHeader (internal)
5083 * Delete header from array
5084 * If this function is called, the indexs may change.
5086 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*lpwhr
, DWORD index
)
5088 if( lpwhr
->nCustHeaders
<= 0 )
5090 if( index
>= lpwhr
->nCustHeaders
)
5092 lpwhr
->nCustHeaders
--;
5094 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[index
].lpszField
);
5095 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[index
].lpszValue
);
5097 memmove( &lpwhr
->pCustHeaders
[index
], &lpwhr
->pCustHeaders
[index
+1],
5098 (lpwhr
->nCustHeaders
- index
)* sizeof(HTTPHEADERW
) );
5099 memset( &lpwhr
->pCustHeaders
[lpwhr
->nCustHeaders
], 0, sizeof(HTTPHEADERW
) );
5105 /***********************************************************************
5106 * HTTP_VerifyValidHeader (internal)
5108 * Verify the given header is not invalid for the given http request
5111 static BOOL
HTTP_VerifyValidHeader(http_request_t
*lpwhr
, LPCWSTR field
)
5113 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5114 if (!strcmpW(lpwhr
->lpszVersion
, g_szHttp1_0
) && !strcmpiW(field
, szAccept_Encoding
))
5115 return ERROR_HTTP_INVALID_HEADER
;
5117 return ERROR_SUCCESS
;
5120 /***********************************************************************
5121 * IsHostInProxyBypassList (@)
5126 BOOL WINAPI
IsHostInProxyBypassList(DWORD flags
, LPCSTR szHost
, DWORD length
)
5128 FIXME("STUB: flags=%d host=%s length=%d\n",flags
,szHost
,length
);
5132 /***********************************************************************
5133 * InternetShowSecurityInfoByURLA (@)
5135 BOOL WINAPI
InternetShowSecurityInfoByURLA(LPCSTR url
, HWND window
)
5137 FIXME("stub: %s %p\n", url
, window
);
5141 /***********************************************************************
5142 * InternetShowSecurityInfoByURLW (@)
5144 BOOL WINAPI
InternetShowSecurityInfoByURLW(LPCWSTR url
, HWND window
)
5146 FIXME("stub: %s %p\n", debugstr_w(url
), window
);