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>
60 #define NO_SHLWAPI_STREAM
61 #define NO_SHLWAPI_REG
62 #define NO_SHLWAPI_STRFCNS
63 #define NO_SHLWAPI_GDI
69 #include "wine/debug.h"
70 #include "wine/exception.h"
71 #include "wine/unicode.h"
73 WINE_DEFAULT_DEBUG_CHANNEL(wininet
);
75 static const WCHAR g_szHttp1_0
[] = {'H','T','T','P','/','1','.','0',0};
76 static const WCHAR g_szHttp1_1
[] = {'H','T','T','P','/','1','.','1',0};
77 static const WCHAR szOK
[] = {'O','K',0};
78 static const WCHAR szDefaultHeader
[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
79 static const WCHAR hostW
[] = { 'H','o','s','t',0 };
80 static const WCHAR szAuthorization
[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
81 static const WCHAR szProxy_Authorization
[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
82 static const WCHAR szStatus
[] = { 'S','t','a','t','u','s',0 };
83 static const WCHAR szKeepAlive
[] = {'K','e','e','p','-','A','l','i','v','e',0};
84 static const WCHAR szGET
[] = { 'G','E','T', 0 };
85 static const WCHAR szHEAD
[] = { 'H','E','A','D', 0 };
86 static const WCHAR szCrLf
[] = {'\r','\n', 0};
88 static const WCHAR szAccept
[] = { 'A','c','c','e','p','t',0 };
89 static const WCHAR szAccept_Charset
[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
90 static const WCHAR szAccept_Encoding
[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
91 static const WCHAR szAccept_Language
[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
92 static const WCHAR szAccept_Ranges
[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
93 static const WCHAR szAge
[] = { 'A','g','e',0 };
94 static const WCHAR szAllow
[] = { 'A','l','l','o','w',0 };
95 static const WCHAR szCache_Control
[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
96 static const WCHAR szConnection
[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
97 static const WCHAR szContent_Base
[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
98 static const WCHAR szContent_Encoding
[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
99 static const WCHAR szContent_ID
[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
100 static const WCHAR szContent_Language
[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
101 static const WCHAR szContent_Length
[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
102 static const WCHAR szContent_Location
[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
103 static const WCHAR szContent_MD5
[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
104 static const WCHAR szContent_Range
[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
105 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 };
106 static const WCHAR szContent_Type
[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
107 static const WCHAR szCookie
[] = { 'C','o','o','k','i','e',0 };
108 static const WCHAR szDate
[] = { 'D','a','t','e',0 };
109 static const WCHAR szFrom
[] = { 'F','r','o','m',0 };
110 static const WCHAR szETag
[] = { 'E','T','a','g',0 };
111 static const WCHAR szExpect
[] = { 'E','x','p','e','c','t',0 };
112 static const WCHAR szExpires
[] = { 'E','x','p','i','r','e','s',0 };
113 static const WCHAR szIf_Match
[] = { 'I','f','-','M','a','t','c','h',0 };
114 static const WCHAR szIf_Modified_Since
[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
115 static const WCHAR szIf_None_Match
[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
116 static const WCHAR szIf_Range
[] = { 'I','f','-','R','a','n','g','e',0 };
117 static const WCHAR szIf_Unmodified_Since
[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
118 static const WCHAR szLast_Modified
[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
119 static const WCHAR szLocation
[] = { 'L','o','c','a','t','i','o','n',0 };
120 static const WCHAR szMax_Forwards
[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
121 static const WCHAR szMime_Version
[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
122 static const WCHAR szPragma
[] = { 'P','r','a','g','m','a',0 };
123 static const WCHAR szProxy_Authenticate
[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
124 static const WCHAR szProxy_Connection
[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
125 static const WCHAR szPublic
[] = { 'P','u','b','l','i','c',0 };
126 static const WCHAR szRange
[] = { 'R','a','n','g','e',0 };
127 static const WCHAR szReferer
[] = { 'R','e','f','e','r','e','r',0 };
128 static const WCHAR szRetry_After
[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
129 static const WCHAR szServer
[] = { 'S','e','r','v','e','r',0 };
130 static const WCHAR szSet_Cookie
[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
131 static const WCHAR szTransfer_Encoding
[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
132 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 };
133 static const WCHAR szUpgrade
[] = { 'U','p','g','r','a','d','e',0 };
134 static const WCHAR szURI
[] = { 'U','R','I',0 };
135 static const WCHAR szUser_Agent
[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
136 static const WCHAR szVary
[] = { 'V','a','r','y',0 };
137 static const WCHAR szVia
[] = { 'V','i','a',0 };
138 static const WCHAR szWarning
[] = { 'W','a','r','n','i','n','g',0 };
139 static const WCHAR szWWW_Authenticate
[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
141 #define MAXHOSTNAME 100
142 #define MAX_FIELD_VALUE_LEN 256
143 #define MAX_FIELD_LEN 256
145 #define HTTP_REFERER szReferer
146 #define HTTP_ACCEPT szAccept
147 #define HTTP_USERAGENT szUser_Agent
149 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
150 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
151 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
152 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
153 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
154 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
155 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
157 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
168 unsigned int auth_data_len
;
169 BOOL finished
; /* finished authenticating */
173 struct gzip_stream_t
{
183 typedef struct _basicAuthorizationData
190 UINT authorizationLen
;
191 } basicAuthorizationData
;
193 typedef struct _authorizationData
207 static struct list basicAuthorizationCache
= LIST_INIT(basicAuthorizationCache
);
208 static struct list authorizationCache
= LIST_INIT(authorizationCache
);
210 static CRITICAL_SECTION authcache_cs
;
211 static CRITICAL_SECTION_DEBUG critsect_debug
=
214 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
215 0, 0, { (DWORD_PTR
)(__FILE__
": authcache_cs") }
217 static CRITICAL_SECTION authcache_cs
= { &critsect_debug
, -1, 0, 0, 0, 0 };
219 static DWORD
HTTP_OpenConnection(http_request_t
*req
);
220 static BOOL
HTTP_GetResponseHeaders(http_request_t
*req
, BOOL clear
);
221 static DWORD
HTTP_ProcessHeader(http_request_t
*req
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
);
222 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
);
223 static DWORD
HTTP_InsertCustomHeader(http_request_t
*req
, LPHTTPHEADERW lpHdr
);
224 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*req
, LPCWSTR lpszField
, INT index
, BOOL Request
);
225 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*req
, DWORD index
);
226 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
);
227 static DWORD
HTTP_HttpQueryInfoW(http_request_t
*, DWORD
, LPVOID
, LPDWORD
, LPDWORD
);
228 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*req
, LPCWSTR lpszUrl
);
229 static UINT
HTTP_DecodeBase64(LPCWSTR base64
, LPSTR bin
);
230 static BOOL
HTTP_VerifyValidHeader(http_request_t
*req
, LPCWSTR field
);
231 static void HTTP_DrainContent(http_request_t
*req
);
232 static BOOL
HTTP_FinishedReading(http_request_t
*req
);
234 static LPHTTPHEADERW
HTTP_GetHeader(http_request_t
*req
, LPCWSTR head
)
237 HeaderIndex
= HTTP_GetCustomHeaderIndex(req
, head
, 0, TRUE
);
238 if (HeaderIndex
== -1)
241 return &req
->custHeaders
[HeaderIndex
];
246 static voidpf
wininet_zalloc(voidpf opaque
, uInt items
, uInt size
)
248 return HeapAlloc(GetProcessHeap(), 0, items
*size
);
251 static void wininet_zfree(voidpf opaque
, voidpf address
)
253 HeapFree(GetProcessHeap(), 0, address
);
256 static void init_gzip_stream(http_request_t
*req
)
258 gzip_stream_t
*gzip_stream
;
261 gzip_stream
= heap_alloc_zero(sizeof(gzip_stream_t
));
262 gzip_stream
->zstream
.zalloc
= wininet_zalloc
;
263 gzip_stream
->zstream
.zfree
= wininet_zfree
;
265 zres
= inflateInit2(&gzip_stream
->zstream
, 0x1f);
267 ERR("inflateInit failed: %d\n", zres
);
268 HeapFree(GetProcessHeap(), 0, gzip_stream
);
272 req
->gzip_stream
= gzip_stream
;
274 index
= HTTP_GetCustomHeaderIndex(req
, szContent_Length
, 0, FALSE
);
276 HTTP_DeleteCustomHeader(req
, index
);
279 static void release_gzip_stream(http_request_t
*req
)
281 if(!req
->gzip_stream
->end_of_data
)
282 inflateEnd(&req
->gzip_stream
->zstream
);
283 heap_free(req
->gzip_stream
);
284 req
->gzip_stream
= NULL
;
289 static void init_gzip_stream(http_request_t
*req
)
291 ERR("gzip stream not supported, missing zlib.\n");
294 static void release_gzip_stream(http_request_t
*req
)
300 /* set the request content length based on the headers */
301 static DWORD
set_content_length( http_request_t
*request
)
303 static const WCHAR szChunked
[] = {'c','h','u','n','k','e','d',0};
307 size
= sizeof(request
->contentLength
);
308 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_CONTENT_LENGTH
,
309 &request
->contentLength
, &size
, NULL
) != ERROR_SUCCESS
)
310 request
->contentLength
= ~0u;
312 size
= sizeof(encoding
);
313 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_TRANSFER_ENCODING
, encoding
, &size
, NULL
) == ERROR_SUCCESS
&&
314 !strcmpiW(encoding
, szChunked
))
316 request
->contentLength
= ~0u;
317 request
->read_chunked
= TRUE
;
320 if(request
->decoding
) {
323 static const WCHAR gzipW
[] = {'g','z','i','p',0};
325 encoding_idx
= HTTP_GetCustomHeaderIndex(request
, szContent_Encoding
, 0, FALSE
);
326 if(encoding_idx
!= -1 && !strcmpiW(request
->custHeaders
[encoding_idx
].lpszValue
, gzipW
))
327 init_gzip_stream(request
);
330 return request
->contentLength
;
333 /***********************************************************************
334 * HTTP_Tokenize (internal)
336 * Tokenize a string, allocating memory for the tokens.
338 static LPWSTR
* HTTP_Tokenize(LPCWSTR string
, LPCWSTR token_string
)
340 LPWSTR
* token_array
;
347 /* empty string has no tokens */
351 for (i
= 0; string
[i
]; i
++)
353 if (!strncmpW(string
+i
, token_string
, strlenW(token_string
)))
357 /* we want to skip over separators, but not the null terminator */
358 for (j
= 0; j
< strlenW(token_string
) - 1; j
++)
366 /* add 1 for terminating NULL */
367 token_array
= HeapAlloc(GetProcessHeap(), 0, (tokens
+1) * sizeof(*token_array
));
368 token_array
[tokens
] = NULL
;
371 for (i
= 0; i
< tokens
; i
++)
374 next_token
= strstrW(string
, token_string
);
375 if (!next_token
) next_token
= string
+strlenW(string
);
376 len
= next_token
- string
;
377 token_array
[i
] = HeapAlloc(GetProcessHeap(), 0, (len
+1)*sizeof(WCHAR
));
378 memcpy(token_array
[i
], string
, len
*sizeof(WCHAR
));
379 token_array
[i
][len
] = '\0';
380 string
= next_token
+strlenW(token_string
);
385 /***********************************************************************
386 * HTTP_FreeTokens (internal)
388 * Frees memory returned from HTTP_Tokenize.
390 static void HTTP_FreeTokens(LPWSTR
* token_array
)
393 for (i
= 0; token_array
[i
]; i
++)
394 HeapFree(GetProcessHeap(), 0, token_array
[i
]);
395 HeapFree(GetProcessHeap(), 0, token_array
);
398 static void HTTP_FixURL(http_request_t
*request
)
400 static const WCHAR szSlash
[] = { '/',0 };
401 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/', 0 };
403 /* If we don't have a path we set it to root */
404 if (NULL
== request
->path
)
405 request
->path
= heap_strdupW(szSlash
);
406 else /* remove \r and \n*/
408 int nLen
= strlenW(request
->path
);
409 while ((nLen
>0 ) && ((request
->path
[nLen
-1] == '\r')||(request
->path
[nLen
-1] == '\n')))
412 request
->path
[nLen
]='\0';
414 /* Replace '\' with '/' */
417 if (request
->path
[nLen
] == '\\') request
->path
[nLen
]='/';
421 if(CSTR_EQUAL
!= CompareStringW( LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
422 request
->path
, strlenW(request
->path
), szHttp
, strlenW(szHttp
) )
423 && request
->path
[0] != '/') /* not an absolute path ?? --> fix it !! */
425 WCHAR
*fixurl
= HeapAlloc(GetProcessHeap(), 0,
426 (strlenW(request
->path
) + 2)*sizeof(WCHAR
));
428 strcpyW(fixurl
+ 1, request
->path
);
429 HeapFree( GetProcessHeap(), 0, request
->path
);
430 request
->path
= fixurl
;
434 static LPWSTR
HTTP_BuildHeaderRequestString( http_request_t
*request
, LPCWSTR verb
, LPCWSTR path
, LPCWSTR version
)
436 LPWSTR requestString
;
442 static const WCHAR szSpace
[] = { ' ',0 };
443 static const WCHAR szColon
[] = { ':',' ',0 };
444 static const WCHAR sztwocrlf
[] = {'\r','\n','\r','\n', 0};
446 /* allocate space for an array of all the string pointers to be added */
447 len
= (request
->nCustHeaders
)*4 + 10;
448 req
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(LPCWSTR
) );
450 /* add the verb, path and HTTP version string */
458 /* Append custom request headers */
459 for (i
= 0; i
< request
->nCustHeaders
; i
++)
461 if (request
->custHeaders
[i
].wFlags
& HDR_ISREQUEST
)
464 req
[n
++] = request
->custHeaders
[i
].lpszField
;
466 req
[n
++] = request
->custHeaders
[i
].lpszValue
;
468 TRACE("Adding custom header %s (%s)\n",
469 debugstr_w(request
->custHeaders
[i
].lpszField
),
470 debugstr_w(request
->custHeaders
[i
].lpszValue
));
475 ERR("oops. buffer overrun\n");
478 requestString
= HTTP_build_req( req
, 4 );
479 HeapFree( GetProcessHeap(), 0, req
);
482 * Set (header) termination string for request
483 * Make sure there's exactly two new lines at the end of the request
485 p
= &requestString
[strlenW(requestString
)-1];
486 while ( (*p
== '\n') || (*p
== '\r') )
488 strcpyW( p
+1, sztwocrlf
);
490 return requestString
;
493 static void HTTP_ProcessCookies( http_request_t
*request
)
497 LPHTTPHEADERW setCookieHeader
;
499 while((HeaderIndex
= HTTP_GetCustomHeaderIndex(request
, szSet_Cookie
, numCookies
, FALSE
)) != -1)
501 setCookieHeader
= &request
->custHeaders
[HeaderIndex
];
503 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
) && setCookieHeader
->lpszValue
)
506 static const WCHAR szFmt
[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
510 Host
= HTTP_GetHeader(request
, hostW
);
511 len
= lstrlenW(Host
->lpszValue
) + 9 + lstrlenW(request
->path
);
512 buf_url
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
513 sprintfW(buf_url
, szFmt
, Host
->lpszValue
, request
->path
);
514 InternetSetCookieW(buf_url
, NULL
, setCookieHeader
->lpszValue
);
516 HeapFree(GetProcessHeap(), 0, buf_url
);
522 static void strip_spaces(LPWSTR start
)
527 while (*str
== ' ' && *str
!= '\0')
531 memmove(start
, str
, sizeof(WCHAR
) * (strlenW(str
) + 1));
533 end
= start
+ strlenW(start
) - 1;
534 while (end
>= start
&& *end
== ' ')
541 static inline BOOL
is_basic_auth_value( LPCWSTR pszAuthValue
, LPWSTR
*pszRealm
)
543 static const WCHAR szBasic
[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
544 static const WCHAR szRealm
[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
546 is_basic
= !strncmpiW(pszAuthValue
, szBasic
, ARRAYSIZE(szBasic
)) &&
547 ((pszAuthValue
[ARRAYSIZE(szBasic
)] == ' ') || !pszAuthValue
[ARRAYSIZE(szBasic
)]);
548 if (is_basic
&& pszRealm
)
551 LPCWSTR ptr
= &pszAuthValue
[ARRAYSIZE(szBasic
)];
555 token
= strchrW(ptr
,'=');
559 while (*realm
== ' ' && *realm
!= '\0')
561 if(!strncmpiW(realm
, szRealm
, ARRAYSIZE(szRealm
)) &&
562 (realm
[ARRAYSIZE(szRealm
)] == ' ' || realm
[ARRAYSIZE(szRealm
)] == '='))
565 while (*token
== ' ' && *token
!= '\0')
569 *pszRealm
= heap_strdupW(token
);
570 strip_spaces(*pszRealm
);
577 static void destroy_authinfo( struct HttpAuthInfo
*authinfo
)
579 if (!authinfo
) return;
581 if (SecIsValidHandle(&authinfo
->ctx
))
582 DeleteSecurityContext(&authinfo
->ctx
);
583 if (SecIsValidHandle(&authinfo
->cred
))
584 FreeCredentialsHandle(&authinfo
->cred
);
586 HeapFree(GetProcessHeap(), 0, authinfo
->auth_data
);
587 HeapFree(GetProcessHeap(), 0, authinfo
->scheme
);
588 HeapFree(GetProcessHeap(), 0, authinfo
);
591 static UINT
retrieve_cached_basic_authorization(LPWSTR host
, LPWSTR realm
, LPSTR
*auth_data
)
593 basicAuthorizationData
*ad
;
596 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host
),debugstr_w(realm
));
598 EnterCriticalSection(&authcache_cs
);
599 LIST_FOR_EACH_ENTRY(ad
, &basicAuthorizationCache
, basicAuthorizationData
, entry
)
601 if (!strcmpiW(host
,ad
->host
) && !strcmpW(realm
,ad
->realm
))
603 TRACE("Authorization found in cache\n");
604 *auth_data
= HeapAlloc(GetProcessHeap(),0,ad
->authorizationLen
);
605 memcpy(*auth_data
,ad
->authorization
,ad
->authorizationLen
);
606 rc
= ad
->authorizationLen
;
610 LeaveCriticalSection(&authcache_cs
);
614 static void cache_basic_authorization(LPWSTR host
, LPWSTR realm
, LPSTR auth_data
, UINT auth_data_len
)
617 basicAuthorizationData
* ad
= NULL
;
619 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host
),debugstr_w(realm
),debugstr_an(auth_data
,auth_data_len
));
621 EnterCriticalSection(&authcache_cs
);
622 LIST_FOR_EACH(cursor
, &basicAuthorizationCache
)
624 basicAuthorizationData
*check
= LIST_ENTRY(cursor
,basicAuthorizationData
,entry
);
625 if (!strcmpiW(host
,check
->host
) && !strcmpW(realm
,check
->realm
))
634 TRACE("Found match in cache, replacing\n");
635 HeapFree(GetProcessHeap(),0,ad
->authorization
);
636 ad
->authorization
= HeapAlloc(GetProcessHeap(),0,auth_data_len
);
637 memcpy(ad
->authorization
, auth_data
, auth_data_len
);
638 ad
->authorizationLen
= auth_data_len
;
642 ad
= HeapAlloc(GetProcessHeap(),0,sizeof(basicAuthorizationData
));
643 ad
->host
= heap_strdupW(host
);
644 ad
->host
= heap_strdupW(realm
);
645 ad
->authorization
= HeapAlloc(GetProcessHeap(),0,auth_data_len
);
646 memcpy(ad
->authorization
, auth_data
, auth_data_len
);
647 ad
->authorizationLen
= auth_data_len
;
648 list_add_head(&basicAuthorizationCache
,&ad
->entry
);
649 TRACE("authorization cached\n");
651 LeaveCriticalSection(&authcache_cs
);
654 static BOOL
retrieve_cached_authorization(LPWSTR host
, LPWSTR scheme
,
655 SEC_WINNT_AUTH_IDENTITY_W
*nt_auth_identity
)
657 authorizationData
*ad
;
659 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host
), debugstr_w(scheme
));
661 EnterCriticalSection(&authcache_cs
);
662 LIST_FOR_EACH_ENTRY(ad
, &authorizationCache
, authorizationData
, entry
) {
663 if(!strcmpiW(host
, ad
->host
) && !strcmpiW(scheme
, ad
->scheme
)) {
664 TRACE("Authorization found in cache\n");
666 nt_auth_identity
->User
= heap_strdupW(ad
->user
);
667 nt_auth_identity
->Password
= heap_strdupW(ad
->password
);
668 nt_auth_identity
->Domain
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*ad
->domain_len
);
669 if(!nt_auth_identity
->User
|| !nt_auth_identity
->Password
||
670 (!nt_auth_identity
->Domain
&& ad
->domain_len
)) {
671 HeapFree(GetProcessHeap(), 0, nt_auth_identity
->User
);
672 HeapFree(GetProcessHeap(), 0, nt_auth_identity
->Password
);
673 HeapFree(GetProcessHeap(), 0, nt_auth_identity
->Domain
);
677 nt_auth_identity
->Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
678 nt_auth_identity
->UserLength
= ad
->user_len
;
679 nt_auth_identity
->PasswordLength
= ad
->password_len
;
680 memcpy(nt_auth_identity
->Domain
, ad
->domain
, sizeof(WCHAR
)*ad
->domain_len
);
681 nt_auth_identity
->DomainLength
= ad
->domain_len
;
682 LeaveCriticalSection(&authcache_cs
);
686 LeaveCriticalSection(&authcache_cs
);
691 static void cache_authorization(LPWSTR host
, LPWSTR scheme
,
692 SEC_WINNT_AUTH_IDENTITY_W
*nt_auth_identity
)
694 authorizationData
*ad
;
697 TRACE("Caching authorization for %s:%s\n", debugstr_w(host
), debugstr_w(scheme
));
699 EnterCriticalSection(&authcache_cs
);
700 LIST_FOR_EACH_ENTRY(ad
, &authorizationCache
, authorizationData
, entry
)
701 if(!strcmpiW(host
, ad
->host
) && !strcmpiW(scheme
, ad
->scheme
)) {
707 HeapFree(GetProcessHeap(), 0, ad
->user
);
708 HeapFree(GetProcessHeap(), 0, ad
->password
);
709 HeapFree(GetProcessHeap(), 0, ad
->domain
);
711 ad
= HeapAlloc(GetProcessHeap(), 0, sizeof(authorizationData
));
713 LeaveCriticalSection(&authcache_cs
);
717 ad
->host
= heap_strdupW(host
);
718 ad
->scheme
= heap_strdupW(scheme
);
719 list_add_head(&authorizationCache
, &ad
->entry
);
722 ad
->user
= heap_strndupW(nt_auth_identity
->User
, nt_auth_identity
->UserLength
);
723 ad
->password
= heap_strndupW(nt_auth_identity
->Password
, nt_auth_identity
->PasswordLength
);
724 ad
->domain
= heap_strndupW(nt_auth_identity
->Domain
, nt_auth_identity
->DomainLength
);
725 ad
->user_len
= nt_auth_identity
->UserLength
;
726 ad
->password_len
= nt_auth_identity
->PasswordLength
;
727 ad
->domain_len
= nt_auth_identity
->DomainLength
;
729 if(!ad
->host
|| !ad
->scheme
|| !ad
->user
|| !ad
->password
730 || (nt_auth_identity
->Domain
&& !ad
->domain
)) {
731 HeapFree(GetProcessHeap(), 0, ad
->host
);
732 HeapFree(GetProcessHeap(), 0, ad
->scheme
);
733 HeapFree(GetProcessHeap(), 0, ad
->user
);
734 HeapFree(GetProcessHeap(), 0, ad
->password
);
735 HeapFree(GetProcessHeap(), 0, ad
->domain
);
736 list_remove(&ad
->entry
);
737 HeapFree(GetProcessHeap(), 0, ad
);
740 LeaveCriticalSection(&authcache_cs
);
743 static BOOL
HTTP_DoAuthorization( http_request_t
*request
, LPCWSTR pszAuthValue
,
744 struct HttpAuthInfo
**ppAuthInfo
,
745 LPWSTR domain_and_username
, LPWSTR password
,
748 SECURITY_STATUS sec_status
;
749 struct HttpAuthInfo
*pAuthInfo
= *ppAuthInfo
;
751 LPWSTR szRealm
= NULL
;
753 TRACE("%s\n", debugstr_w(pszAuthValue
));
760 pAuthInfo
= HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo
));
764 SecInvalidateHandle(&pAuthInfo
->cred
);
765 SecInvalidateHandle(&pAuthInfo
->ctx
);
766 memset(&pAuthInfo
->exp
, 0, sizeof(pAuthInfo
->exp
));
768 pAuthInfo
->auth_data
= NULL
;
769 pAuthInfo
->auth_data_len
= 0;
770 pAuthInfo
->finished
= FALSE
;
772 if (is_basic_auth_value(pszAuthValue
,NULL
))
774 static const WCHAR szBasic
[] = {'B','a','s','i','c',0};
775 pAuthInfo
->scheme
= heap_strdupW(szBasic
);
776 if (!pAuthInfo
->scheme
)
778 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
785 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity
;
787 pAuthInfo
->scheme
= heap_strdupW(pszAuthValue
);
788 if (!pAuthInfo
->scheme
)
790 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
794 if (domain_and_username
)
796 WCHAR
*user
= strchrW(domain_and_username
, '\\');
797 WCHAR
*domain
= domain_and_username
;
799 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
801 pAuthData
= &nt_auth_identity
;
806 user
= domain_and_username
;
810 nt_auth_identity
.Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
811 nt_auth_identity
.User
= user
;
812 nt_auth_identity
.UserLength
= strlenW(nt_auth_identity
.User
);
813 nt_auth_identity
.Domain
= domain
;
814 nt_auth_identity
.DomainLength
= domain
? user
- domain
- 1 : 0;
815 nt_auth_identity
.Password
= password
;
816 nt_auth_identity
.PasswordLength
= strlenW(nt_auth_identity
.Password
);
818 cache_authorization(host
, pAuthInfo
->scheme
, &nt_auth_identity
);
820 else if(retrieve_cached_authorization(host
, pAuthInfo
->scheme
, &nt_auth_identity
))
821 pAuthData
= &nt_auth_identity
;
823 /* use default credentials */
826 sec_status
= AcquireCredentialsHandleW(NULL
, pAuthInfo
->scheme
,
827 SECPKG_CRED_OUTBOUND
, NULL
,
829 NULL
, &pAuthInfo
->cred
,
832 if(pAuthData
&& !domain_and_username
) {
833 HeapFree(GetProcessHeap(), 0, nt_auth_identity
.User
);
834 HeapFree(GetProcessHeap(), 0, nt_auth_identity
.Domain
);
835 HeapFree(GetProcessHeap(), 0, nt_auth_identity
.Password
);
838 if (sec_status
== SEC_E_OK
)
840 PSecPkgInfoW sec_pkg_info
;
841 sec_status
= QuerySecurityPackageInfoW(pAuthInfo
->scheme
, &sec_pkg_info
);
842 if (sec_status
== SEC_E_OK
)
844 pAuthInfo
->max_token
= sec_pkg_info
->cbMaxToken
;
845 FreeContextBuffer(sec_pkg_info
);
848 if (sec_status
!= SEC_E_OK
)
850 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
851 debugstr_w(pAuthInfo
->scheme
), sec_status
);
852 HeapFree(GetProcessHeap(), 0, pAuthInfo
->scheme
);
853 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
857 *ppAuthInfo
= pAuthInfo
;
859 else if (pAuthInfo
->finished
)
862 if ((strlenW(pszAuthValue
) < strlenW(pAuthInfo
->scheme
)) ||
863 strncmpiW(pszAuthValue
, pAuthInfo
->scheme
, strlenW(pAuthInfo
->scheme
)))
865 ERR("authentication scheme changed from %s to %s\n",
866 debugstr_w(pAuthInfo
->scheme
), debugstr_w(pszAuthValue
));
870 if (is_basic_auth_value(pszAuthValue
,&szRealm
))
874 char *auth_data
= NULL
;
875 UINT auth_data_len
= 0;
877 TRACE("basic authentication realm %s\n",debugstr_w(szRealm
));
879 if (!domain_and_username
)
882 auth_data_len
= retrieve_cached_basic_authorization(host
, szRealm
,&auth_data
);
883 if (auth_data_len
== 0)
885 HeapFree(GetProcessHeap(),0,szRealm
);
891 userlen
= WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, lstrlenW(domain_and_username
), NULL
, 0, NULL
, NULL
);
892 passlen
= WideCharToMultiByte(CP_UTF8
, 0, password
, lstrlenW(password
), NULL
, 0, NULL
, NULL
);
894 /* length includes a nul terminator, which will be re-used for the ':' */
895 auth_data
= HeapAlloc(GetProcessHeap(), 0, userlen
+ 1 + passlen
);
898 HeapFree(GetProcessHeap(),0,szRealm
);
902 WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, -1, auth_data
, userlen
, NULL
, NULL
);
903 auth_data
[userlen
] = ':';
904 WideCharToMultiByte(CP_UTF8
, 0, password
, -1, &auth_data
[userlen
+1], passlen
, NULL
, NULL
);
905 auth_data_len
= userlen
+ 1 + passlen
;
907 cache_basic_authorization(host
, szRealm
, auth_data
, auth_data_len
);
910 pAuthInfo
->auth_data
= auth_data
;
911 pAuthInfo
->auth_data_len
= auth_data_len
;
912 pAuthInfo
->finished
= TRUE
;
913 HeapFree(GetProcessHeap(),0,szRealm
);
920 SecBufferDesc out_desc
, in_desc
;
922 unsigned char *buffer
;
923 ULONG context_req
= ISC_REQ_CONNECTION
| ISC_REQ_USE_DCE_STYLE
|
924 ISC_REQ_MUTUAL_AUTH
| ISC_REQ_DELEGATE
;
926 in
.BufferType
= SECBUFFER_TOKEN
;
930 in_desc
.ulVersion
= 0;
931 in_desc
.cBuffers
= 1;
932 in_desc
.pBuffers
= &in
;
934 pszAuthData
= pszAuthValue
+ strlenW(pAuthInfo
->scheme
);
935 if (*pszAuthData
== ' ')
938 in
.cbBuffer
= HTTP_DecodeBase64(pszAuthData
, NULL
);
939 in
.pvBuffer
= HeapAlloc(GetProcessHeap(), 0, in
.cbBuffer
);
940 HTTP_DecodeBase64(pszAuthData
, in
.pvBuffer
);
943 buffer
= HeapAlloc(GetProcessHeap(), 0, pAuthInfo
->max_token
);
945 out
.BufferType
= SECBUFFER_TOKEN
;
946 out
.cbBuffer
= pAuthInfo
->max_token
;
947 out
.pvBuffer
= buffer
;
949 out_desc
.ulVersion
= 0;
950 out_desc
.cBuffers
= 1;
951 out_desc
.pBuffers
= &out
;
953 sec_status
= InitializeSecurityContextW(first
? &pAuthInfo
->cred
: NULL
,
954 first
? NULL
: &pAuthInfo
->ctx
,
955 first
? request
->session
->serverName
: NULL
,
956 context_req
, 0, SECURITY_NETWORK_DREP
,
957 in
.pvBuffer
? &in_desc
: NULL
,
958 0, &pAuthInfo
->ctx
, &out_desc
,
959 &pAuthInfo
->attr
, &pAuthInfo
->exp
);
960 if (sec_status
== SEC_E_OK
)
962 pAuthInfo
->finished
= TRUE
;
963 pAuthInfo
->auth_data
= out
.pvBuffer
;
964 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
965 TRACE("sending last auth packet\n");
967 else if (sec_status
== SEC_I_CONTINUE_NEEDED
)
969 pAuthInfo
->auth_data
= out
.pvBuffer
;
970 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
971 TRACE("sending next auth packet\n");
975 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status
);
976 HeapFree(GetProcessHeap(), 0, out
.pvBuffer
);
977 destroy_authinfo(pAuthInfo
);
986 /***********************************************************************
987 * HTTP_HttpAddRequestHeadersW (internal)
989 static DWORD
HTTP_HttpAddRequestHeadersW(http_request_t
*request
,
990 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
995 DWORD len
, res
= ERROR_HTTP_INVALID_HEADER
;
997 TRACE("copying header: %s\n", debugstr_wn(lpszHeader
, dwHeaderLength
));
999 if( dwHeaderLength
== ~0U )
1000 len
= strlenW(lpszHeader
);
1002 len
= dwHeaderLength
;
1003 buffer
= HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR
)*(len
+1) );
1004 lstrcpynW( buffer
, lpszHeader
, len
+ 1);
1010 LPWSTR
* pFieldAndValue
;
1012 lpszEnd
= lpszStart
;
1014 while (*lpszEnd
!= '\0')
1016 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
1021 if (*lpszStart
== '\0')
1024 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
1027 lpszEnd
++; /* Jump over newline */
1029 TRACE("interpreting header %s\n", debugstr_w(lpszStart
));
1030 if (*lpszStart
== '\0')
1032 /* Skip 0-length headers */
1033 lpszStart
= lpszEnd
;
1034 res
= ERROR_SUCCESS
;
1037 pFieldAndValue
= HTTP_InterpretHttpHeader(lpszStart
);
1040 res
= HTTP_VerifyValidHeader(request
, pFieldAndValue
[0]);
1041 if (res
== ERROR_SUCCESS
)
1042 res
= HTTP_ProcessHeader(request
, pFieldAndValue
[0],
1043 pFieldAndValue
[1], dwModifier
| HTTP_ADDHDR_FLAG_REQ
);
1044 HTTP_FreeTokens(pFieldAndValue
);
1047 lpszStart
= lpszEnd
;
1048 } while (res
== ERROR_SUCCESS
);
1050 HeapFree(GetProcessHeap(), 0, buffer
);
1055 /***********************************************************************
1056 * HttpAddRequestHeadersW (WININET.@)
1058 * Adds one or more HTTP header to the request handler
1061 * On Windows if dwHeaderLength includes the trailing '\0', then
1062 * HttpAddRequestHeadersW() adds it too. However this results in an
1063 * invalid Http header which is rejected by some servers so we probably
1064 * don't need to match Windows on that point.
1071 BOOL WINAPI
HttpAddRequestHeadersW(HINTERNET hHttpRequest
,
1072 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1074 http_request_t
*request
;
1075 DWORD res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
1077 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_wn(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
1082 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
1083 if (request
&& request
->hdr
.htype
== WH_HHTTPREQ
)
1084 res
= HTTP_HttpAddRequestHeadersW( request
, lpszHeader
, dwHeaderLength
, dwModifier
);
1086 WININET_Release( &request
->hdr
);
1088 if(res
!= ERROR_SUCCESS
)
1090 return res
== ERROR_SUCCESS
;
1093 /***********************************************************************
1094 * HttpAddRequestHeadersA (WININET.@)
1096 * Adds one or more HTTP header to the request handler
1103 BOOL WINAPI
HttpAddRequestHeadersA(HINTERNET hHttpRequest
,
1104 LPCSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1110 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_an(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
1112 len
= MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, NULL
, 0 );
1113 hdr
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
1114 MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, hdr
, len
);
1115 if( dwHeaderLength
!= ~0U )
1116 dwHeaderLength
= len
;
1118 r
= HttpAddRequestHeadersW( hHttpRequest
, hdr
, dwHeaderLength
, dwModifier
);
1120 HeapFree( GetProcessHeap(), 0, hdr
);
1125 /***********************************************************************
1126 * HttpOpenRequestA (WININET.@)
1128 * Open a HTTP request handle
1131 * HINTERNET a HTTP request handle on success
1135 HINTERNET WINAPI
HttpOpenRequestA(HINTERNET hHttpSession
,
1136 LPCSTR lpszVerb
, LPCSTR lpszObjectName
, LPCSTR lpszVersion
,
1137 LPCSTR lpszReferrer
, LPCSTR
*lpszAcceptTypes
,
1138 DWORD dwFlags
, DWORD_PTR dwContext
)
1140 LPWSTR szVerb
= NULL
, szObjectName
= NULL
;
1141 LPWSTR szVersion
= NULL
, szReferrer
= NULL
, *szAcceptTypes
= NULL
;
1142 INT acceptTypesCount
;
1143 HINTERNET rc
= FALSE
;
1146 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
1147 debugstr_a(lpszVerb
), debugstr_a(lpszObjectName
),
1148 debugstr_a(lpszVersion
), debugstr_a(lpszReferrer
), lpszAcceptTypes
,
1149 dwFlags
, dwContext
);
1153 szVerb
= heap_strdupAtoW(lpszVerb
);
1160 szObjectName
= heap_strdupAtoW(lpszObjectName
);
1161 if ( !szObjectName
)
1167 szVersion
= heap_strdupAtoW(lpszVersion
);
1174 szReferrer
= heap_strdupAtoW(lpszReferrer
);
1179 if (lpszAcceptTypes
)
1181 acceptTypesCount
= 0;
1182 types
= lpszAcceptTypes
;
1187 /* find out how many there are */
1188 if (*types
&& **types
)
1190 TRACE("accept type: %s\n", debugstr_a(*types
));
1196 WARN("invalid accept type pointer\n");
1201 szAcceptTypes
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
*) * (acceptTypesCount
+1));
1202 if (!szAcceptTypes
) goto end
;
1204 acceptTypesCount
= 0;
1205 types
= lpszAcceptTypes
;
1210 if (*types
&& **types
)
1211 szAcceptTypes
[acceptTypesCount
++] = heap_strdupAtoW(*types
);
1215 /* ignore invalid pointer */
1220 szAcceptTypes
[acceptTypesCount
] = NULL
;
1223 rc
= HttpOpenRequestW(hHttpSession
, szVerb
, szObjectName
,
1224 szVersion
, szReferrer
,
1225 (LPCWSTR
*)szAcceptTypes
, dwFlags
, dwContext
);
1230 acceptTypesCount
= 0;
1231 while (szAcceptTypes
[acceptTypesCount
])
1233 HeapFree(GetProcessHeap(), 0, szAcceptTypes
[acceptTypesCount
]);
1236 HeapFree(GetProcessHeap(), 0, szAcceptTypes
);
1238 HeapFree(GetProcessHeap(), 0, szReferrer
);
1239 HeapFree(GetProcessHeap(), 0, szVersion
);
1240 HeapFree(GetProcessHeap(), 0, szObjectName
);
1241 HeapFree(GetProcessHeap(), 0, szVerb
);
1246 /***********************************************************************
1249 static UINT
HTTP_EncodeBase64( LPCSTR bin
, unsigned int len
, LPWSTR base64
)
1252 static const CHAR HTTP_Base64Enc
[] =
1253 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1257 /* first 6 bits, all from bin[0] */
1258 base64
[n
++] = HTTP_Base64Enc
[(bin
[0] & 0xfc) >> 2];
1259 x
= (bin
[0] & 3) << 4;
1261 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1264 base64
[n
++] = HTTP_Base64Enc
[x
];
1269 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[1]&0xf0) >> 4 ) ];
1270 x
= ( bin
[1] & 0x0f ) << 2;
1272 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1275 base64
[n
++] = HTTP_Base64Enc
[x
];
1279 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[2]&0xc0 ) >> 6 ) ];
1281 /* last 6 bits, all from bin [2] */
1282 base64
[n
++] = HTTP_Base64Enc
[ bin
[2] & 0x3f ];
1290 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1291 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1292 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1293 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1294 static const signed char HTTP_Base64Dec
[256] =
1296 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1297 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1298 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1299 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1300 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1301 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1302 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1303 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1304 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1305 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1306 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1307 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1308 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1309 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1310 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1311 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1312 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1313 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1314 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1315 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1316 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1317 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1318 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1319 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1320 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1321 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1325 /***********************************************************************
1328 static UINT
HTTP_DecodeBase64( LPCWSTR base64
, LPSTR bin
)
1336 if (base64
[0] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1337 ((in
[0] = HTTP_Base64Dec
[base64
[0]]) == -1) ||
1338 base64
[1] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1339 ((in
[1] = HTTP_Base64Dec
[base64
[1]]) == -1))
1341 WARN("invalid base64: %s\n", debugstr_w(base64
));
1345 bin
[n
] = (unsigned char) (in
[0] << 2 | in
[1] >> 4);
1348 if ((base64
[2] == '=') && (base64
[3] == '='))
1350 if (base64
[2] > ARRAYSIZE(HTTP_Base64Dec
) ||
1351 ((in
[2] = HTTP_Base64Dec
[base64
[2]]) == -1))
1353 WARN("invalid base64: %s\n", debugstr_w(&base64
[2]));
1357 bin
[n
] = (unsigned char) (in
[1] << 4 | in
[2] >> 2);
1360 if (base64
[3] == '=')
1362 if (base64
[3] > ARRAYSIZE(HTTP_Base64Dec
) ||
1363 ((in
[3] = HTTP_Base64Dec
[base64
[3]]) == -1))
1365 WARN("invalid base64: %s\n", debugstr_w(&base64
[3]));
1369 bin
[n
] = (unsigned char) (((in
[2] << 6) & 0xc0) | in
[3]);
1378 /***********************************************************************
1379 * HTTP_InsertAuthorization
1381 * Insert or delete the authorization field in the request header.
1383 static BOOL
HTTP_InsertAuthorization( http_request_t
*request
, struct HttpAuthInfo
*pAuthInfo
, LPCWSTR header
)
1387 static const WCHAR wszSpace
[] = {' ',0};
1388 static const WCHAR wszBasic
[] = {'B','a','s','i','c',0};
1390 WCHAR
*authorization
= NULL
;
1392 if (pAuthInfo
->auth_data_len
)
1394 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1395 len
= strlenW(pAuthInfo
->scheme
)+1+((pAuthInfo
->auth_data_len
+2)*4)/3;
1396 authorization
= HeapAlloc(GetProcessHeap(), 0, (len
+1)*sizeof(WCHAR
));
1400 strcpyW(authorization
, pAuthInfo
->scheme
);
1401 strcatW(authorization
, wszSpace
);
1402 HTTP_EncodeBase64(pAuthInfo
->auth_data
,
1403 pAuthInfo
->auth_data_len
,
1404 authorization
+strlenW(authorization
));
1406 /* clear the data as it isn't valid now that it has been sent to the
1407 * server, unless it's Basic authentication which doesn't do
1408 * connection tracking */
1409 if (strcmpiW(pAuthInfo
->scheme
, wszBasic
))
1411 HeapFree(GetProcessHeap(), 0, pAuthInfo
->auth_data
);
1412 pAuthInfo
->auth_data
= NULL
;
1413 pAuthInfo
->auth_data_len
= 0;
1417 TRACE("Inserting authorization: %s\n", debugstr_w(authorization
));
1419 HTTP_ProcessHeader(request
, header
, authorization
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
1421 HeapFree(GetProcessHeap(), 0, authorization
);
1426 static WCHAR
*HTTP_BuildProxyRequestUrl(http_request_t
*req
)
1428 WCHAR new_location
[INTERNET_MAX_URL_LENGTH
], *url
;
1431 size
= sizeof(new_location
);
1432 if (HTTP_HttpQueryInfoW(req
, HTTP_QUERY_LOCATION
, new_location
, &size
, NULL
) == ERROR_SUCCESS
)
1434 if (!(url
= HeapAlloc( GetProcessHeap(), 0, size
+ sizeof(WCHAR
) ))) return NULL
;
1435 strcpyW( url
, new_location
);
1439 static const WCHAR slash
[] = { '/',0 };
1440 static const WCHAR format
[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1441 static const WCHAR formatSSL
[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1442 http_session_t
*session
= req
->session
;
1444 size
= 16; /* "https://" + sizeof(port#) + ":/\0" */
1445 size
+= strlenW( session
->hostName
) + strlenW( req
->path
);
1447 if (!(url
= HeapAlloc( GetProcessHeap(), 0, size
* sizeof(WCHAR
) ))) return NULL
;
1449 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1450 sprintfW( url
, formatSSL
, session
->hostName
, session
->hostPort
);
1452 sprintfW( url
, format
, session
->hostName
, session
->hostPort
);
1453 if (req
->path
[0] != '/') strcatW( url
, slash
);
1454 strcatW( url
, req
->path
);
1456 TRACE("url=%s\n", debugstr_w(url
));
1460 /***********************************************************************
1461 * HTTP_DealWithProxy
1463 static BOOL
HTTP_DealWithProxy(appinfo_t
*hIC
, http_session_t
*session
, http_request_t
*request
)
1465 WCHAR buf
[MAXHOSTNAME
];
1466 WCHAR protoProxy
[MAXHOSTNAME
+ 15];
1467 DWORD protoProxyLen
= sizeof(protoProxy
) / sizeof(protoProxy
[0]);
1468 WCHAR proxy
[MAXHOSTNAME
+ 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1469 static WCHAR szNul
[] = { 0 };
1470 URL_COMPONENTSW UrlComponents
;
1471 static const WCHAR protoHttp
[] = { 'h','t','t','p',0 };
1472 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/',0 };
1473 static const WCHAR szFormat
[] = { 'h','t','t','p',':','/','/','%','s',0 };
1475 memset( &UrlComponents
, 0, sizeof UrlComponents
);
1476 UrlComponents
.dwStructSize
= sizeof UrlComponents
;
1477 UrlComponents
.lpszHostName
= buf
;
1478 UrlComponents
.dwHostNameLength
= MAXHOSTNAME
;
1480 if (!INTERNET_FindProxyForProtocol(hIC
->proxy
, protoHttp
, protoProxy
, &protoProxyLen
))
1482 if( CSTR_EQUAL
!= CompareStringW(LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
1483 protoProxy
,strlenW(szHttp
),szHttp
,strlenW(szHttp
)) )
1484 sprintfW(proxy
, szFormat
, protoProxy
);
1486 strcpyW(proxy
, protoProxy
);
1487 if( !InternetCrackUrlW(proxy
, 0, 0, &UrlComponents
) )
1489 if( UrlComponents
.dwHostNameLength
== 0 )
1492 if( !request
->path
)
1493 request
->path
= szNul
;
1495 if(UrlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
1496 UrlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
1498 HeapFree(GetProcessHeap(), 0, session
->serverName
);
1499 session
->serverName
= heap_strdupW(UrlComponents
.lpszHostName
);
1500 session
->serverPort
= UrlComponents
.nPort
;
1502 TRACE("proxy server=%s port=%d\n", debugstr_w(session
->serverName
), session
->serverPort
);
1506 #ifndef INET6_ADDRSTRLEN
1507 #define INET6_ADDRSTRLEN 46
1510 static DWORD
HTTP_ResolveName(http_request_t
*request
)
1512 char szaddr
[INET6_ADDRSTRLEN
];
1513 http_session_t
*session
= request
->session
;
1516 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
1517 INTERNET_STATUS_RESOLVING_NAME
,
1518 session
->serverName
,
1519 (strlenW(session
->serverName
)+1) * sizeof(WCHAR
));
1521 session
->sa_len
= sizeof(session
->socketAddress
);
1522 if (!GetAddress(session
->serverName
, session
->serverPort
,
1523 (struct sockaddr
*)&session
->socketAddress
, &session
->sa_len
))
1524 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
1526 switch (session
->socketAddress
.ss_family
)
1529 addr
= &((struct sockaddr_in
*)&session
->socketAddress
)->sin_addr
;
1532 addr
= &((struct sockaddr_in6
*)&session
->socketAddress
)->sin6_addr
;
1535 WARN("unsupported family %d\n", session
->socketAddress
.ss_family
);
1536 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
1538 inet_ntop(session
->socketAddress
.ss_family
, addr
, szaddr
, sizeof(szaddr
));
1539 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
1540 INTERNET_STATUS_NAME_RESOLVED
,
1541 szaddr
, strlen(szaddr
)+1);
1543 TRACE("resolved %s to %s\n", debugstr_w(session
->serverName
), szaddr
);
1544 return ERROR_SUCCESS
;
1547 static BOOL
HTTP_GetRequestURL(http_request_t
*req
, LPWSTR buf
)
1549 static const WCHAR http
[] = { 'h','t','t','p',':','/','/',0 };
1550 static const WCHAR https
[] = { 'h','t','t','p','s',':','/','/',0 };
1551 static const WCHAR slash
[] = { '/',0 };
1552 LPHTTPHEADERW host_header
;
1555 host_header
= HTTP_GetHeader(req
, hostW
);
1559 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1563 strcpyW(buf
, scheme
);
1564 strcatW(buf
, host_header
->lpszValue
);
1565 if (req
->path
[0] != '/')
1566 strcatW(buf
, slash
);
1567 strcatW(buf
, req
->path
);
1572 /***********************************************************************
1573 * HTTPREQ_Destroy (internal)
1575 * Deallocate request handle
1578 static void HTTPREQ_Destroy(object_header_t
*hdr
)
1580 http_request_t
*request
= (http_request_t
*) hdr
;
1585 if(request
->hCacheFile
) {
1586 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1588 CloseHandle(request
->hCacheFile
);
1590 if(HTTP_GetRequestURL(request
, url
)) {
1593 headersLen
= request
->rawHeaders
? strlenW(request
->rawHeaders
) : 0;
1594 CommitUrlCacheEntryW(url
, request
->cacheFile
, request
->expires
,
1595 request
->last_modified
, NORMAL_CACHE_ENTRY
,
1596 request
->rawHeaders
, headersLen
, NULL
, 0);
1600 HeapFree(GetProcessHeap(), 0, request
->cacheFile
);
1602 DeleteCriticalSection( &request
->read_section
);
1603 WININET_Release(&request
->session
->hdr
);
1605 destroy_authinfo(request
->authInfo
);
1606 destroy_authinfo(request
->proxyAuthInfo
);
1608 HeapFree(GetProcessHeap(), 0, request
->path
);
1609 HeapFree(GetProcessHeap(), 0, request
->verb
);
1610 HeapFree(GetProcessHeap(), 0, request
->rawHeaders
);
1611 HeapFree(GetProcessHeap(), 0, request
->version
);
1612 HeapFree(GetProcessHeap(), 0, request
->statusText
);
1614 for (i
= 0; i
< request
->nCustHeaders
; i
++)
1616 HeapFree(GetProcessHeap(), 0, request
->custHeaders
[i
].lpszField
);
1617 HeapFree(GetProcessHeap(), 0, request
->custHeaders
[i
].lpszValue
);
1620 if(request
->gzip_stream
)
1621 release_gzip_stream(request
);
1623 HeapFree(GetProcessHeap(), 0, request
->custHeaders
);
1626 static void HTTPREQ_CloseConnection(object_header_t
*hdr
)
1628 http_request_t
*request
= (http_request_t
*) hdr
;
1630 TRACE("%p\n",request
);
1632 if (!NETCON_connected(&request
->netConnection
))
1635 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
1636 INTERNET_STATUS_CLOSING_CONNECTION
, 0, 0);
1638 NETCON_close(&request
->netConnection
);
1640 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
1641 INTERNET_STATUS_CONNECTION_CLOSED
, 0, 0);
1644 static BOOL
HTTP_KeepAlive(http_request_t
*request
)
1646 WCHAR szVersion
[10];
1647 WCHAR szConnectionResponse
[20];
1648 DWORD dwBufferSize
= sizeof(szVersion
);
1649 BOOL keepalive
= FALSE
;
1651 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1652 * the connection is keep-alive by default */
1653 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_VERSION
, szVersion
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1654 && !strcmpiW(szVersion
, g_szHttp1_1
))
1659 dwBufferSize
= sizeof(szConnectionResponse
);
1660 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_PROXY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1661 || HTTP_HttpQueryInfoW(request
, HTTP_QUERY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
)
1663 keepalive
= !strcmpiW(szConnectionResponse
, szKeepAlive
);
1669 static DWORD
HTTPREQ_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
1671 http_request_t
*req
= (http_request_t
*)hdr
;
1674 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO
:
1676 http_session_t
*session
= req
->session
;
1677 INTERNET_DIAGNOSTIC_SOCKET_INFO
*info
= buffer
;
1679 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1681 if (*size
< sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
))
1682 return ERROR_INSUFFICIENT_BUFFER
;
1683 *size
= sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
);
1684 /* FIXME: can't get a SOCKET from our connection since we don't use
1688 /* FIXME: get source port from req->netConnection */
1689 info
->SourcePort
= 0;
1690 info
->DestPort
= session
->hostPort
;
1692 if (HTTP_KeepAlive(req
))
1693 info
->Flags
|= IDSI_FLAG_KEEP_ALIVE
;
1694 if (session
->appInfo
->proxy
&& session
->appInfo
->proxy
[0] != 0)
1695 info
->Flags
|= IDSI_FLAG_PROXY
;
1696 if (req
->netConnection
.useSSL
)
1697 info
->Flags
|= IDSI_FLAG_SECURE
;
1699 return ERROR_SUCCESS
;
1702 case INTERNET_OPTION_SECURITY_FLAGS
:
1707 if (*size
< sizeof(ULONG
))
1708 return ERROR_INSUFFICIENT_BUFFER
;
1710 *size
= sizeof(DWORD
);
1712 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1713 flags
|= SECURITY_FLAG_SECURE
;
1714 flags
|= req
->netConnection
.security_flags
;
1715 bits
= NETCON_GetCipherStrength(&req
->netConnection
);
1717 flags
|= SECURITY_FLAG_STRENGTH_STRONG
;
1718 else if (bits
>= 56)
1719 flags
|= SECURITY_FLAG_STRENGTH_MEDIUM
;
1721 flags
|= SECURITY_FLAG_STRENGTH_WEAK
;
1722 *(DWORD
*)buffer
= flags
;
1723 return ERROR_SUCCESS
;
1726 case INTERNET_OPTION_HANDLE_TYPE
:
1727 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1729 if (*size
< sizeof(ULONG
))
1730 return ERROR_INSUFFICIENT_BUFFER
;
1732 *size
= sizeof(DWORD
);
1733 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_HTTP_REQUEST
;
1734 return ERROR_SUCCESS
;
1736 case INTERNET_OPTION_URL
: {
1737 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1742 static const WCHAR httpW
[] = {'h','t','t','p',':','/','/',0};
1744 TRACE("INTERNET_OPTION_URL\n");
1746 host
= HTTP_GetHeader(req
, hostW
);
1747 strcpyW(url
, httpW
);
1748 strcatW(url
, host
->lpszValue
);
1749 if (NULL
!= (pch
= strchrW(url
+ strlenW(httpW
), ':')))
1751 strcatW(url
, req
->path
);
1753 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url
));
1756 len
= (strlenW(url
)+1) * sizeof(WCHAR
);
1758 return ERROR_INSUFFICIENT_BUFFER
;
1761 strcpyW(buffer
, url
);
1762 return ERROR_SUCCESS
;
1764 len
= WideCharToMultiByte(CP_ACP
, 0, url
, -1, buffer
, *size
, NULL
, NULL
);
1766 return ERROR_INSUFFICIENT_BUFFER
;
1769 return ERROR_SUCCESS
;
1773 case INTERNET_OPTION_CACHE_TIMESTAMPS
: {
1774 INTERNET_CACHE_ENTRY_INFOW
*info
;
1775 INTERNET_CACHE_TIMESTAMPS
*ts
= buffer
;
1776 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1777 DWORD nbytes
, error
;
1780 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1782 if (*size
< sizeof(*ts
))
1784 *size
= sizeof(*ts
);
1785 return ERROR_INSUFFICIENT_BUFFER
;
1788 HTTP_GetRequestURL(req
, url
);
1789 ret
= GetUrlCacheEntryInfoW(url
, NULL
, &nbytes
);
1790 error
= GetLastError();
1791 if (!ret
&& error
== ERROR_INSUFFICIENT_BUFFER
)
1793 if (!(info
= HeapAlloc(GetProcessHeap(), 0, nbytes
)))
1794 return ERROR_OUTOFMEMORY
;
1796 GetUrlCacheEntryInfoW(url
, info
, &nbytes
);
1798 ts
->ftExpires
= info
->ExpireTime
;
1799 ts
->ftLastModified
= info
->LastModifiedTime
;
1801 HeapFree(GetProcessHeap(), 0, info
);
1802 *size
= sizeof(*ts
);
1803 return ERROR_SUCCESS
;
1808 case INTERNET_OPTION_DATAFILE_NAME
: {
1811 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1813 if(!req
->cacheFile
) {
1815 return ERROR_INTERNET_ITEM_NOT_FOUND
;
1819 req_size
= (lstrlenW(req
->cacheFile
)+1) * sizeof(WCHAR
);
1820 if(*size
< req_size
)
1821 return ERROR_INSUFFICIENT_BUFFER
;
1824 memcpy(buffer
, req
->cacheFile
, *size
);
1825 return ERROR_SUCCESS
;
1827 req_size
= WideCharToMultiByte(CP_ACP
, 0, req
->cacheFile
, -1, NULL
, 0, NULL
, NULL
);
1828 if (req_size
> *size
)
1829 return ERROR_INSUFFICIENT_BUFFER
;
1831 *size
= WideCharToMultiByte(CP_ACP
, 0, req
->cacheFile
,
1832 -1, buffer
, *size
, NULL
, NULL
);
1833 return ERROR_SUCCESS
;
1837 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT
: {
1838 PCCERT_CONTEXT context
;
1840 if(*size
< sizeof(INTERNET_CERTIFICATE_INFOA
)) {
1841 *size
= sizeof(INTERNET_CERTIFICATE_INFOA
);
1842 return ERROR_INSUFFICIENT_BUFFER
;
1845 context
= (PCCERT_CONTEXT
)NETCON_GetCert(&(req
->netConnection
));
1847 INTERNET_CERTIFICATE_INFOA
*info
= (INTERNET_CERTIFICATE_INFOA
*)buffer
;
1850 memset(info
, 0, sizeof(INTERNET_CERTIFICATE_INFOW
));
1851 info
->ftExpiry
= context
->pCertInfo
->NotAfter
;
1852 info
->ftStart
= context
->pCertInfo
->NotBefore
;
1853 len
= CertNameToStrA(context
->dwCertEncodingType
,
1854 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1855 info
->lpszSubjectInfo
= LocalAlloc(0, len
);
1856 if(info
->lpszSubjectInfo
)
1857 CertNameToStrA(context
->dwCertEncodingType
,
1858 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
,
1859 info
->lpszSubjectInfo
, len
);
1860 len
= CertNameToStrA(context
->dwCertEncodingType
,
1861 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1862 info
->lpszIssuerInfo
= LocalAlloc(0, len
);
1863 if(info
->lpszIssuerInfo
)
1864 CertNameToStrA(context
->dwCertEncodingType
,
1865 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
,
1866 info
->lpszIssuerInfo
, len
);
1867 info
->dwKeySize
= NETCON_GetCipherStrength(&req
->netConnection
);
1868 CertFreeCertificateContext(context
);
1869 return ERROR_SUCCESS
;
1874 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
1877 static DWORD
HTTPREQ_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
1879 http_request_t
*req
= (http_request_t
*)hdr
;
1882 case INTERNET_OPTION_SECURITY_FLAGS
:
1886 if (!buffer
|| size
!= sizeof(DWORD
))
1887 return ERROR_INVALID_PARAMETER
;
1888 flags
= *(DWORD
*)buffer
;
1889 TRACE("%08x\n", flags
);
1890 req
->netConnection
.security_flags
= flags
;
1891 return ERROR_SUCCESS
;
1893 case INTERNET_OPTION_SEND_TIMEOUT
:
1894 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
1895 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1897 if (size
!= sizeof(DWORD
))
1898 return ERROR_INVALID_PARAMETER
;
1900 return NETCON_set_timeout(&req
->netConnection
, option
== INTERNET_OPTION_SEND_TIMEOUT
,
1903 case INTERNET_OPTION_USERNAME
:
1904 HeapFree(GetProcessHeap(), 0, req
->session
->userName
);
1905 if (!(req
->session
->userName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
1906 return ERROR_SUCCESS
;
1908 case INTERNET_OPTION_PASSWORD
:
1909 HeapFree(GetProcessHeap(), 0, req
->session
->password
);
1910 if (!(req
->session
->password
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
1911 return ERROR_SUCCESS
;
1912 case INTERNET_OPTION_HTTP_DECODING
:
1913 if(size
!= sizeof(BOOL
))
1914 return ERROR_INVALID_PARAMETER
;
1915 req
->decoding
= *(BOOL
*)buffer
;
1916 return ERROR_SUCCESS
;
1919 return ERROR_INTERNET_INVALID_OPTION
;
1922 /* read some more data into the read buffer (the read section must be held) */
1923 static DWORD
read_more_data( http_request_t
*req
, int maxlen
)
1930 /* move existing data to the start of the buffer */
1932 memmove( req
->read_buf
, req
->read_buf
+ req
->read_pos
, req
->read_size
);
1936 if (maxlen
== -1) maxlen
= sizeof(req
->read_buf
);
1938 res
= NETCON_recv( &req
->netConnection
, req
->read_buf
+ req
->read_size
,
1939 maxlen
- req
->read_size
, 0, &len
);
1940 if(res
== ERROR_SUCCESS
)
1941 req
->read_size
+= len
;
1946 /* remove some amount of data from the read buffer (the read section must be held) */
1947 static void remove_data( http_request_t
*req
, int count
)
1949 if (!(req
->read_size
-= count
)) req
->read_pos
= 0;
1950 else req
->read_pos
+= count
;
1953 static BOOL
read_line( http_request_t
*req
, LPSTR buffer
, DWORD
*len
)
1955 int count
, bytes_read
, pos
= 0;
1958 EnterCriticalSection( &req
->read_section
);
1961 BYTE
*eol
= memchr( req
->read_buf
+ req
->read_pos
, '\n', req
->read_size
);
1965 count
= eol
- (req
->read_buf
+ req
->read_pos
);
1966 bytes_read
= count
+ 1;
1968 else count
= bytes_read
= req
->read_size
;
1970 count
= min( count
, *len
- pos
);
1971 memcpy( buffer
+ pos
, req
->read_buf
+ req
->read_pos
, count
);
1973 remove_data( req
, bytes_read
);
1976 if ((res
= read_more_data( req
, -1 )) != ERROR_SUCCESS
|| !req
->read_size
)
1979 TRACE( "returning empty string\n" );
1980 LeaveCriticalSection( &req
->read_section
);
1981 INTERNET_SetLastError(res
);
1985 LeaveCriticalSection( &req
->read_section
);
1989 if (pos
&& buffer
[pos
- 1] == '\r') pos
--;
1992 buffer
[*len
- 1] = 0;
1993 TRACE( "returning %s\n", debugstr_a(buffer
));
1997 /* discard data contents until we reach end of line (the read section must be held) */
1998 static DWORD
discard_eol( http_request_t
*req
)
2004 BYTE
*eol
= memchr( req
->read_buf
+ req
->read_pos
, '\n', req
->read_size
);
2007 remove_data( req
, (eol
+ 1) - (req
->read_buf
+ req
->read_pos
) );
2010 req
->read_pos
= req
->read_size
= 0; /* discard everything */
2011 if ((res
= read_more_data( req
, -1 )) != ERROR_SUCCESS
) return res
;
2012 } while (req
->read_size
);
2013 return ERROR_SUCCESS
;
2016 /* read the size of the next chunk (the read section must be held) */
2017 static DWORD
start_next_chunk( http_request_t
*req
)
2019 DWORD chunk_size
= 0, res
;
2021 if (!req
->contentLength
) return ERROR_SUCCESS
;
2022 if (req
->contentLength
== req
->contentRead
)
2024 /* read terminator for the previous chunk */
2025 if ((res
= discard_eol( req
)) != ERROR_SUCCESS
) return res
;
2026 req
->contentLength
= ~0u;
2027 req
->contentRead
= 0;
2031 while (req
->read_size
)
2033 char ch
= req
->read_buf
[req
->read_pos
];
2034 if (ch
>= '0' && ch
<= '9') chunk_size
= chunk_size
* 16 + ch
- '0';
2035 else if (ch
>= 'a' && ch
<= 'f') chunk_size
= chunk_size
* 16 + ch
- 'a' + 10;
2036 else if (ch
>= 'A' && ch
<= 'F') chunk_size
= chunk_size
* 16 + ch
- 'A' + 10;
2037 else if (ch
== ';' || ch
== '\r' || ch
== '\n')
2039 TRACE( "reading %u byte chunk\n", chunk_size
);
2040 req
->contentLength
= chunk_size
;
2041 req
->contentRead
= 0;
2042 return discard_eol( req
);
2044 remove_data( req
, 1 );
2046 if ((res
= read_more_data( req
, -1 )) != ERROR_SUCCESS
) return res
;
2047 if (!req
->read_size
)
2049 req
->contentLength
= req
->contentRead
= 0;
2050 return ERROR_SUCCESS
;
2055 /* check if we have reached the end of the data to read (the read section must be held) */
2056 static BOOL
end_of_read_data( http_request_t
*req
)
2058 if (req
->gzip_stream
) return req
->gzip_stream
->end_of_data
&& !req
->gzip_stream
->buf_size
;
2059 if (req
->read_chunked
) return (req
->contentLength
== 0);
2060 if (req
->contentLength
== ~0u) return FALSE
;
2061 return (req
->contentLength
== req
->contentRead
);
2064 /* fetch some more data into the read buffer (the read section must be held) */
2065 static DWORD
refill_read_buffer( http_request_t
*req
)
2067 int len
= sizeof(req
->read_buf
);
2070 if (req
->read_chunked
&& (req
->contentLength
== ~0u || req
->contentLength
== req
->contentRead
))
2072 if ((res
= start_next_chunk( req
)) != ERROR_SUCCESS
) return res
;
2075 if (req
->contentLength
!= ~0u) len
= min( len
, req
->contentLength
- req
->contentRead
);
2076 if (len
<= req
->read_size
) return ERROR_SUCCESS
;
2078 if ((res
= read_more_data( req
, len
)) != ERROR_SUCCESS
) return res
;
2079 if (!req
->read_size
) req
->contentLength
= req
->contentRead
= 0;
2080 return ERROR_SUCCESS
;
2083 static DWORD
read_gzip_data(http_request_t
*req
, BYTE
*buf
, int size
, BOOL sync
, int *read_ret
)
2085 DWORD ret
= ERROR_SUCCESS
;
2089 z_stream
*zstream
= &req
->gzip_stream
->zstream
;
2093 while(read
< size
&& !req
->gzip_stream
->end_of_data
) {
2094 if(!req
->read_size
) {
2095 if((!sync
&& read
) || (ret
= refill_read_buffer(req
)) != ERROR_SUCCESS
)
2099 if(req
->contentRead
== req
->contentLength
)
2102 buf_avail
= req
->contentLength
== ~0 ? req
->read_size
: min(req
->read_size
, req
->contentLength
-req
->contentRead
);
2104 zstream
->next_in
= req
->read_buf
+req
->read_pos
;
2105 zstream
->avail_in
= buf_avail
;
2106 zstream
->next_out
= buf
+read
;
2107 zstream
->avail_out
= size
-read
;
2108 zres
= inflate(zstream
, Z_FULL_FLUSH
);
2109 read
= size
- zstream
->avail_out
;
2110 req
->contentRead
+= buf_avail
-zstream
->avail_in
;
2111 remove_data(req
, buf_avail
-zstream
->avail_in
);
2112 if(zres
== Z_STREAM_END
) {
2113 TRACE("end of data\n");
2114 req
->gzip_stream
->end_of_data
= TRUE
;
2115 inflateEnd(&req
->gzip_stream
->zstream
);
2116 }else if(zres
!= Z_OK
) {
2117 WARN("inflate failed %d\n", zres
);
2119 ret
= ERROR_INTERNET_DECODING_FAILED
;
2129 static DWORD
refill_gzip_buffer(http_request_t
*req
)
2134 if(req
->gzip_stream
->buf_size
== sizeof(req
->gzip_stream
->buf
))
2135 return ERROR_SUCCESS
;
2137 if(req
->gzip_stream
->buf_pos
) {
2138 if(req
->gzip_stream
->buf_size
)
2139 memmove(req
->gzip_stream
->buf
, req
->gzip_stream
->buf
+ req
->gzip_stream
->buf_pos
, req
->gzip_stream
->buf_size
);
2140 req
->gzip_stream
->buf_pos
= 0;
2143 res
= read_gzip_data(req
, req
->gzip_stream
->buf
+ req
->gzip_stream
->buf_size
,
2144 sizeof(req
->gzip_stream
->buf
) - req
->gzip_stream
->buf_size
, FALSE
, &len
);
2145 if(res
== ERROR_SUCCESS
)
2146 req
->gzip_stream
->buf_size
+= len
;
2151 static DWORD
refill_buffer( http_request_t
*req
)
2153 return req
->gzip_stream
? refill_gzip_buffer(req
) : refill_read_buffer(req
);
2156 /* return the size of data available to be read immediately (the read section must be held) */
2157 static DWORD
get_avail_data( http_request_t
*req
)
2159 if (req
->gzip_stream
)
2160 return req
->gzip_stream
->buf_size
;
2161 if (req
->read_chunked
&& (req
->contentLength
== ~0u || req
->contentLength
== req
->contentRead
))
2163 return min( req
->read_size
, req
->contentLength
- req
->contentRead
);
2166 static void HTTP_ReceiveRequestData(http_request_t
*req
, BOOL first_notif
)
2168 INTERNET_ASYNC_RESULT iar
;
2173 EnterCriticalSection( &req
->read_section
);
2174 if ((res
= refill_buffer( req
)) == ERROR_SUCCESS
) {
2175 iar
.dwResult
= (DWORD_PTR
)req
->hdr
.hInternet
;
2176 iar
.dwError
= first_notif
? 0 : get_avail_data(req
);
2181 LeaveCriticalSection( &req
->read_section
);
2183 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2184 sizeof(INTERNET_ASYNC_RESULT
));
2187 /* read data from the http connection (the read section must be held) */
2188 static DWORD
HTTPREQ_Read(http_request_t
*req
, void *buffer
, DWORD size
, DWORD
*read
, BOOL sync
)
2190 BOOL finished_reading
= FALSE
;
2191 int len
, bytes_read
= 0;
2192 DWORD ret
= ERROR_SUCCESS
;
2194 EnterCriticalSection( &req
->read_section
);
2196 if (req
->read_chunked
&& (req
->contentLength
== ~0u || req
->contentLength
== req
->contentRead
))
2198 if (start_next_chunk( req
) != ERROR_SUCCESS
) goto done
;
2201 if(req
->gzip_stream
) {
2202 if(req
->gzip_stream
->buf_size
) {
2203 bytes_read
= min(req
->gzip_stream
->buf_size
, size
);
2204 memcpy(buffer
, req
->gzip_stream
->buf
+ req
->gzip_stream
->buf_pos
, bytes_read
);
2205 req
->gzip_stream
->buf_pos
+= bytes_read
;
2206 req
->gzip_stream
->buf_size
-= bytes_read
;
2207 }else if(!req
->read_size
&& !req
->gzip_stream
->end_of_data
) {
2211 if(size
> bytes_read
) {
2212 ret
= read_gzip_data(req
, (BYTE
*)buffer
+bytes_read
, size
-bytes_read
, sync
, &len
);
2213 if(ret
== ERROR_SUCCESS
)
2217 finished_reading
= req
->gzip_stream
->end_of_data
&& !req
->gzip_stream
->buf_size
;
2219 if (req
->contentLength
!= ~0u) size
= min( size
, req
->contentLength
- req
->contentRead
);
2221 if (req
->read_size
) {
2222 bytes_read
= min( req
->read_size
, size
);
2223 memcpy( buffer
, req
->read_buf
+ req
->read_pos
, bytes_read
);
2224 remove_data( req
, bytes_read
);
2227 if (size
> bytes_read
&& (!bytes_read
|| sync
)) {
2228 if (NETCON_recv( &req
->netConnection
, (char *)buffer
+ bytes_read
, size
- bytes_read
,
2229 sync
? MSG_WAITALL
: 0, &len
) == ERROR_SUCCESS
)
2231 /* always return success, even if the network layer returns an error */
2234 finished_reading
= !bytes_read
&& req
->contentRead
== req
->contentLength
;
2235 req
->contentRead
+= bytes_read
;
2240 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read
, req
->contentRead
, req
->contentLength
);
2241 LeaveCriticalSection( &req
->read_section
);
2243 if(ret
== ERROR_SUCCESS
&& req
->cacheFile
) {
2245 DWORD dwBytesWritten
;
2247 res
= WriteFile(req
->hCacheFile
, buffer
, bytes_read
, &dwBytesWritten
, NULL
);
2249 WARN("WriteFile failed: %u\n", GetLastError());
2252 if(finished_reading
)
2253 HTTP_FinishedReading(req
);
2259 static DWORD
HTTPREQ_ReadFile(object_header_t
*hdr
, void *buffer
, DWORD size
, DWORD
*read
)
2261 http_request_t
*req
= (http_request_t
*)hdr
;
2264 EnterCriticalSection( &req
->read_section
);
2265 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2266 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2268 res
= HTTPREQ_Read(req
, buffer
, size
, read
, TRUE
);
2269 if(res
== ERROR_SUCCESS
)
2271 LeaveCriticalSection( &req
->read_section
);
2276 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST
*workRequest
)
2278 struct WORKREQ_INTERNETREADFILEEXA
const *data
= &workRequest
->u
.InternetReadFileExA
;
2279 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2280 INTERNET_ASYNC_RESULT iar
;
2283 TRACE("INTERNETREADFILEEXA %p\n", workRequest
->hdr
);
2285 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2286 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2288 iar
.dwResult
= res
== ERROR_SUCCESS
;
2291 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2292 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2293 sizeof(INTERNET_ASYNC_RESULT
));
2296 static DWORD
HTTPREQ_ReadFileExA(object_header_t
*hdr
, INTERNET_BUFFERSA
*buffers
,
2297 DWORD flags
, DWORD_PTR context
)
2299 http_request_t
*req
= (http_request_t
*)hdr
;
2300 DWORD res
, size
, read
, error
= ERROR_SUCCESS
;
2302 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2303 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2305 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2306 return ERROR_INVALID_PARAMETER
;
2308 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2310 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2312 WORKREQUEST workRequest
;
2314 if (TryEnterCriticalSection( &req
->read_section
))
2316 if (get_avail_data(req
))
2318 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2319 &buffers
->dwBufferLength
, FALSE
);
2320 size
= buffers
->dwBufferLength
;
2321 LeaveCriticalSection( &req
->read_section
);
2324 LeaveCriticalSection( &req
->read_section
);
2327 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExAProc
;
2328 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2329 workRequest
.u
.InternetReadFileExA
.lpBuffersOut
= buffers
;
2331 INTERNET_AsyncCall(&workRequest
);
2333 return ERROR_IO_PENDING
;
2337 size
= buffers
->dwBufferLength
;
2339 EnterCriticalSection( &req
->read_section
);
2340 if(hdr
->dwError
== ERROR_SUCCESS
)
2341 hdr
->dwError
= INTERNET_HANDLE_IN_USE
;
2342 else if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2343 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2346 res
= HTTPREQ_Read(req
, (char*)buffers
->lpvBuffer
+read
, size
-read
,
2347 &buffers
->dwBufferLength
, !(flags
& IRF_NO_WAIT
));
2348 if(res
== ERROR_SUCCESS
)
2349 read
+= buffers
->dwBufferLength
;
2353 if(!req
->read_chunked
|| read
==size
|| req
->contentLength
!=req
->contentRead
2354 || !req
->contentLength
|| (req
->gzip_stream
&& req
->gzip_stream
->end_of_data
))
2356 LeaveCriticalSection( &req
->read_section
);
2358 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2359 &buffers
->dwBufferLength
, sizeof(buffers
->dwBufferLength
));
2360 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2361 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2363 EnterCriticalSection( &req
->read_section
);
2366 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2367 hdr
->dwError
= ERROR_SUCCESS
;
2369 error
= hdr
->dwError
;
2371 LeaveCriticalSection( &req
->read_section
);
2372 size
= buffers
->dwBufferLength
;
2373 buffers
->dwBufferLength
= read
;
2376 if (res
== ERROR_SUCCESS
) {
2377 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2378 &size
, sizeof(size
));
2381 return res
==ERROR_SUCCESS
? error
: res
;
2384 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST
*workRequest
)
2386 struct WORKREQ_INTERNETREADFILEEXW
const *data
= &workRequest
->u
.InternetReadFileExW
;
2387 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2388 INTERNET_ASYNC_RESULT iar
;
2391 TRACE("INTERNETREADFILEEXW %p\n", workRequest
->hdr
);
2393 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2394 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2396 iar
.dwResult
= res
== ERROR_SUCCESS
;
2399 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2400 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2401 sizeof(INTERNET_ASYNC_RESULT
));
2404 static DWORD
HTTPREQ_ReadFileExW(object_header_t
*hdr
, INTERNET_BUFFERSW
*buffers
,
2405 DWORD flags
, DWORD_PTR context
)
2408 http_request_t
*req
= (http_request_t
*)hdr
;
2409 DWORD res
, size
, read
, error
= ERROR_SUCCESS
;
2411 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2412 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2414 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2415 return ERROR_INVALID_PARAMETER
;
2417 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2419 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2421 WORKREQUEST workRequest
;
2423 if (TryEnterCriticalSection( &req
->read_section
))
2425 if (get_avail_data(req
))
2427 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2428 &buffers
->dwBufferLength
, FALSE
);
2429 size
= buffers
->dwBufferLength
;
2430 LeaveCriticalSection( &req
->read_section
);
2433 LeaveCriticalSection( &req
->read_section
);
2436 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExWProc
;
2437 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2438 workRequest
.u
.InternetReadFileExW
.lpBuffersOut
= buffers
;
2440 INTERNET_AsyncCall(&workRequest
);
2442 return ERROR_IO_PENDING
;
2446 size
= buffers
->dwBufferLength
;
2448 EnterCriticalSection( &req
->read_section
);
2449 if(hdr
->dwError
== ERROR_SUCCESS
)
2450 hdr
->dwError
= INTERNET_HANDLE_IN_USE
;
2451 else if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2452 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2455 res
= HTTPREQ_Read(req
, (char*)buffers
->lpvBuffer
+read
, size
-read
,
2456 &buffers
->dwBufferLength
, !(flags
& IRF_NO_WAIT
));
2457 if(res
== ERROR_SUCCESS
)
2458 read
+= buffers
->dwBufferLength
;
2462 if(!req
->read_chunked
|| read
==size
|| req
->contentLength
!=req
->contentRead
2463 || !req
->contentLength
|| (req
->gzip_stream
&& req
->gzip_stream
->end_of_data
))
2465 LeaveCriticalSection( &req
->read_section
);
2467 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2468 &buffers
->dwBufferLength
, sizeof(buffers
->dwBufferLength
));
2469 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2470 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2472 EnterCriticalSection( &req
->read_section
);
2475 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2476 hdr
->dwError
= ERROR_SUCCESS
;
2478 error
= hdr
->dwError
;
2480 LeaveCriticalSection( &req
->read_section
);
2481 size
= buffers
->dwBufferLength
;
2482 buffers
->dwBufferLength
= read
;
2485 if (res
== ERROR_SUCCESS
) {
2486 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2487 &size
, sizeof(size
));
2490 return res
==ERROR_SUCCESS
? error
: res
;
2493 static DWORD
HTTPREQ_WriteFile(object_header_t
*hdr
, const void *buffer
, DWORD size
, DWORD
*written
)
2496 http_request_t
*request
= (http_request_t
*)hdr
;
2498 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
2501 res
= NETCON_send(&request
->netConnection
, buffer
, size
, 0, (LPINT
)written
);
2502 if (res
== ERROR_SUCCESS
)
2503 request
->bytesWritten
+= *written
;
2505 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_SENT
, written
, sizeof(DWORD
));
2509 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST
*workRequest
)
2511 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2513 HTTP_ReceiveRequestData(req
, FALSE
);
2516 static DWORD
HTTPREQ_QueryDataAvailable(object_header_t
*hdr
, DWORD
*available
, DWORD flags
, DWORD_PTR ctx
)
2518 http_request_t
*req
= (http_request_t
*)hdr
;
2520 TRACE("(%p %p %x %lx)\n", req
, available
, flags
, ctx
);
2522 if (req
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
2524 WORKREQUEST workRequest
;
2526 /* never wait, if we can't enter the section we queue an async request right away */
2527 if (TryEnterCriticalSection( &req
->read_section
))
2529 if ((*available
= get_avail_data( req
))) goto done
;
2530 if (end_of_read_data( req
)) goto done
;
2531 LeaveCriticalSection( &req
->read_section
);
2534 workRequest
.asyncproc
= HTTPREQ_AsyncQueryDataAvailableProc
;
2535 workRequest
.hdr
= WININET_AddRef( &req
->hdr
);
2537 INTERNET_AsyncCall(&workRequest
);
2539 return ERROR_IO_PENDING
;
2542 EnterCriticalSection( &req
->read_section
);
2544 if (!(*available
= get_avail_data( req
)) && !end_of_read_data( req
))
2546 refill_buffer( req
);
2547 *available
= get_avail_data( req
);
2551 if (*available
== sizeof(req
->read_buf
) && !req
->gzip_stream
) /* check if we have even more pending in the socket */
2554 if (NETCON_query_data_available(&req
->netConnection
, &extra
))
2555 *available
= min( *available
+ extra
, req
->contentLength
- req
->contentRead
);
2557 LeaveCriticalSection( &req
->read_section
);
2559 TRACE( "returning %u\n", *available
);
2560 return ERROR_SUCCESS
;
2563 static const object_vtbl_t HTTPREQVtbl
= {
2565 HTTPREQ_CloseConnection
,
2566 HTTPREQ_QueryOption
,
2569 HTTPREQ_ReadFileExA
,
2570 HTTPREQ_ReadFileExW
,
2572 HTTPREQ_QueryDataAvailable
,
2576 /***********************************************************************
2577 * HTTP_HttpOpenRequestW (internal)
2579 * Open a HTTP request handle
2582 * HINTERNET a HTTP request handle on success
2586 static DWORD
HTTP_HttpOpenRequestW(http_session_t
*session
,
2587 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
2588 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
2589 DWORD dwFlags
, DWORD_PTR dwContext
, HINTERNET
*ret
)
2591 appinfo_t
*hIC
= NULL
;
2592 http_request_t
*request
;
2593 LPWSTR lpszHostName
= NULL
;
2594 static const WCHAR szHostForm
[] = {'%','s',':','%','u',0};
2599 assert( session
->hdr
.htype
== WH_HHTTPSESSION
);
2600 hIC
= session
->appInfo
;
2602 request
= alloc_object(&session
->hdr
, &HTTPREQVtbl
, sizeof(http_request_t
));
2604 return ERROR_OUTOFMEMORY
;
2606 request
->hdr
.htype
= WH_HHTTPREQ
;
2607 request
->hdr
.dwFlags
= dwFlags
;
2608 request
->hdr
.dwContext
= dwContext
;
2609 request
->contentLength
= ~0u;
2611 InitializeCriticalSection( &request
->read_section
);
2613 WININET_AddRef( &session
->hdr
);
2614 request
->session
= session
;
2615 list_add_head( &session
->hdr
.children
, &request
->hdr
.entry
);
2617 lpszHostName
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
) *
2618 (strlenW(session
->hostName
) + 7 /* length of ":65535" + 1 */));
2619 if (NULL
== lpszHostName
)
2621 res
= ERROR_OUTOFMEMORY
;
2625 if ((res
= NETCON_init(&request
->netConnection
, dwFlags
& INTERNET_FLAG_SECURE
)) != ERROR_SUCCESS
)
2628 if (lpszObjectName
&& *lpszObjectName
) {
2632 rc
= UrlEscapeW(lpszObjectName
, NULL
, &len
, URL_ESCAPE_SPACES_ONLY
);
2633 if (rc
!= E_POINTER
)
2634 len
= strlenW(lpszObjectName
)+1;
2635 request
->path
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
2636 rc
= UrlEscapeW(lpszObjectName
, request
->path
, &len
,
2637 URL_ESCAPE_SPACES_ONLY
);
2640 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName
),rc
);
2641 strcpyW(request
->path
,lpszObjectName
);
2644 static const WCHAR slashW
[] = {'/',0};
2646 request
->path
= heap_strdupW(slashW
);
2649 if (lpszReferrer
&& *lpszReferrer
)
2650 HTTP_ProcessHeader(request
, HTTP_REFERER
, lpszReferrer
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2652 if (lpszAcceptTypes
)
2655 for (i
= 0; lpszAcceptTypes
[i
]; i
++)
2657 if (!*lpszAcceptTypes
[i
]) continue;
2658 HTTP_ProcessHeader(request
, HTTP_ACCEPT
, lpszAcceptTypes
[i
],
2659 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
|
2660 HTTP_ADDHDR_FLAG_REQ
|
2661 (i
== 0 ? HTTP_ADDHDR_FLAG_REPLACE
: 0));
2665 request
->verb
= heap_strdupW(lpszVerb
&& *lpszVerb
? lpszVerb
: szGET
);
2666 request
->version
= heap_strdupW(lpszVersion
? lpszVersion
: g_szHttp1_1
);
2668 if (session
->hostPort
!= INTERNET_INVALID_PORT_NUMBER
&&
2669 session
->hostPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
2670 session
->hostPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
2672 sprintfW(lpszHostName
, szHostForm
, session
->hostName
, session
->hostPort
);
2673 HTTP_ProcessHeader(request
, hostW
, lpszHostName
,
2674 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2677 HTTP_ProcessHeader(request
, hostW
, session
->hostName
,
2678 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2680 if (session
->serverPort
== INTERNET_INVALID_PORT_NUMBER
)
2681 session
->serverPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
2682 INTERNET_DEFAULT_HTTPS_PORT
:
2683 INTERNET_DEFAULT_HTTP_PORT
);
2685 if (session
->hostPort
== INTERNET_INVALID_PORT_NUMBER
)
2686 session
->hostPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
2687 INTERNET_DEFAULT_HTTPS_PORT
:
2688 INTERNET_DEFAULT_HTTP_PORT
);
2690 if (NULL
!= hIC
->proxy
&& hIC
->proxy
[0] != 0)
2691 HTTP_DealWithProxy( hIC
, session
, request
);
2693 INTERNET_SendCallback(&session
->hdr
, dwContext
,
2694 INTERNET_STATUS_HANDLE_CREATED
, &request
->hdr
.hInternet
,
2698 TRACE("<-- %u (%p)\n", res
, request
);
2700 HeapFree(GetProcessHeap(), 0, lpszHostName
);
2701 if(res
!= ERROR_SUCCESS
) {
2702 WININET_Release( &request
->hdr
);
2707 *ret
= request
->hdr
.hInternet
;
2708 return ERROR_SUCCESS
;
2711 /***********************************************************************
2712 * HttpOpenRequestW (WININET.@)
2714 * Open a HTTP request handle
2717 * HINTERNET a HTTP request handle on success
2721 HINTERNET WINAPI
HttpOpenRequestW(HINTERNET hHttpSession
,
2722 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
2723 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
2724 DWORD dwFlags
, DWORD_PTR dwContext
)
2726 http_session_t
*session
;
2727 HINTERNET handle
= NULL
;
2730 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
2731 debugstr_w(lpszVerb
), debugstr_w(lpszObjectName
),
2732 debugstr_w(lpszVersion
), debugstr_w(lpszReferrer
), lpszAcceptTypes
,
2733 dwFlags
, dwContext
);
2734 if(lpszAcceptTypes
!=NULL
)
2737 for(i
=0;lpszAcceptTypes
[i
]!=NULL
;i
++)
2738 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes
[i
]));
2741 session
= (http_session_t
*) get_handle_object( hHttpSession
);
2742 if (NULL
== session
|| session
->hdr
.htype
!= WH_HHTTPSESSION
)
2744 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
2749 * My tests seem to show that the windows version does not
2750 * become asynchronous until after this point. And anyhow
2751 * if this call was asynchronous then how would you get the
2752 * necessary HINTERNET pointer returned by this function.
2755 res
= HTTP_HttpOpenRequestW(session
, lpszVerb
, lpszObjectName
,
2756 lpszVersion
, lpszReferrer
, lpszAcceptTypes
,
2757 dwFlags
, dwContext
, &handle
);
2760 WININET_Release( &session
->hdr
);
2761 TRACE("returning %p\n", handle
);
2762 if(res
!= ERROR_SUCCESS
)
2767 /* read any content returned by the server so that the connection can be
2769 static void HTTP_DrainContent(http_request_t
*req
)
2773 if (!NETCON_connected(&req
->netConnection
)) return;
2775 if (req
->contentLength
== -1)
2777 NETCON_close(&req
->netConnection
);
2780 if (!strcmpW(req
->verb
, szHEAD
)) return;
2785 if (HTTPREQ_Read(req
, buffer
, sizeof(buffer
), &bytes_read
, TRUE
) != ERROR_SUCCESS
)
2787 } while (bytes_read
);
2790 static const LPCWSTR header_lookup
[] = {
2791 szMime_Version
, /* HTTP_QUERY_MIME_VERSION = 0 */
2792 szContent_Type
, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2793 szContent_Transfer_Encoding
,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2794 szContent_ID
, /* HTTP_QUERY_CONTENT_ID = 3 */
2795 NULL
, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2796 szContent_Length
, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2797 szContent_Language
, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2798 szAllow
, /* HTTP_QUERY_ALLOW = 7 */
2799 szPublic
, /* HTTP_QUERY_PUBLIC = 8 */
2800 szDate
, /* HTTP_QUERY_DATE = 9 */
2801 szExpires
, /* HTTP_QUERY_EXPIRES = 10 */
2802 szLast_Modified
, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2803 NULL
, /* HTTP_QUERY_MESSAGE_ID = 12 */
2804 szURI
, /* HTTP_QUERY_URI = 13 */
2805 szFrom
, /* HTTP_QUERY_DERIVED_FROM = 14 */
2806 NULL
, /* HTTP_QUERY_COST = 15 */
2807 NULL
, /* HTTP_QUERY_LINK = 16 */
2808 szPragma
, /* HTTP_QUERY_PRAGMA = 17 */
2809 NULL
, /* HTTP_QUERY_VERSION = 18 */
2810 szStatus
, /* HTTP_QUERY_STATUS_CODE = 19 */
2811 NULL
, /* HTTP_QUERY_STATUS_TEXT = 20 */
2812 NULL
, /* HTTP_QUERY_RAW_HEADERS = 21 */
2813 NULL
, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2814 szConnection
, /* HTTP_QUERY_CONNECTION = 23 */
2815 szAccept
, /* HTTP_QUERY_ACCEPT = 24 */
2816 szAccept_Charset
, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2817 szAccept_Encoding
, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2818 szAccept_Language
, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2819 szAuthorization
, /* HTTP_QUERY_AUTHORIZATION = 28 */
2820 szContent_Encoding
, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2821 NULL
, /* HTTP_QUERY_FORWARDED = 30 */
2822 NULL
, /* HTTP_QUERY_FROM = 31 */
2823 szIf_Modified_Since
, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2824 szLocation
, /* HTTP_QUERY_LOCATION = 33 */
2825 NULL
, /* HTTP_QUERY_ORIG_URI = 34 */
2826 szReferer
, /* HTTP_QUERY_REFERER = 35 */
2827 szRetry_After
, /* HTTP_QUERY_RETRY_AFTER = 36 */
2828 szServer
, /* HTTP_QUERY_SERVER = 37 */
2829 NULL
, /* HTTP_TITLE = 38 */
2830 szUser_Agent
, /* HTTP_QUERY_USER_AGENT = 39 */
2831 szWWW_Authenticate
, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2832 szProxy_Authenticate
, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2833 szAccept_Ranges
, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2834 szSet_Cookie
, /* HTTP_QUERY_SET_COOKIE = 43 */
2835 szCookie
, /* HTTP_QUERY_COOKIE = 44 */
2836 NULL
, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2837 NULL
, /* HTTP_QUERY_REFRESH = 46 */
2838 NULL
, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2839 szAge
, /* HTTP_QUERY_AGE = 48 */
2840 szCache_Control
, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2841 szContent_Base
, /* HTTP_QUERY_CONTENT_BASE = 50 */
2842 szContent_Location
, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2843 szContent_MD5
, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2844 szContent_Range
, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2845 szETag
, /* HTTP_QUERY_ETAG = 54 */
2846 hostW
, /* HTTP_QUERY_HOST = 55 */
2847 szIf_Match
, /* HTTP_QUERY_IF_MATCH = 56 */
2848 szIf_None_Match
, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2849 szIf_Range
, /* HTTP_QUERY_IF_RANGE = 58 */
2850 szIf_Unmodified_Since
, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2851 szMax_Forwards
, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2852 szProxy_Authorization
, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2853 szRange
, /* HTTP_QUERY_RANGE = 62 */
2854 szTransfer_Encoding
, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2855 szUpgrade
, /* HTTP_QUERY_UPGRADE = 64 */
2856 szVary
, /* HTTP_QUERY_VARY = 65 */
2857 szVia
, /* HTTP_QUERY_VIA = 66 */
2858 szWarning
, /* HTTP_QUERY_WARNING = 67 */
2859 szExpect
, /* HTTP_QUERY_EXPECT = 68 */
2860 szProxy_Connection
, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2861 szUnless_Modified_Since
, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2864 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2866 /***********************************************************************
2867 * HTTP_HttpQueryInfoW (internal)
2869 static DWORD
HTTP_HttpQueryInfoW(http_request_t
*request
, DWORD dwInfoLevel
,
2870 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
2872 LPHTTPHEADERW lphttpHdr
= NULL
;
2873 BOOL request_only
= dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
;
2874 INT requested_index
= lpdwIndex
? *lpdwIndex
: 0;
2875 DWORD level
= (dwInfoLevel
& ~HTTP_QUERY_MODIFIER_FLAGS_MASK
);
2878 /* Find requested header structure */
2881 case HTTP_QUERY_CUSTOM
:
2882 if (!lpBuffer
) return ERROR_INVALID_PARAMETER
;
2883 index
= HTTP_GetCustomHeaderIndex(request
, lpBuffer
, requested_index
, request_only
);
2885 case HTTP_QUERY_RAW_HEADERS_CRLF
:
2889 DWORD res
= ERROR_INVALID_PARAMETER
;
2892 headers
= HTTP_BuildHeaderRequestString(request
, request
->verb
, request
->path
, request
->version
);
2894 headers
= request
->rawHeaders
;
2897 len
= strlenW(headers
) * sizeof(WCHAR
);
2899 if (len
+ sizeof(WCHAR
) > *lpdwBufferLength
)
2901 len
+= sizeof(WCHAR
);
2902 res
= ERROR_INSUFFICIENT_BUFFER
;
2907 memcpy(lpBuffer
, headers
, len
+ sizeof(WCHAR
));
2910 len
= strlenW(szCrLf
) * sizeof(WCHAR
);
2911 memcpy(lpBuffer
, szCrLf
, sizeof(szCrLf
));
2913 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
/ sizeof(WCHAR
)));
2914 res
= ERROR_SUCCESS
;
2916 *lpdwBufferLength
= len
;
2919 HeapFree(GetProcessHeap(), 0, headers
);
2922 case HTTP_QUERY_RAW_HEADERS
:
2924 LPWSTR
* ppszRawHeaderLines
= HTTP_Tokenize(request
->rawHeaders
, szCrLf
);
2926 LPWSTR pszString
= lpBuffer
;
2928 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
2929 size
+= strlenW(ppszRawHeaderLines
[i
]) + 1;
2931 if (size
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
2933 HTTP_FreeTokens(ppszRawHeaderLines
);
2934 *lpdwBufferLength
= (size
+ 1) * sizeof(WCHAR
);
2935 return ERROR_INSUFFICIENT_BUFFER
;
2939 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
2941 DWORD len
= strlenW(ppszRawHeaderLines
[i
]);
2942 memcpy(pszString
, ppszRawHeaderLines
[i
], (len
+1)*sizeof(WCHAR
));
2946 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, size
));
2948 *lpdwBufferLength
= size
* sizeof(WCHAR
);
2949 HTTP_FreeTokens(ppszRawHeaderLines
);
2951 return ERROR_SUCCESS
;
2953 case HTTP_QUERY_STATUS_TEXT
:
2954 if (request
->statusText
)
2956 DWORD len
= strlenW(request
->statusText
);
2957 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
2959 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
2960 return ERROR_INSUFFICIENT_BUFFER
;
2964 memcpy(lpBuffer
, request
->statusText
, (len
+ 1) * sizeof(WCHAR
));
2965 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
2967 *lpdwBufferLength
= len
* sizeof(WCHAR
);
2968 return ERROR_SUCCESS
;
2971 case HTTP_QUERY_VERSION
:
2972 if (request
->version
)
2974 DWORD len
= strlenW(request
->version
);
2975 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
2977 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
2978 return ERROR_INSUFFICIENT_BUFFER
;
2982 memcpy(lpBuffer
, request
->version
, (len
+ 1) * sizeof(WCHAR
));
2983 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
2985 *lpdwBufferLength
= len
* sizeof(WCHAR
);
2986 return ERROR_SUCCESS
;
2989 case HTTP_QUERY_CONTENT_ENCODING
:
2990 index
= HTTP_GetCustomHeaderIndex(request
, header_lookup
[request
->gzip_stream
? HTTP_QUERY_CONTENT_TYPE
: level
],
2991 requested_index
,request_only
);
2994 assert (LAST_TABLE_HEADER
== (HTTP_QUERY_UNLESS_MODIFIED_SINCE
+ 1));
2996 if (level
< LAST_TABLE_HEADER
&& header_lookup
[level
])
2997 index
= HTTP_GetCustomHeaderIndex(request
, header_lookup
[level
],
2998 requested_index
,request_only
);
3002 lphttpHdr
= &request
->custHeaders
[index
];
3004 /* Ensure header satisfies requested attributes */
3006 ((dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
) &&
3007 (~lphttpHdr
->wFlags
& HDR_ISREQUEST
)))
3009 return ERROR_HTTP_HEADER_NOT_FOUND
;
3012 if (lpdwIndex
&& level
!= HTTP_QUERY_STATUS_CODE
) (*lpdwIndex
)++;
3014 /* coalesce value to requested type */
3015 if (dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
&& lpBuffer
)
3017 *(int *)lpBuffer
= atoiW(lphttpHdr
->lpszValue
);
3018 TRACE(" returning number: %d\n", *(int *)lpBuffer
);
3020 else if (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
&& lpBuffer
)
3026 tmpTime
= ConvertTimeString(lphttpHdr
->lpszValue
);
3028 tmpTM
= *gmtime(&tmpTime
);
3029 STHook
= (SYSTEMTIME
*)lpBuffer
;
3030 STHook
->wDay
= tmpTM
.tm_mday
;
3031 STHook
->wHour
= tmpTM
.tm_hour
;
3032 STHook
->wMilliseconds
= 0;
3033 STHook
->wMinute
= tmpTM
.tm_min
;
3034 STHook
->wDayOfWeek
= tmpTM
.tm_wday
;
3035 STHook
->wMonth
= tmpTM
.tm_mon
+ 1;
3036 STHook
->wSecond
= tmpTM
.tm_sec
;
3037 STHook
->wYear
= tmpTM
.tm_year
;
3039 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3040 STHook
->wYear
, STHook
->wMonth
, STHook
->wDay
, STHook
->wDayOfWeek
,
3041 STHook
->wHour
, STHook
->wMinute
, STHook
->wSecond
, STHook
->wMilliseconds
);
3043 else if (lphttpHdr
->lpszValue
)
3045 DWORD len
= (strlenW(lphttpHdr
->lpszValue
) + 1) * sizeof(WCHAR
);
3047 if (len
> *lpdwBufferLength
)
3049 *lpdwBufferLength
= len
;
3050 return ERROR_INSUFFICIENT_BUFFER
;
3054 memcpy(lpBuffer
, lphttpHdr
->lpszValue
, len
);
3055 TRACE("! returning string: %s\n", debugstr_w(lpBuffer
));
3057 *lpdwBufferLength
= len
- sizeof(WCHAR
);
3059 return ERROR_SUCCESS
;
3062 /***********************************************************************
3063 * HttpQueryInfoW (WININET.@)
3065 * Queries for information about an HTTP request
3072 BOOL WINAPI
HttpQueryInfoW(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3073 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3075 http_request_t
*request
;
3078 if (TRACE_ON(wininet
)) {
3079 #define FE(x) { x, #x }
3080 static const wininet_flag_info query_flags
[] = {
3081 FE(HTTP_QUERY_MIME_VERSION
),
3082 FE(HTTP_QUERY_CONTENT_TYPE
),
3083 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING
),
3084 FE(HTTP_QUERY_CONTENT_ID
),
3085 FE(HTTP_QUERY_CONTENT_DESCRIPTION
),
3086 FE(HTTP_QUERY_CONTENT_LENGTH
),
3087 FE(HTTP_QUERY_CONTENT_LANGUAGE
),
3088 FE(HTTP_QUERY_ALLOW
),
3089 FE(HTTP_QUERY_PUBLIC
),
3090 FE(HTTP_QUERY_DATE
),
3091 FE(HTTP_QUERY_EXPIRES
),
3092 FE(HTTP_QUERY_LAST_MODIFIED
),
3093 FE(HTTP_QUERY_MESSAGE_ID
),
3095 FE(HTTP_QUERY_DERIVED_FROM
),
3096 FE(HTTP_QUERY_COST
),
3097 FE(HTTP_QUERY_LINK
),
3098 FE(HTTP_QUERY_PRAGMA
),
3099 FE(HTTP_QUERY_VERSION
),
3100 FE(HTTP_QUERY_STATUS_CODE
),
3101 FE(HTTP_QUERY_STATUS_TEXT
),
3102 FE(HTTP_QUERY_RAW_HEADERS
),
3103 FE(HTTP_QUERY_RAW_HEADERS_CRLF
),
3104 FE(HTTP_QUERY_CONNECTION
),
3105 FE(HTTP_QUERY_ACCEPT
),
3106 FE(HTTP_QUERY_ACCEPT_CHARSET
),
3107 FE(HTTP_QUERY_ACCEPT_ENCODING
),
3108 FE(HTTP_QUERY_ACCEPT_LANGUAGE
),
3109 FE(HTTP_QUERY_AUTHORIZATION
),
3110 FE(HTTP_QUERY_CONTENT_ENCODING
),
3111 FE(HTTP_QUERY_FORWARDED
),
3112 FE(HTTP_QUERY_FROM
),
3113 FE(HTTP_QUERY_IF_MODIFIED_SINCE
),
3114 FE(HTTP_QUERY_LOCATION
),
3115 FE(HTTP_QUERY_ORIG_URI
),
3116 FE(HTTP_QUERY_REFERER
),
3117 FE(HTTP_QUERY_RETRY_AFTER
),
3118 FE(HTTP_QUERY_SERVER
),
3119 FE(HTTP_QUERY_TITLE
),
3120 FE(HTTP_QUERY_USER_AGENT
),
3121 FE(HTTP_QUERY_WWW_AUTHENTICATE
),
3122 FE(HTTP_QUERY_PROXY_AUTHENTICATE
),
3123 FE(HTTP_QUERY_ACCEPT_RANGES
),
3124 FE(HTTP_QUERY_SET_COOKIE
),
3125 FE(HTTP_QUERY_COOKIE
),
3126 FE(HTTP_QUERY_REQUEST_METHOD
),
3127 FE(HTTP_QUERY_REFRESH
),
3128 FE(HTTP_QUERY_CONTENT_DISPOSITION
),
3130 FE(HTTP_QUERY_CACHE_CONTROL
),
3131 FE(HTTP_QUERY_CONTENT_BASE
),
3132 FE(HTTP_QUERY_CONTENT_LOCATION
),
3133 FE(HTTP_QUERY_CONTENT_MD5
),
3134 FE(HTTP_QUERY_CONTENT_RANGE
),
3135 FE(HTTP_QUERY_ETAG
),
3136 FE(HTTP_QUERY_HOST
),
3137 FE(HTTP_QUERY_IF_MATCH
),
3138 FE(HTTP_QUERY_IF_NONE_MATCH
),
3139 FE(HTTP_QUERY_IF_RANGE
),
3140 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE
),
3141 FE(HTTP_QUERY_MAX_FORWARDS
),
3142 FE(HTTP_QUERY_PROXY_AUTHORIZATION
),
3143 FE(HTTP_QUERY_RANGE
),
3144 FE(HTTP_QUERY_TRANSFER_ENCODING
),
3145 FE(HTTP_QUERY_UPGRADE
),
3146 FE(HTTP_QUERY_VARY
),
3148 FE(HTTP_QUERY_WARNING
),
3149 FE(HTTP_QUERY_CUSTOM
)
3151 static const wininet_flag_info modifier_flags
[] = {
3152 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS
),
3153 FE(HTTP_QUERY_FLAG_SYSTEMTIME
),
3154 FE(HTTP_QUERY_FLAG_NUMBER
),
3155 FE(HTTP_QUERY_FLAG_COALESCE
)
3158 DWORD info_mod
= dwInfoLevel
& HTTP_QUERY_MODIFIER_FLAGS_MASK
;
3159 DWORD info
= dwInfoLevel
& HTTP_QUERY_HEADER_MASK
;
3162 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest
, dwInfoLevel
, info
);
3163 TRACE(" Attribute:");
3164 for (i
= 0; i
< (sizeof(query_flags
) / sizeof(query_flags
[0])); i
++) {
3165 if (query_flags
[i
].val
== info
) {
3166 TRACE(" %s", query_flags
[i
].name
);
3170 if (i
== (sizeof(query_flags
) / sizeof(query_flags
[0]))) {
3171 TRACE(" Unknown (%08x)", info
);
3174 TRACE(" Modifier:");
3175 for (i
= 0; i
< (sizeof(modifier_flags
) / sizeof(modifier_flags
[0])); i
++) {
3176 if (modifier_flags
[i
].val
& info_mod
) {
3177 TRACE(" %s", modifier_flags
[i
].name
);
3178 info_mod
&= ~ modifier_flags
[i
].val
;
3183 TRACE(" Unknown (%08x)", info_mod
);
3188 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
3189 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
3191 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
3195 if (lpBuffer
== NULL
)
3196 *lpdwBufferLength
= 0;
3197 res
= HTTP_HttpQueryInfoW( request
, dwInfoLevel
,
3198 lpBuffer
, lpdwBufferLength
, lpdwIndex
);
3202 WININET_Release( &request
->hdr
);
3204 TRACE("%u <--\n", res
);
3205 if(res
!= ERROR_SUCCESS
)
3207 return res
== ERROR_SUCCESS
;
3210 /***********************************************************************
3211 * HttpQueryInfoA (WININET.@)
3213 * Queries for information about an HTTP request
3220 BOOL WINAPI
HttpQueryInfoA(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3221 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3227 if((dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
) ||
3228 (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
))
3230 return HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, lpBuffer
,
3231 lpdwBufferLength
, lpdwIndex
);
3237 len
= (*lpdwBufferLength
)*sizeof(WCHAR
);
3238 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3240 alloclen
= MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, NULL
, 0 ) * sizeof(WCHAR
);
3246 bufferW
= HeapAlloc( GetProcessHeap(), 0, alloclen
);
3247 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3248 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3249 MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, bufferW
, alloclen
/ sizeof(WCHAR
) );
3256 result
= HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, bufferW
,
3260 len
= WideCharToMultiByte( CP_ACP
,0, bufferW
, len
/ sizeof(WCHAR
) + 1,
3261 lpBuffer
, *lpdwBufferLength
, NULL
, NULL
);
3262 *lpdwBufferLength
= len
- 1;
3264 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer
));
3267 /* since the strings being returned from HttpQueryInfoW should be
3268 * only ASCII characters, it is reasonable to assume that all of
3269 * the Unicode characters can be reduced to a single byte */
3270 *lpdwBufferLength
= len
/ sizeof(WCHAR
);
3272 HeapFree(GetProcessHeap(), 0, bufferW
);
3277 /***********************************************************************
3278 * HTTP_GetRedirectURL (internal)
3280 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*request
, LPCWSTR lpszUrl
)
3282 static WCHAR szHttp
[] = {'h','t','t','p',0};
3283 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3284 http_session_t
*session
= request
->session
;
3285 URL_COMPONENTSW urlComponents
;
3286 DWORD url_length
= 0;
3288 LPWSTR combined_url
;
3290 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3291 urlComponents
.lpszScheme
= (request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) ? szHttps
: szHttp
;
3292 urlComponents
.dwSchemeLength
= 0;
3293 urlComponents
.lpszHostName
= session
->hostName
;
3294 urlComponents
.dwHostNameLength
= 0;
3295 urlComponents
.nPort
= session
->hostPort
;
3296 urlComponents
.lpszUserName
= session
->userName
;
3297 urlComponents
.dwUserNameLength
= 0;
3298 urlComponents
.lpszPassword
= NULL
;
3299 urlComponents
.dwPasswordLength
= 0;
3300 urlComponents
.lpszUrlPath
= request
->path
;
3301 urlComponents
.dwUrlPathLength
= 0;
3302 urlComponents
.lpszExtraInfo
= NULL
;
3303 urlComponents
.dwExtraInfoLength
= 0;
3305 if (!InternetCreateUrlW(&urlComponents
, 0, NULL
, &url_length
) &&
3306 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3309 orig_url
= HeapAlloc(GetProcessHeap(), 0, url_length
);
3311 /* convert from bytes to characters */
3312 url_length
= url_length
/ sizeof(WCHAR
) - 1;
3313 if (!InternetCreateUrlW(&urlComponents
, 0, orig_url
, &url_length
))
3315 HeapFree(GetProcessHeap(), 0, orig_url
);
3320 if (!InternetCombineUrlW(orig_url
, lpszUrl
, NULL
, &url_length
, ICU_ENCODE_SPACES_ONLY
) &&
3321 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3323 HeapFree(GetProcessHeap(), 0, orig_url
);
3326 combined_url
= HeapAlloc(GetProcessHeap(), 0, url_length
* sizeof(WCHAR
));
3328 if (!InternetCombineUrlW(orig_url
, lpszUrl
, combined_url
, &url_length
, ICU_ENCODE_SPACES_ONLY
))
3330 HeapFree(GetProcessHeap(), 0, orig_url
);
3331 HeapFree(GetProcessHeap(), 0, combined_url
);
3334 HeapFree(GetProcessHeap(), 0, orig_url
);
3335 return combined_url
;
3339 /***********************************************************************
3340 * HTTP_HandleRedirect (internal)
3342 static DWORD
HTTP_HandleRedirect(http_request_t
*request
, LPCWSTR lpszUrl
)
3344 http_session_t
*session
= request
->session
;
3345 appinfo_t
*hIC
= session
->appInfo
;
3346 BOOL using_proxy
= hIC
->proxy
&& hIC
->proxy
[0];
3347 WCHAR path
[INTERNET_MAX_URL_LENGTH
];
3352 /* if it's an absolute path, keep the same session info */
3353 lstrcpynW(path
, lpszUrl
, INTERNET_MAX_URL_LENGTH
);
3357 URL_COMPONENTSW urlComponents
;
3358 WCHAR protocol
[32], hostName
[MAXHOSTNAME
], userName
[1024];
3359 static WCHAR szHttp
[] = {'h','t','t','p',0};
3360 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3366 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3367 urlComponents
.lpszScheme
= protocol
;
3368 urlComponents
.dwSchemeLength
= 32;
3369 urlComponents
.lpszHostName
= hostName
;
3370 urlComponents
.dwHostNameLength
= MAXHOSTNAME
;
3371 urlComponents
.lpszUserName
= userName
;
3372 urlComponents
.dwUserNameLength
= 1024;
3373 urlComponents
.lpszPassword
= NULL
;
3374 urlComponents
.dwPasswordLength
= 0;
3375 urlComponents
.lpszUrlPath
= path
;
3376 urlComponents
.dwUrlPathLength
= 2048;
3377 urlComponents
.lpszExtraInfo
= NULL
;
3378 urlComponents
.dwExtraInfoLength
= 0;
3379 if(!InternetCrackUrlW(lpszUrl
, strlenW(lpszUrl
), 0, &urlComponents
))
3380 return INTERNET_GetLastError();
3382 if (!strncmpW(szHttp
, urlComponents
.lpszScheme
, strlenW(szHttp
)) &&
3383 (request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
))
3385 TRACE("redirect from secure page to non-secure page\n");
3386 /* FIXME: warn about from secure redirect to non-secure page */
3387 request
->hdr
.dwFlags
&= ~INTERNET_FLAG_SECURE
;
3389 if (!strncmpW(szHttps
, urlComponents
.lpszScheme
, strlenW(szHttps
)) &&
3390 !(request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
))
3392 TRACE("redirect from non-secure page to secure page\n");
3393 /* FIXME: notify about redirect to secure page */
3394 request
->hdr
.dwFlags
|= INTERNET_FLAG_SECURE
;
3397 if (urlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
3399 if (lstrlenW(protocol
)>4) /*https*/
3400 urlComponents
.nPort
= INTERNET_DEFAULT_HTTPS_PORT
;
3402 urlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
3407 * This upsets redirects to binary files on sourceforge.net
3408 * and gives an html page instead of the target file
3409 * Examination of the HTTP request sent by native wininet.dll
3410 * reveals that it doesn't send a referrer in that case.
3411 * Maybe there's a flag that enables this, or maybe a referrer
3412 * shouldn't be added in case of a redirect.
3415 /* consider the current host as the referrer */
3416 if (session
->lpszServerName
&& *session
->lpszServerName
)
3417 HTTP_ProcessHeader(request
, HTTP_REFERER
, session
->lpszServerName
,
3418 HTTP_ADDHDR_FLAG_REQ
|HTTP_ADDREQ_FLAG_REPLACE
|
3419 HTTP_ADDHDR_FLAG_ADD_IF_NEW
);
3422 HeapFree(GetProcessHeap(), 0, session
->hostName
);
3423 if (urlComponents
.nPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
3424 urlComponents
.nPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
3427 static const WCHAR fmt
[] = {'%','s',':','%','i',0};
3428 len
= lstrlenW(hostName
);
3429 len
+= 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3430 session
->hostName
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
3431 sprintfW(session
->hostName
, fmt
, hostName
, urlComponents
.nPort
);
3434 session
->hostName
= heap_strdupW(hostName
);
3436 HTTP_ProcessHeader(request
, hostW
, session
->hostName
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDREQ_FLAG_REPLACE
| HTTP_ADDHDR_FLAG_REQ
);
3438 HeapFree(GetProcessHeap(), 0, session
->userName
);
3439 session
->userName
= NULL
;
3441 session
->userName
= heap_strdupW(userName
);
3445 if (strcmpiW(session
->serverName
, hostName
) || session
->serverPort
!= urlComponents
.nPort
)
3449 HeapFree(GetProcessHeap(), 0, session
->serverName
);
3450 session
->serverName
= heap_strdupW(hostName
);
3451 session
->serverPort
= urlComponents
.nPort
;
3453 NETCON_close(&request
->netConnection
);
3454 if ((res
= HTTP_ResolveName(request
)) != ERROR_SUCCESS
)
3457 res
= NETCON_init(&request
->netConnection
, request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
);
3458 if (res
!= ERROR_SUCCESS
)
3461 request
->read_pos
= request
->read_size
= 0;
3462 request
->read_chunked
= FALSE
;
3466 TRACE("Redirect through proxy\n");
3469 HeapFree(GetProcessHeap(), 0, request
->path
);
3476 rc
= UrlEscapeW(path
, NULL
, &needed
, URL_ESCAPE_SPACES_ONLY
);
3477 if (rc
!= E_POINTER
)
3478 needed
= strlenW(path
)+1;
3479 request
->path
= HeapAlloc(GetProcessHeap(), 0, needed
*sizeof(WCHAR
));
3480 rc
= UrlEscapeW(path
, request
->path
, &needed
,
3481 URL_ESCAPE_SPACES_ONLY
);
3484 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path
),rc
);
3485 strcpyW(request
->path
,path
);
3489 /* Remove custom content-type/length headers on redirects. */
3490 index
= HTTP_GetCustomHeaderIndex(request
, szContent_Type
, 0, TRUE
);
3492 HTTP_DeleteCustomHeader(request
, index
);
3493 index
= HTTP_GetCustomHeaderIndex(request
, szContent_Length
, 0, TRUE
);
3495 HTTP_DeleteCustomHeader(request
, index
);
3497 return ERROR_SUCCESS
;
3500 /***********************************************************************
3501 * HTTP_build_req (internal)
3503 * concatenate all the strings in the request together
3505 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
)
3510 for( t
= list
; *t
; t
++ )
3511 len
+= strlenW( *t
);
3514 str
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
3517 for( t
= list
; *t
; t
++ )
3523 static DWORD
HTTP_SecureProxyConnect(http_request_t
*request
)
3526 LPWSTR requestString
;
3532 static const WCHAR szConnect
[] = {'C','O','N','N','E','C','T',0};
3533 static const WCHAR szFormat
[] = {'%','s',':','%','d',0};
3534 http_session_t
*session
= request
->session
;
3538 lpszPath
= HeapAlloc( GetProcessHeap(), 0, (lstrlenW( session
->hostName
) + 13)*sizeof(WCHAR
) );
3539 sprintfW( lpszPath
, szFormat
, session
->hostName
, session
->hostPort
);
3540 requestString
= HTTP_BuildHeaderRequestString( request
, szConnect
, lpszPath
, g_szHttp1_1
);
3541 HeapFree( GetProcessHeap(), 0, lpszPath
);
3543 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3544 NULL
, 0, NULL
, NULL
);
3545 len
--; /* the nul terminator isn't needed */
3546 ascii_req
= HeapAlloc( GetProcessHeap(), 0, len
);
3547 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3548 ascii_req
, len
, NULL
, NULL
);
3549 HeapFree( GetProcessHeap(), 0, requestString
);
3551 TRACE("full request -> %s\n", debugstr_an( ascii_req
, len
) );
3553 res
= NETCON_send( &request
->netConnection
, ascii_req
, len
, 0, &cnt
);
3554 HeapFree( GetProcessHeap(), 0, ascii_req
);
3555 if (res
!= ERROR_SUCCESS
)
3558 responseLen
= HTTP_GetResponseHeaders( request
, TRUE
);
3560 return ERROR_HTTP_INVALID_HEADER
;
3562 return ERROR_SUCCESS
;
3565 static void HTTP_InsertCookies(http_request_t
*request
)
3567 static const WCHAR szUrlForm
[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3568 LPWSTR lpszCookies
, lpszUrl
= NULL
;
3569 DWORD nCookieSize
, size
;
3570 LPHTTPHEADERW Host
= HTTP_GetHeader(request
, hostW
);
3572 size
= (strlenW(Host
->lpszValue
) + strlenW(szUrlForm
) + strlenW(request
->path
)) * sizeof(WCHAR
);
3573 if (!(lpszUrl
= HeapAlloc(GetProcessHeap(), 0, size
))) return;
3574 sprintfW( lpszUrl
, szUrlForm
, Host
->lpszValue
, request
->path
);
3576 if (InternetGetCookieW(lpszUrl
, NULL
, NULL
, &nCookieSize
))
3579 static const WCHAR szCookie
[] = {'C','o','o','k','i','e',':',' ',0};
3581 size
= sizeof(szCookie
) + nCookieSize
* sizeof(WCHAR
) + sizeof(szCrLf
);
3582 if ((lpszCookies
= HeapAlloc(GetProcessHeap(), 0, size
)))
3584 cnt
+= sprintfW(lpszCookies
, szCookie
);
3585 InternetGetCookieW(lpszUrl
, NULL
, lpszCookies
+ cnt
, &nCookieSize
);
3586 strcatW(lpszCookies
, szCrLf
);
3588 HTTP_HttpAddRequestHeadersW(request
, lpszCookies
, strlenW(lpszCookies
), HTTP_ADDREQ_FLAG_REPLACE
);
3589 HeapFree(GetProcessHeap(), 0, lpszCookies
);
3592 HeapFree(GetProcessHeap(), 0, lpszUrl
);
3595 static WORD
HTTP_ParseDay(LPCWSTR day
)
3597 static const WCHAR days
[7][4] = {{ 's','u','n',0 },
3605 for (i
= 0; i
< sizeof(days
)/sizeof(*days
); i
++)
3606 if (!strcmpiW(day
, days
[i
]))
3613 static WORD
HTTP_ParseMonth(LPCWSTR month
)
3615 static const WCHAR jan
[] = { 'j','a','n',0 };
3616 static const WCHAR feb
[] = { 'f','e','b',0 };
3617 static const WCHAR mar
[] = { 'm','a','r',0 };
3618 static const WCHAR apr
[] = { 'a','p','r',0 };
3619 static const WCHAR may
[] = { 'm','a','y',0 };
3620 static const WCHAR jun
[] = { 'j','u','n',0 };
3621 static const WCHAR jul
[] = { 'j','u','l',0 };
3622 static const WCHAR aug
[] = { 'a','u','g',0 };
3623 static const WCHAR sep
[] = { 's','e','p',0 };
3624 static const WCHAR oct
[] = { 'o','c','t',0 };
3625 static const WCHAR nov
[] = { 'n','o','v',0 };
3626 static const WCHAR dec
[] = { 'd','e','c',0 };
3628 if (!strcmpiW(month
, jan
)) return 1;
3629 if (!strcmpiW(month
, feb
)) return 2;
3630 if (!strcmpiW(month
, mar
)) return 3;
3631 if (!strcmpiW(month
, apr
)) return 4;
3632 if (!strcmpiW(month
, may
)) return 5;
3633 if (!strcmpiW(month
, jun
)) return 6;
3634 if (!strcmpiW(month
, jul
)) return 7;
3635 if (!strcmpiW(month
, aug
)) return 8;
3636 if (!strcmpiW(month
, sep
)) return 9;
3637 if (!strcmpiW(month
, oct
)) return 10;
3638 if (!strcmpiW(month
, nov
)) return 11;
3639 if (!strcmpiW(month
, dec
)) return 12;
3644 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
3645 * optionally preceded by whitespace.
3646 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
3647 * st, and sets *str to the first character after the time format.
3649 static BOOL
HTTP_ParseTime(SYSTEMTIME
*st
, LPCWSTR
*str
)
3655 while (isspaceW(*ptr
))
3658 num
= strtoulW(ptr
, &nextPtr
, 10);
3659 if (!nextPtr
|| nextPtr
<= ptr
|| *nextPtr
!= ':')
3661 ERR("unexpected time format %s\n", debugstr_w(ptr
));
3666 ERR("unexpected hour in time format %s\n", debugstr_w(ptr
));
3670 st
->wHour
= (WORD
)num
;
3671 num
= strtoulW(ptr
, &nextPtr
, 10);
3672 if (!nextPtr
|| nextPtr
<= ptr
|| *nextPtr
!= ':')
3674 ERR("unexpected time format %s\n", debugstr_w(ptr
));
3679 ERR("unexpected minute in time format %s\n", debugstr_w(ptr
));
3683 st
->wMinute
= (WORD
)num
;
3684 num
= strtoulW(ptr
, &nextPtr
, 10);
3685 if (!nextPtr
|| nextPtr
<= ptr
)
3687 ERR("unexpected time format %s\n", debugstr_w(ptr
));
3692 ERR("unexpected second in time format %s\n", debugstr_w(ptr
));
3697 st
->wSecond
= (WORD
)num
;
3701 static BOOL
HTTP_ParseDateAsAsctime(LPCWSTR value
, FILETIME
*ft
)
3703 static const WCHAR gmt
[]= { 'G','M','T',0 };
3704 WCHAR day
[4], *dayPtr
, month
[4], *monthPtr
, *nextPtr
;
3706 SYSTEMTIME st
= { 0 };
3709 for (ptr
= value
, dayPtr
= day
; *ptr
&& !isspaceW(*ptr
) &&
3710 dayPtr
- day
< sizeof(day
) / sizeof(day
[0]) - 1; ptr
++, dayPtr
++)
3713 st
.wDayOfWeek
= HTTP_ParseDay(day
);
3714 if (st
.wDayOfWeek
>= 7)
3716 ERR("unexpected weekday %s\n", debugstr_w(day
));
3720 while (isspaceW(*ptr
))
3723 for (monthPtr
= month
; !isspace(*ptr
) &&
3724 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
3728 st
.wMonth
= HTTP_ParseMonth(month
);
3729 if (!st
.wMonth
|| st
.wMonth
> 12)
3731 ERR("unexpected month %s\n", debugstr_w(month
));
3735 while (isspaceW(*ptr
))
3738 num
= strtoulW(ptr
, &nextPtr
, 10);
3739 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
3741 ERR("unexpected day %s\n", debugstr_w(ptr
));
3745 st
.wDay
= (WORD
)num
;
3747 while (isspaceW(*ptr
))
3750 if (!HTTP_ParseTime(&st
, &ptr
))
3753 while (isspaceW(*ptr
))
3756 num
= strtoulW(ptr
, &nextPtr
, 10);
3757 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
3759 ERR("unexpected year %s\n", debugstr_w(ptr
));
3763 st
.wYear
= (WORD
)num
;
3765 while (isspaceW(*ptr
))
3768 /* asctime() doesn't report a timezone, but some web servers do, so accept
3769 * with or without GMT.
3771 if (*ptr
&& strcmpW(ptr
, gmt
))
3773 ERR("unexpected timezone %s\n", debugstr_w(ptr
));
3776 return SystemTimeToFileTime(&st
, ft
);
3779 static BOOL
HTTP_ParseRfc1123Date(LPCWSTR value
, FILETIME
*ft
)
3781 static const WCHAR gmt
[]= { 'G','M','T',0 };
3782 WCHAR
*nextPtr
, day
[4], month
[4], *monthPtr
;
3785 SYSTEMTIME st
= { 0 };
3787 ptr
= strchrW(value
, ',');
3790 if (ptr
- value
!= 3)
3792 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
3795 memcpy(day
, value
, (ptr
- value
) * sizeof(WCHAR
));
3797 st
.wDayOfWeek
= HTTP_ParseDay(day
);
3798 if (st
.wDayOfWeek
> 6)
3800 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
3805 while (isspaceW(*ptr
))
3808 num
= strtoulW(ptr
, &nextPtr
, 10);
3809 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
3811 ERR("unexpected day %s\n", debugstr_w(value
));
3815 st
.wDay
= (WORD
)num
;
3817 while (isspaceW(*ptr
))
3820 for (monthPtr
= month
; !isspace(*ptr
) &&
3821 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
3825 st
.wMonth
= HTTP_ParseMonth(month
);
3826 if (!st
.wMonth
|| st
.wMonth
> 12)
3828 ERR("unexpected month %s\n", debugstr_w(month
));
3832 while (isspaceW(*ptr
))
3835 num
= strtoulW(ptr
, &nextPtr
, 10);
3836 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
3838 ERR("unexpected year %s\n", debugstr_w(value
));
3842 st
.wYear
= (WORD
)num
;
3844 if (!HTTP_ParseTime(&st
, &ptr
))
3847 while (isspaceW(*ptr
))
3850 if (strcmpW(ptr
, gmt
))
3852 ERR("unexpected time zone %s\n", debugstr_w(ptr
));
3855 return SystemTimeToFileTime(&st
, ft
);
3858 /* FIXME: only accepts dates in RFC 1123 format and asctime() format,
3859 * which may not be the only formats actually seen in the wild.
3860 * http://www.hackcraft.net/web/datetime/ suggests at least RFC 850 dates
3861 * should be accepted as well.
3863 static BOOL
HTTP_ParseDate(LPCWSTR value
, FILETIME
*ft
)
3867 if (strchrW(value
, ','))
3868 ret
= HTTP_ParseRfc1123Date(value
, ft
);
3871 ret
= HTTP_ParseDateAsAsctime(value
, ft
);
3873 ERR("unexpected date format %s\n", debugstr_w(value
));
3878 static void HTTP_ProcessExpires(http_request_t
*request
)
3880 BOOL expirationFound
= FALSE
;
3883 /* Look for a Cache-Control header with a max-age directive, as it takes
3884 * precedence over the Expires header.
3886 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szCache_Control
, 0, FALSE
);
3887 if (headerIndex
!= -1)
3889 LPHTTPHEADERW ccHeader
= &request
->custHeaders
[headerIndex
];
3892 for (ptr
= ccHeader
->lpszValue
; ptr
&& *ptr
; )
3894 LPWSTR comma
= strchrW(ptr
, ','), end
, equal
;
3899 end
= ptr
+ strlenW(ptr
);
3900 for (equal
= end
- 1; equal
> ptr
&& *equal
!= '='; equal
--)
3904 static const WCHAR max_age
[] = {
3905 'm','a','x','-','a','g','e',0 };
3907 if (!strncmpiW(ptr
, max_age
, equal
- ptr
- 1))
3912 age
= strtoulW(equal
+ 1, &nextPtr
, 10);
3913 if (nextPtr
> equal
+ 1)
3917 NtQuerySystemTime( &ft
);
3918 /* Age is in seconds, FILETIME resolution is in
3919 * 100 nanosecond intervals.
3921 ft
.QuadPart
+= age
* (ULONGLONG
)1000000;
3922 request
->expires
.dwLowDateTime
= ft
.u
.LowPart
;
3923 request
->expires
.dwHighDateTime
= ft
.u
.HighPart
;
3924 expirationFound
= TRUE
;
3931 while (isspaceW(*ptr
))
3938 if (!expirationFound
)
3940 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szExpires
, 0, FALSE
);
3941 if (headerIndex
!= -1)
3943 LPHTTPHEADERW expiresHeader
= &request
->custHeaders
[headerIndex
];
3946 if (HTTP_ParseDate(expiresHeader
->lpszValue
, &ft
))
3948 expirationFound
= TRUE
;
3949 request
->expires
= ft
;
3953 if (!expirationFound
)
3957 /* With no known age, default to 10 minutes until expiration. */
3958 NtQuerySystemTime( &t
);
3959 t
.QuadPart
+= 10 * 60 * (ULONGLONG
)10000000;
3960 request
->expires
.dwLowDateTime
= t
.u
.LowPart
;
3961 request
->expires
.dwHighDateTime
= t
.u
.HighPart
;
3965 static void HTTP_ProcessLastModified(http_request_t
*request
)
3969 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szLast_Modified
, 0, FALSE
);
3970 if (headerIndex
!= -1)
3972 LPHTTPHEADERW expiresHeader
= &request
->custHeaders
[headerIndex
];
3975 if (HTTP_ParseDate(expiresHeader
->lpszValue
, &ft
))
3976 request
->last_modified
= ft
;
3980 static void HTTP_CacheRequest(http_request_t
*request
)
3982 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
3983 WCHAR cacheFileName
[MAX_PATH
+1];
3986 b
= HTTP_GetRequestURL(request
, url
);
3988 WARN("Could not get URL\n");
3992 b
= CreateUrlCacheEntryW(url
, request
->contentLength
, NULL
, cacheFileName
, 0);
3994 HeapFree(GetProcessHeap(), 0, request
->cacheFile
);
3995 CloseHandle(request
->hCacheFile
);
3997 request
->cacheFile
= heap_strdupW(cacheFileName
);
3998 request
->hCacheFile
= CreateFileW(request
->cacheFile
, GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
3999 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
4000 if(request
->hCacheFile
== INVALID_HANDLE_VALUE
) {
4001 WARN("Could not create file: %u\n", GetLastError());
4002 request
->hCacheFile
= NULL
;
4005 WARN("Could not create cache entry: %08x\n", GetLastError());
4009 /***********************************************************************
4010 * HTTP_HttpSendRequestW (internal)
4012 * Sends the specified request to the HTTP server
4015 * ERROR_SUCCESS on success
4016 * win32 error code on failure
4019 static DWORD
HTTP_HttpSendRequestW(http_request_t
*request
, LPCWSTR lpszHeaders
,
4020 DWORD dwHeaderLength
, LPVOID lpOptional
, DWORD dwOptionalLength
,
4021 DWORD dwContentLength
, BOOL bEndRequest
)
4024 BOOL redirected
= FALSE
;
4025 LPWSTR requestString
= NULL
;
4028 INTERNET_ASYNC_RESULT iar
;
4029 static const WCHAR szPost
[] = { 'P','O','S','T',0 };
4030 static const WCHAR szContentLength
[] =
4031 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4032 WCHAR contentLengthStr
[sizeof szContentLength
/2 /* includes \r\n */ + 20 /* int */ ];
4035 TRACE("--> %p\n", request
);
4037 assert(request
->hdr
.htype
== WH_HHTTPREQ
);
4039 /* if the verb is NULL default to GET */
4041 request
->verb
= heap_strdupW(szGET
);
4043 if (dwContentLength
|| strcmpW(request
->verb
, szGET
))
4045 sprintfW(contentLengthStr
, szContentLength
, dwContentLength
);
4046 HTTP_HttpAddRequestHeadersW(request
, contentLengthStr
, -1L, HTTP_ADDREQ_FLAG_REPLACE
);
4047 request
->bytesToWrite
= dwContentLength
;
4049 if (request
->session
->appInfo
->agent
)
4051 WCHAR
*agent_header
;
4052 static const WCHAR user_agent
[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4055 len
= strlenW(request
->session
->appInfo
->agent
) + strlenW(user_agent
);
4056 agent_header
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
4057 sprintfW(agent_header
, user_agent
, request
->session
->appInfo
->agent
);
4059 HTTP_HttpAddRequestHeadersW(request
, agent_header
, strlenW(agent_header
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4060 HeapFree(GetProcessHeap(), 0, agent_header
);
4062 if (request
->hdr
.dwFlags
& INTERNET_FLAG_PRAGMA_NOCACHE
)
4064 static const WCHAR pragma_nocache
[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4065 HTTP_HttpAddRequestHeadersW(request
, pragma_nocache
, strlenW(pragma_nocache
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4067 if ((request
->hdr
.dwFlags
& INTERNET_FLAG_NO_CACHE_WRITE
) && !strcmpW(request
->verb
, szPost
))
4069 static const WCHAR cache_control
[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4070 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4071 HTTP_HttpAddRequestHeadersW(request
, cache_control
, strlenW(cache_control
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4077 BOOL reusing_connection
;
4082 /* like native, just in case the caller forgot to call InternetReadFile
4083 * for all the data */
4084 HTTP_DrainContent(request
);
4085 request
->contentRead
= 0;
4087 request
->contentLength
= ~0u;
4088 request
->bytesToWrite
= 0;
4091 if (TRACE_ON(wininet
))
4093 LPHTTPHEADERW Host
= HTTP_GetHeader(request
, hostW
);
4094 TRACE("Going to url %s %s\n", debugstr_w(Host
->lpszValue
), debugstr_w(request
->path
));
4097 HTTP_FixURL(request
);
4098 if (request
->hdr
.dwFlags
& INTERNET_FLAG_KEEP_CONNECTION
)
4100 HTTP_ProcessHeader(request
, szConnection
, szKeepAlive
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
4102 HTTP_InsertAuthorization(request
, request
->authInfo
, szAuthorization
);
4103 HTTP_InsertAuthorization(request
, request
->proxyAuthInfo
, szProxy_Authorization
);
4105 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
))
4106 HTTP_InsertCookies(request
);
4108 /* add the headers the caller supplied */
4109 if( lpszHeaders
&& dwHeaderLength
)
4111 HTTP_HttpAddRequestHeadersW(request
, lpszHeaders
, dwHeaderLength
,
4112 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REPLACE
);
4115 if (request
->session
->appInfo
->proxy
&& request
->session
->appInfo
->proxy
[0])
4117 WCHAR
*url
= HTTP_BuildProxyRequestUrl(request
);
4118 requestString
= HTTP_BuildHeaderRequestString(request
, request
->verb
, url
, request
->version
);
4119 HeapFree(GetProcessHeap(), 0, url
);
4122 requestString
= HTTP_BuildHeaderRequestString(request
, request
->verb
, request
->path
, request
->version
);
4125 TRACE("Request header -> %s\n", debugstr_w(requestString
) );
4127 /* Send the request and store the results */
4128 if(NETCON_connected(&request
->netConnection
))
4129 reusing_connection
= TRUE
;
4131 reusing_connection
= FALSE
;
4133 if ((res
= HTTP_OpenConnection(request
)) != ERROR_SUCCESS
)
4136 /* send the request as ASCII, tack on the optional data */
4137 if (!lpOptional
|| redirected
)
4138 dwOptionalLength
= 0;
4139 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
4140 NULL
, 0, NULL
, NULL
);
4141 ascii_req
= HeapAlloc( GetProcessHeap(), 0, len
+ dwOptionalLength
);
4142 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
4143 ascii_req
, len
, NULL
, NULL
);
4145 memcpy( &ascii_req
[len
-1], lpOptional
, dwOptionalLength
);
4146 len
= (len
+ dwOptionalLength
- 1);
4148 TRACE("full request -> %s\n", debugstr_a(ascii_req
) );
4150 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4151 INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
4153 res
= NETCON_send(&request
->netConnection
, ascii_req
, len
, 0, &cnt
);
4154 HeapFree( GetProcessHeap(), 0, ascii_req
);
4156 request
->bytesWritten
= dwOptionalLength
;
4158 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4159 INTERNET_STATUS_REQUEST_SENT
,
4160 &len
, sizeof(DWORD
));
4167 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4168 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
4170 if (res
!= ERROR_SUCCESS
)
4173 responseLen
= HTTP_GetResponseHeaders(request
, TRUE
);
4174 /* FIXME: We should know that connection is closed before sending
4175 * headers. Otherwise wrong callbacks are executed */
4176 if(!responseLen
&& reusing_connection
) {
4177 TRACE("Connection closed by server, reconnecting\n");
4182 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4183 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
,
4186 HTTP_ProcessCookies(request
);
4187 HTTP_ProcessExpires(request
);
4188 HTTP_ProcessLastModified(request
);
4190 if (!set_content_length( request
)) HTTP_FinishedReading(request
);
4192 dwBufferSize
= sizeof(dwStatusCode
);
4193 if (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_STATUS_CODE
,
4194 &dwStatusCode
,&dwBufferSize
,NULL
) != ERROR_SUCCESS
)
4197 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
) && responseLen
)
4199 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
4200 dwBufferSize
=sizeof(szNewLocation
);
4201 if ((dwStatusCode
== HTTP_STATUS_REDIRECT
||
4202 dwStatusCode
== HTTP_STATUS_MOVED
||
4203 dwStatusCode
== HTTP_STATUS_REDIRECT_METHOD
) &&
4204 HTTP_HttpQueryInfoW(request
,HTTP_QUERY_LOCATION
,szNewLocation
,&dwBufferSize
,NULL
) == ERROR_SUCCESS
)
4206 if (strcmpW(request
->verb
, szGET
) && strcmpW(request
->verb
, szHEAD
))
4208 HeapFree(GetProcessHeap(), 0, request
->verb
);
4209 request
->verb
= heap_strdupW(szGET
);
4211 HTTP_DrainContent(request
);
4212 if ((new_url
= HTTP_GetRedirectURL( request
, szNewLocation
)))
4214 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
4215 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
4216 res
= HTTP_HandleRedirect(request
, new_url
);
4217 if (res
== ERROR_SUCCESS
)
4219 HeapFree(GetProcessHeap(), 0, requestString
);
4222 HeapFree( GetProcessHeap(), 0, new_url
);
4227 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTH
) && res
== ERROR_SUCCESS
)
4229 WCHAR szAuthValue
[2048];
4231 if (dwStatusCode
== HTTP_STATUS_DENIED
)
4233 LPHTTPHEADERW Host
= HTTP_GetHeader(request
, hostW
);
4235 while (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_WWW_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
4237 if (HTTP_DoAuthorization(request
, szAuthValue
,
4239 request
->session
->userName
,
4240 request
->session
->password
,
4243 HeapFree(GetProcessHeap(), 0, requestString
);
4250 TRACE("Cleaning wrong authorization data\n");
4251 destroy_authinfo(request
->authInfo
);
4252 request
->authInfo
= NULL
;
4255 if (dwStatusCode
== HTTP_STATUS_PROXY_AUTH_REQ
)
4258 while (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_PROXY_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
4260 if (HTTP_DoAuthorization(request
, szAuthValue
,
4261 &request
->proxyAuthInfo
,
4262 request
->session
->appInfo
->proxyUsername
,
4263 request
->session
->appInfo
->proxyPassword
,
4272 TRACE("Cleaning wrong proxy authorization data\n");
4273 destroy_authinfo(request
->proxyAuthInfo
);
4274 request
->proxyAuthInfo
= NULL
;
4280 res
= ERROR_SUCCESS
;
4284 if(res
== ERROR_SUCCESS
)
4285 HTTP_CacheRequest(request
);
4289 HeapFree(GetProcessHeap(), 0, requestString
);
4291 /* TODO: send notification for P3P header */
4293 if (request
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4295 if (res
== ERROR_SUCCESS
&& request
->bytesWritten
== request
->bytesToWrite
)
4296 HTTP_ReceiveRequestData(request
, TRUE
);
4299 iar
.dwResult
= (res
==ERROR_SUCCESS
? (DWORD_PTR
)request
->hdr
.hInternet
: 0);
4302 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4303 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
4304 sizeof(INTERNET_ASYNC_RESULT
));
4312 /***********************************************************************
4314 * Helper functions for the HttpSendRequest(Ex) functions
4317 static void AsyncHttpSendRequestProc(WORKREQUEST
*workRequest
)
4319 struct WORKREQ_HTTPSENDREQUESTW
const *req
= &workRequest
->u
.HttpSendRequestW
;
4320 http_request_t
*request
= (http_request_t
*) workRequest
->hdr
;
4322 TRACE("%p\n", request
);
4324 HTTP_HttpSendRequestW(request
, req
->lpszHeader
,
4325 req
->dwHeaderLength
, req
->lpOptional
, req
->dwOptionalLength
,
4326 req
->dwContentLength
, req
->bEndRequest
);
4328 HeapFree(GetProcessHeap(), 0, req
->lpszHeader
);
4332 static DWORD
HTTP_HttpEndRequestW(http_request_t
*request
, DWORD dwFlags
, DWORD_PTR dwContext
)
4336 DWORD res
= ERROR_SUCCESS
;
4338 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4339 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
4341 responseLen
= HTTP_GetResponseHeaders(request
, TRUE
);
4343 res
= ERROR_HTTP_HEADER_NOT_FOUND
;
4345 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4346 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
, sizeof(DWORD
));
4348 /* process cookies here. Is this right? */
4349 HTTP_ProcessCookies(request
);
4350 HTTP_ProcessExpires(request
);
4351 HTTP_ProcessLastModified(request
);
4353 if (!set_content_length( request
)) HTTP_FinishedReading(request
);
4355 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
))
4357 DWORD dwCode
,dwCodeLength
= sizeof(DWORD
);
4358 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_STATUS_CODE
, &dwCode
, &dwCodeLength
, NULL
) == ERROR_SUCCESS
4359 && (dwCode
== 302 || dwCode
== 301 || dwCode
== 303))
4361 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
4362 dwBufferSize
=sizeof(szNewLocation
);
4363 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_LOCATION
, szNewLocation
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
)
4365 if (strcmpW(request
->verb
, szGET
) && strcmpW(request
->verb
, szHEAD
))
4367 HeapFree(GetProcessHeap(), 0, request
->verb
);
4368 request
->verb
= heap_strdupW(szGET
);
4370 HTTP_DrainContent(request
);
4371 if ((new_url
= HTTP_GetRedirectURL( request
, szNewLocation
)))
4373 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
4374 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
4375 res
= HTTP_HandleRedirect(request
, new_url
);
4376 if (res
== ERROR_SUCCESS
)
4377 res
= HTTP_HttpSendRequestW(request
, NULL
, 0, NULL
, 0, 0, TRUE
);
4378 HeapFree( GetProcessHeap(), 0, new_url
);
4384 if (res
== ERROR_SUCCESS
) {
4385 HTTP_ReceiveRequestData(request
, TRUE
);
4387 INTERNET_ASYNC_RESULT iar
= {0, res
};
4389 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4390 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
4391 sizeof(INTERNET_ASYNC_RESULT
));
4397 /***********************************************************************
4398 * HttpEndRequestA (WININET.@)
4400 * Ends an HTTP request that was started by HttpSendRequestEx
4403 * TRUE if successful
4407 BOOL WINAPI
HttpEndRequestA(HINTERNET hRequest
,
4408 LPINTERNET_BUFFERSA lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
4410 TRACE("(%p, %p, %08x, %08lx)\n", hRequest
, lpBuffersOut
, dwFlags
, dwContext
);
4414 SetLastError(ERROR_INVALID_PARAMETER
);
4418 return HttpEndRequestW(hRequest
, NULL
, dwFlags
, dwContext
);
4421 static void AsyncHttpEndRequestProc(WORKREQUEST
*work
)
4423 struct WORKREQ_HTTPENDREQUESTW
const *req
= &work
->u
.HttpEndRequestW
;
4424 http_request_t
*request
= (http_request_t
*)work
->hdr
;
4426 TRACE("%p\n", request
);
4428 HTTP_HttpEndRequestW(request
, req
->dwFlags
, req
->dwContext
);
4431 /***********************************************************************
4432 * HttpEndRequestW (WININET.@)
4434 * Ends an HTTP request that was started by HttpSendRequestEx
4437 * TRUE if successful
4441 BOOL WINAPI
HttpEndRequestW(HINTERNET hRequest
,
4442 LPINTERNET_BUFFERSW lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
4444 http_request_t
*request
;
4451 SetLastError(ERROR_INVALID_PARAMETER
);
4455 request
= (http_request_t
*) get_handle_object( hRequest
);
4457 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
4459 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
4461 WININET_Release( &request
->hdr
);
4464 request
->hdr
.dwFlags
|= dwFlags
;
4466 if (request
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4469 struct WORKREQ_HTTPENDREQUESTW
*work_endrequest
;
4471 work
.asyncproc
= AsyncHttpEndRequestProc
;
4472 work
.hdr
= WININET_AddRef( &request
->hdr
);
4474 work_endrequest
= &work
.u
.HttpEndRequestW
;
4475 work_endrequest
->dwFlags
= dwFlags
;
4476 work_endrequest
->dwContext
= dwContext
;
4478 INTERNET_AsyncCall(&work
);
4479 res
= ERROR_IO_PENDING
;
4482 res
= HTTP_HttpEndRequestW(request
, dwFlags
, dwContext
);
4484 WININET_Release( &request
->hdr
);
4485 TRACE("%u <--\n", res
);
4486 if(res
!= ERROR_SUCCESS
)
4488 return res
== ERROR_SUCCESS
;
4491 /***********************************************************************
4492 * HttpSendRequestExA (WININET.@)
4494 * Sends the specified request to the HTTP server and allows chunked
4499 * Failure: FALSE, call GetLastError() for more information.
4501 BOOL WINAPI
HttpSendRequestExA(HINTERNET hRequest
,
4502 LPINTERNET_BUFFERSA lpBuffersIn
,
4503 LPINTERNET_BUFFERSA lpBuffersOut
,
4504 DWORD dwFlags
, DWORD_PTR dwContext
)
4506 INTERNET_BUFFERSW BuffersInW
;
4509 LPWSTR header
= NULL
;
4511 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
4512 lpBuffersOut
, dwFlags
, dwContext
);
4516 BuffersInW
.dwStructSize
= sizeof(LPINTERNET_BUFFERSW
);
4517 if (lpBuffersIn
->lpcszHeader
)
4519 headerlen
= MultiByteToWideChar(CP_ACP
,0,lpBuffersIn
->lpcszHeader
,
4520 lpBuffersIn
->dwHeadersLength
,0,0);
4521 header
= HeapAlloc(GetProcessHeap(),0,headerlen
*sizeof(WCHAR
));
4522 if (!(BuffersInW
.lpcszHeader
= header
))
4524 SetLastError(ERROR_OUTOFMEMORY
);
4527 BuffersInW
.dwHeadersLength
= MultiByteToWideChar(CP_ACP
, 0,
4528 lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
4532 BuffersInW
.lpcszHeader
= NULL
;
4533 BuffersInW
.dwHeadersTotal
= lpBuffersIn
->dwHeadersTotal
;
4534 BuffersInW
.lpvBuffer
= lpBuffersIn
->lpvBuffer
;
4535 BuffersInW
.dwBufferLength
= lpBuffersIn
->dwBufferLength
;
4536 BuffersInW
.dwBufferTotal
= lpBuffersIn
->dwBufferTotal
;
4537 BuffersInW
.Next
= NULL
;
4540 rc
= HttpSendRequestExW(hRequest
, lpBuffersIn
? &BuffersInW
: NULL
, NULL
, dwFlags
, dwContext
);
4542 HeapFree(GetProcessHeap(),0,header
);
4547 /***********************************************************************
4548 * HttpSendRequestExW (WININET.@)
4550 * Sends the specified request to the HTTP server and allows chunked
4555 * Failure: FALSE, call GetLastError() for more information.
4557 BOOL WINAPI
HttpSendRequestExW(HINTERNET hRequest
,
4558 LPINTERNET_BUFFERSW lpBuffersIn
,
4559 LPINTERNET_BUFFERSW lpBuffersOut
,
4560 DWORD dwFlags
, DWORD_PTR dwContext
)
4562 http_request_t
*request
;
4563 http_session_t
*session
;
4567 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
4568 lpBuffersOut
, dwFlags
, dwContext
);
4570 request
= (http_request_t
*) get_handle_object( hRequest
);
4572 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
4574 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
4578 session
= request
->session
;
4579 assert(session
->hdr
.htype
== WH_HHTTPSESSION
);
4580 hIC
= session
->appInfo
;
4581 assert(hIC
->hdr
.htype
== WH_HINIT
);
4583 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4585 WORKREQUEST workRequest
;
4586 struct WORKREQ_HTTPSENDREQUESTW
*req
;
4588 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
4589 workRequest
.hdr
= WININET_AddRef( &request
->hdr
);
4590 req
= &workRequest
.u
.HttpSendRequestW
;
4595 if (lpBuffersIn
->lpcszHeader
)
4597 if (lpBuffersIn
->dwHeadersLength
== ~0u)
4598 size
= (strlenW( lpBuffersIn
->lpcszHeader
) + 1) * sizeof(WCHAR
);
4600 size
= lpBuffersIn
->dwHeadersLength
* sizeof(WCHAR
);
4602 req
->lpszHeader
= HeapAlloc( GetProcessHeap(), 0, size
);
4603 memcpy( req
->lpszHeader
, lpBuffersIn
->lpcszHeader
, size
);
4605 else req
->lpszHeader
= NULL
;
4607 req
->dwHeaderLength
= size
/ sizeof(WCHAR
);
4608 req
->lpOptional
= lpBuffersIn
->lpvBuffer
;
4609 req
->dwOptionalLength
= lpBuffersIn
->dwBufferLength
;
4610 req
->dwContentLength
= lpBuffersIn
->dwBufferTotal
;
4614 req
->lpszHeader
= NULL
;
4615 req
->dwHeaderLength
= 0;
4616 req
->lpOptional
= NULL
;
4617 req
->dwOptionalLength
= 0;
4618 req
->dwContentLength
= 0;
4621 req
->bEndRequest
= FALSE
;
4623 INTERNET_AsyncCall(&workRequest
);
4625 * This is from windows.
4627 res
= ERROR_IO_PENDING
;
4632 res
= HTTP_HttpSendRequestW(request
, lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
4633 lpBuffersIn
->lpvBuffer
, lpBuffersIn
->dwBufferLength
,
4634 lpBuffersIn
->dwBufferTotal
, FALSE
);
4636 res
= HTTP_HttpSendRequestW(request
, NULL
, 0, NULL
, 0, 0, FALSE
);
4641 WININET_Release( &request
->hdr
);
4645 return res
== ERROR_SUCCESS
;
4648 /***********************************************************************
4649 * HttpSendRequestW (WININET.@)
4651 * Sends the specified request to the HTTP server
4658 BOOL WINAPI
HttpSendRequestW(HINTERNET hHttpRequest
, LPCWSTR lpszHeaders
,
4659 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
4661 http_request_t
*request
;
4662 http_session_t
*session
= NULL
;
4663 appinfo_t
*hIC
= NULL
;
4664 DWORD res
= ERROR_SUCCESS
;
4666 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest
,
4667 debugstr_wn(lpszHeaders
, dwHeaderLength
), dwHeaderLength
, lpOptional
, dwOptionalLength
);
4669 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
4670 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
4672 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
4676 session
= request
->session
;
4677 if (NULL
== session
|| session
->hdr
.htype
!= WH_HHTTPSESSION
)
4679 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
4683 hIC
= session
->appInfo
;
4684 if (NULL
== hIC
|| hIC
->hdr
.htype
!= WH_HINIT
)
4686 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
4690 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4692 WORKREQUEST workRequest
;
4693 struct WORKREQ_HTTPSENDREQUESTW
*req
;
4695 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
4696 workRequest
.hdr
= WININET_AddRef( &request
->hdr
);
4697 req
= &workRequest
.u
.HttpSendRequestW
;
4702 if (dwHeaderLength
== ~0u) size
= (strlenW(lpszHeaders
) + 1) * sizeof(WCHAR
);
4703 else size
= dwHeaderLength
* sizeof(WCHAR
);
4705 req
->lpszHeader
= HeapAlloc(GetProcessHeap(), 0, size
);
4706 memcpy(req
->lpszHeader
, lpszHeaders
, size
);
4709 req
->lpszHeader
= 0;
4710 req
->dwHeaderLength
= dwHeaderLength
;
4711 req
->lpOptional
= lpOptional
;
4712 req
->dwOptionalLength
= dwOptionalLength
;
4713 req
->dwContentLength
= dwOptionalLength
;
4714 req
->bEndRequest
= TRUE
;
4716 INTERNET_AsyncCall(&workRequest
);
4718 * This is from windows.
4720 res
= ERROR_IO_PENDING
;
4724 res
= HTTP_HttpSendRequestW(request
, lpszHeaders
,
4725 dwHeaderLength
, lpOptional
, dwOptionalLength
,
4726 dwOptionalLength
, TRUE
);
4730 WININET_Release( &request
->hdr
);
4733 return res
== ERROR_SUCCESS
;
4736 /***********************************************************************
4737 * HttpSendRequestA (WININET.@)
4739 * Sends the specified request to the HTTP server
4746 BOOL WINAPI
HttpSendRequestA(HINTERNET hHttpRequest
, LPCSTR lpszHeaders
,
4747 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
4750 LPWSTR szHeaders
=NULL
;
4751 DWORD nLen
=dwHeaderLength
;
4752 if(lpszHeaders
!=NULL
)
4754 nLen
=MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,NULL
,0);
4755 szHeaders
=HeapAlloc(GetProcessHeap(),0,nLen
*sizeof(WCHAR
));
4756 MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,szHeaders
,nLen
);
4758 result
=HttpSendRequestW(hHttpRequest
, szHeaders
, nLen
, lpOptional
, dwOptionalLength
);
4759 HeapFree(GetProcessHeap(),0,szHeaders
);
4763 /***********************************************************************
4764 * HTTPSESSION_Destroy (internal)
4766 * Deallocate session handle
4769 static void HTTPSESSION_Destroy(object_header_t
*hdr
)
4771 http_session_t
*session
= (http_session_t
*) hdr
;
4773 TRACE("%p\n", session
);
4775 WININET_Release(&session
->appInfo
->hdr
);
4777 HeapFree(GetProcessHeap(), 0, session
->hostName
);
4778 HeapFree(GetProcessHeap(), 0, session
->serverName
);
4779 HeapFree(GetProcessHeap(), 0, session
->password
);
4780 HeapFree(GetProcessHeap(), 0, session
->userName
);
4783 static DWORD
HTTPSESSION_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
4786 case INTERNET_OPTION_HANDLE_TYPE
:
4787 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4789 if (*size
< sizeof(ULONG
))
4790 return ERROR_INSUFFICIENT_BUFFER
;
4792 *size
= sizeof(DWORD
);
4793 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_CONNECT_HTTP
;
4794 return ERROR_SUCCESS
;
4797 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
4800 static DWORD
HTTPSESSION_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
4802 http_session_t
*ses
= (http_session_t
*)hdr
;
4805 case INTERNET_OPTION_USERNAME
:
4807 HeapFree(GetProcessHeap(), 0, ses
->userName
);
4808 if (!(ses
->userName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
4809 return ERROR_SUCCESS
;
4811 case INTERNET_OPTION_PASSWORD
:
4813 HeapFree(GetProcessHeap(), 0, ses
->password
);
4814 if (!(ses
->password
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
4815 return ERROR_SUCCESS
;
4820 return ERROR_INTERNET_INVALID_OPTION
;
4823 static const object_vtbl_t HTTPSESSIONVtbl
= {
4824 HTTPSESSION_Destroy
,
4826 HTTPSESSION_QueryOption
,
4827 HTTPSESSION_SetOption
,
4836 /***********************************************************************
4837 * HTTP_Connect (internal)
4839 * Create http session handle
4842 * HINTERNET a session handle on success
4846 DWORD
HTTP_Connect(appinfo_t
*hIC
, LPCWSTR lpszServerName
,
4847 INTERNET_PORT serverPort
, LPCWSTR lpszUserName
,
4848 LPCWSTR lpszPassword
, DWORD dwFlags
, DWORD_PTR dwContext
,
4849 DWORD dwInternalFlags
, HINTERNET
*ret
)
4851 http_session_t
*session
= NULL
;
4855 if (!lpszServerName
|| !lpszServerName
[0])
4856 return ERROR_INVALID_PARAMETER
;
4858 assert( hIC
->hdr
.htype
== WH_HINIT
);
4860 session
= alloc_object(&hIC
->hdr
, &HTTPSESSIONVtbl
, sizeof(http_session_t
));
4862 return ERROR_OUTOFMEMORY
;
4865 * According to my tests. The name is not resolved until a request is sent
4868 session
->hdr
.htype
= WH_HHTTPSESSION
;
4869 session
->hdr
.dwFlags
= dwFlags
;
4870 session
->hdr
.dwContext
= dwContext
;
4871 session
->hdr
.dwInternalFlags
|= dwInternalFlags
;
4873 WININET_AddRef( &hIC
->hdr
);
4874 session
->appInfo
= hIC
;
4875 list_add_head( &hIC
->hdr
.children
, &session
->hdr
.entry
);
4877 if(hIC
->proxy
&& hIC
->accessType
== INTERNET_OPEN_TYPE_PROXY
) {
4878 if(hIC
->proxyBypass
)
4879 FIXME("Proxy bypass is ignored.\n");
4881 session
->serverName
= heap_strdupW(lpszServerName
);
4882 session
->hostName
= heap_strdupW(lpszServerName
);
4883 if (lpszUserName
&& lpszUserName
[0])
4884 session
->userName
= heap_strdupW(lpszUserName
);
4885 if (lpszPassword
&& lpszPassword
[0])
4886 session
->password
= heap_strdupW(lpszPassword
);
4887 session
->serverPort
= serverPort
;
4888 session
->hostPort
= serverPort
;
4890 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4891 if (!(session
->hdr
.dwInternalFlags
& INET_OPENURL
))
4893 INTERNET_SendCallback(&hIC
->hdr
, dwContext
,
4894 INTERNET_STATUS_HANDLE_CREATED
, &session
->hdr
.hInternet
,
4899 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4903 TRACE("%p --> %p\n", hIC
, session
);
4905 *ret
= session
->hdr
.hInternet
;
4906 return ERROR_SUCCESS
;
4910 /***********************************************************************
4911 * HTTP_OpenConnection (internal)
4913 * Connect to a web server
4920 static DWORD
HTTP_OpenConnection(http_request_t
*request
)
4922 http_session_t
*session
;
4923 appinfo_t
*hIC
= NULL
;
4924 char szaddr
[INET6_ADDRSTRLEN
];
4926 DWORD res
= ERROR_SUCCESS
;
4931 if (request
->hdr
.htype
!= WH_HHTTPREQ
)
4933 res
= ERROR_INVALID_PARAMETER
;
4937 if (NETCON_connected(&request
->netConnection
))
4939 if ((res
= HTTP_ResolveName(request
)) != ERROR_SUCCESS
) goto lend
;
4941 session
= request
->session
;
4943 hIC
= session
->appInfo
;
4944 switch (session
->socketAddress
.ss_family
)
4947 addr
= &((struct sockaddr_in
*)&session
->socketAddress
)->sin_addr
;
4950 addr
= &((struct sockaddr_in6
*)&session
->socketAddress
)->sin6_addr
;
4953 WARN("unsupported family %d\n", session
->socketAddress
.ss_family
);
4954 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
4956 inet_ntop(session
->socketAddress
.ss_family
, addr
, szaddr
, sizeof(szaddr
));
4957 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4958 INTERNET_STATUS_CONNECTING_TO_SERVER
,
4962 res
= NETCON_create(&request
->netConnection
, session
->socketAddress
.ss_family
, SOCK_STREAM
, 0);
4963 if (res
!= ERROR_SUCCESS
)
4965 WARN("Socket creation failed: %u\n", res
);
4969 res
= NETCON_connect(&request
->netConnection
, (struct sockaddr
*)&session
->socketAddress
,
4971 if(res
!= ERROR_SUCCESS
)
4974 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4975 INTERNET_STATUS_CONNECTED_TO_SERVER
,
4976 szaddr
, strlen(szaddr
)+1);
4978 if (request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
4980 /* Note: we differ from Microsoft's WinINet here. they seem to have
4981 * a bug that causes no status callbacks to be sent when starting
4982 * a tunnel to a proxy server using the CONNECT verb. i believe our
4983 * behaviour to be more correct and to not cause any incompatibilities
4984 * because using a secure connection through a proxy server is a rare
4985 * case that would be hard for anyone to depend on */
4986 if (hIC
->proxy
&& (res
= HTTP_SecureProxyConnect(request
)) != ERROR_SUCCESS
) {
4987 HTTPREQ_CloseConnection(&request
->hdr
);
4991 res
= NETCON_secure_connect(&request
->netConnection
, session
->hostName
);
4992 if(res
!= ERROR_SUCCESS
)
4994 WARN("Couldn't connect securely to host\n");
4996 if((request
->hdr
.ErrorMask
&INTERNET_ERROR_MASK_COMBINED_SEC_CERT
) && (
4997 res
== ERROR_INTERNET_SEC_CERT_DATE_INVALID
4998 || res
== ERROR_INTERNET_INVALID_CA
4999 || res
== ERROR_INTERNET_SEC_CERT_NO_REV
5000 || res
== ERROR_INTERNET_SEC_CERT_REV_FAILED
5001 || res
== ERROR_INTERNET_SEC_CERT_REVOKED
5002 || res
== ERROR_INTERNET_SEC_INVALID_CERT
5003 || res
== ERROR_INTERNET_SEC_CERT_CN_INVALID
))
5004 res
= ERROR_INTERNET_SEC_CERT_ERRORS
;
5006 HTTPREQ_CloseConnection(&request
->hdr
);
5013 request
->read_pos
= request
->read_size
= 0;
5014 request
->read_chunked
= FALSE
;
5015 if(request
->gzip_stream
)
5016 release_gzip_stream(request
);
5018 TRACE("%d <--\n", res
);
5023 /***********************************************************************
5024 * HTTP_clear_response_headers (internal)
5026 * clear out any old response headers
5028 static void HTTP_clear_response_headers( http_request_t
*request
)
5032 for( i
=0; i
<request
->nCustHeaders
; i
++)
5034 if( !request
->custHeaders
[i
].lpszField
)
5036 if( !request
->custHeaders
[i
].lpszValue
)
5038 if ( request
->custHeaders
[i
].wFlags
& HDR_ISREQUEST
)
5040 HTTP_DeleteCustomHeader( request
, i
);
5045 /***********************************************************************
5046 * HTTP_GetResponseHeaders (internal)
5048 * Read server response
5055 static INT
HTTP_GetResponseHeaders(http_request_t
*request
, BOOL clear
)
5058 WCHAR buffer
[MAX_REPLY_LEN
];
5059 DWORD buflen
= MAX_REPLY_LEN
;
5060 BOOL bSuccess
= FALSE
;
5062 char bufferA
[MAX_REPLY_LEN
];
5063 LPWSTR status_code
= NULL
, status_text
= NULL
;
5064 DWORD cchMaxRawHeaders
= 1024;
5065 LPWSTR lpszRawHeaders
= NULL
;
5067 DWORD cchRawHeaders
= 0;
5068 BOOL codeHundred
= FALSE
;
5072 if (!NETCON_connected(&request
->netConnection
))
5076 static const WCHAR szHundred
[] = {'1','0','0',0};
5078 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5080 buflen
= MAX_REPLY_LEN
;
5081 if (!read_line(request
, bufferA
, &buflen
))
5084 /* clear old response headers (eg. from a redirect response) */
5086 HTTP_clear_response_headers( request
);
5091 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
5092 /* check is this a status code line? */
5093 if (!strncmpW(buffer
, g_szHttp1_0
, 4))
5095 /* split the version from the status code */
5096 status_code
= strchrW( buffer
, ' ' );
5101 /* split the status code from the status text */
5102 status_text
= strchrW( status_code
, ' ' );
5107 TRACE("version [%s] status code [%s] status text [%s]\n",
5108 debugstr_w(buffer
), debugstr_w(status_code
), debugstr_w(status_text
) );
5110 codeHundred
= (!strcmpW(status_code
, szHundred
));
5112 else if (!codeHundred
)
5114 WARN("No status line at head of response (%s)\n", debugstr_w(buffer
));
5116 HeapFree(GetProcessHeap(), 0, request
->version
);
5117 HeapFree(GetProcessHeap(), 0, request
->statusText
);
5119 request
->version
= heap_strdupW(g_szHttp1_0
);
5120 request
->statusText
= heap_strdupW(szOK
);
5122 HeapFree(GetProcessHeap(), 0, request
->rawHeaders
);
5123 request
->rawHeaders
= heap_strdupW(szDefaultHeader
);
5128 } while (codeHundred
);
5130 /* Add status code */
5131 HTTP_ProcessHeader(request
, szStatus
, status_code
,
5132 HTTP_ADDHDR_FLAG_REPLACE
);
5134 HeapFree(GetProcessHeap(),0,request
->version
);
5135 HeapFree(GetProcessHeap(),0,request
->statusText
);
5137 request
->version
= heap_strdupW(buffer
);
5138 request
->statusText
= heap_strdupW(status_text
);
5140 /* Restore the spaces */
5141 *(status_code
-1) = ' ';
5142 *(status_text
-1) = ' ';
5144 /* regenerate raw headers */
5145 lpszRawHeaders
= HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
5146 if (!lpszRawHeaders
) goto lend
;
5148 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5149 cchMaxRawHeaders
*= 2;
5150 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
5151 if (temp
== NULL
) goto lend
;
5152 lpszRawHeaders
= temp
;
5153 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
5154 cchRawHeaders
+= (buflen
-1);
5155 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
5156 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
5157 lpszRawHeaders
[cchRawHeaders
] = '\0';
5159 /* Parse each response line */
5162 buflen
= MAX_REPLY_LEN
;
5163 if (read_line(request
, bufferA
, &buflen
))
5165 LPWSTR
* pFieldAndValue
;
5167 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA
));
5169 if (!bufferA
[0]) break;
5170 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
5172 pFieldAndValue
= HTTP_InterpretHttpHeader(buffer
);
5175 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5176 cchMaxRawHeaders
*= 2;
5177 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
5178 if (temp
== NULL
) goto lend
;
5179 lpszRawHeaders
= temp
;
5180 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
5181 cchRawHeaders
+= (buflen
-1);
5182 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
5183 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
5184 lpszRawHeaders
[cchRawHeaders
] = '\0';
5186 HTTP_ProcessHeader(request
, pFieldAndValue
[0], pFieldAndValue
[1],
5187 HTTP_ADDREQ_FLAG_ADD
);
5189 HTTP_FreeTokens(pFieldAndValue
);
5200 /* make sure the response header is terminated with an empty line. Some apps really
5201 truly care about that empty line being there for some reason. Just add it to the
5203 if (cchRawHeaders
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5205 cchMaxRawHeaders
= cchRawHeaders
+ strlenW(szCrLf
);
5206 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
5207 if (temp
== NULL
) goto lend
;
5208 lpszRawHeaders
= temp
;
5211 memcpy(&lpszRawHeaders
[cchRawHeaders
], szCrLf
, sizeof(szCrLf
));
5213 HeapFree(GetProcessHeap(), 0, request
->rawHeaders
);
5214 request
->rawHeaders
= lpszRawHeaders
;
5215 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders
));
5225 HeapFree(GetProcessHeap(), 0, lpszRawHeaders
);
5230 /***********************************************************************
5231 * HTTP_InterpretHttpHeader (internal)
5233 * Parse server response
5237 * Pointer to array of field, value, NULL on success.
5240 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
)
5242 LPWSTR
* pTokenPair
;
5246 pTokenPair
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*pTokenPair
)*3);
5248 pszColon
= strchrW(buffer
, ':');
5249 /* must have two tokens */
5252 HTTP_FreeTokens(pTokenPair
);
5254 TRACE("No ':' in line: %s\n", debugstr_w(buffer
));
5258 pTokenPair
[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon
- buffer
+ 1) * sizeof(WCHAR
));
5261 HTTP_FreeTokens(pTokenPair
);
5264 memcpy(pTokenPair
[0], buffer
, (pszColon
- buffer
) * sizeof(WCHAR
));
5265 pTokenPair
[0][pszColon
- buffer
] = '\0';
5269 len
= strlenW(pszColon
);
5270 pTokenPair
[1] = HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
));
5273 HTTP_FreeTokens(pTokenPair
);
5276 memcpy(pTokenPair
[1], pszColon
, (len
+ 1) * sizeof(WCHAR
));
5278 strip_spaces(pTokenPair
[0]);
5279 strip_spaces(pTokenPair
[1]);
5281 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair
[0]), debugstr_w(pTokenPair
[1]));
5285 /***********************************************************************
5286 * HTTP_ProcessHeader (internal)
5288 * Stuff header into header tables according to <dwModifier>
5292 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5294 static DWORD
HTTP_ProcessHeader(http_request_t
*request
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
)
5296 LPHTTPHEADERW lphttpHdr
= NULL
;
5298 BOOL request_only
= dwModifier
& HTTP_ADDHDR_FLAG_REQ
;
5299 DWORD res
= ERROR_HTTP_INVALID_HEADER
;
5301 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field
), debugstr_w(value
), dwModifier
);
5303 /* REPLACE wins out over ADD */
5304 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
5305 dwModifier
&= ~HTTP_ADDHDR_FLAG_ADD
;
5307 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD
)
5310 index
= HTTP_GetCustomHeaderIndex(request
, field
, 0, request_only
);
5314 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD_IF_NEW
)
5315 return ERROR_HTTP_INVALID_HEADER
;
5316 lphttpHdr
= &request
->custHeaders
[index
];
5322 hdr
.lpszField
= (LPWSTR
)field
;
5323 hdr
.lpszValue
= (LPWSTR
)value
;
5324 hdr
.wFlags
= hdr
.wCount
= 0;
5326 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5327 hdr
.wFlags
|= HDR_ISREQUEST
;
5329 return HTTP_InsertCustomHeader(request
, &hdr
);
5331 /* no value to delete */
5332 else return ERROR_SUCCESS
;
5334 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5335 lphttpHdr
->wFlags
|= HDR_ISREQUEST
;
5337 lphttpHdr
->wFlags
&= ~HDR_ISREQUEST
;
5339 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
5341 HTTP_DeleteCustomHeader( request
, index
);
5347 hdr
.lpszField
= (LPWSTR
)field
;
5348 hdr
.lpszValue
= (LPWSTR
)value
;
5349 hdr
.wFlags
= hdr
.wCount
= 0;
5351 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5352 hdr
.wFlags
|= HDR_ISREQUEST
;
5354 return HTTP_InsertCustomHeader(request
, &hdr
);
5357 return ERROR_SUCCESS
;
5359 else if (dwModifier
& COALESCEFLAGS
)
5364 INT origlen
= strlenW(lphttpHdr
->lpszValue
);
5365 INT valuelen
= strlenW(value
);
5367 if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
)
5370 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
5372 else if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON
)
5375 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
5378 len
= origlen
+ valuelen
+ ((ch
> 0) ? 2 : 0);
5380 lpsztmp
= HeapReAlloc(GetProcessHeap(), 0, lphttpHdr
->lpszValue
, (len
+1)*sizeof(WCHAR
));
5383 lphttpHdr
->lpszValue
= lpsztmp
;
5384 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5387 lphttpHdr
->lpszValue
[origlen
] = ch
;
5389 lphttpHdr
->lpszValue
[origlen
] = ' ';
5393 memcpy(&lphttpHdr
->lpszValue
[origlen
], value
, valuelen
*sizeof(WCHAR
));
5394 lphttpHdr
->lpszValue
[len
] = '\0';
5395 res
= ERROR_SUCCESS
;
5399 WARN("HeapReAlloc (%d bytes) failed\n",len
+1);
5400 res
= ERROR_OUTOFMEMORY
;
5403 TRACE("<-- %d\n", res
);
5408 /***********************************************************************
5409 * HTTP_FinishedReading (internal)
5411 * Called when all content from server has been read by client.
5414 static BOOL
HTTP_FinishedReading(http_request_t
*request
)
5416 BOOL keepalive
= HTTP_KeepAlive(request
);
5423 HTTPREQ_CloseConnection(&request
->hdr
);
5426 /* FIXME: store data in the URL cache here */
5432 /***********************************************************************
5433 * HTTP_GetCustomHeaderIndex (internal)
5435 * Return index of custom header from header array
5438 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*request
, LPCWSTR lpszField
,
5439 int requested_index
, BOOL request_only
)
5443 TRACE("%s, %d, %d\n", debugstr_w(lpszField
), requested_index
, request_only
);
5445 for (index
= 0; index
< request
->nCustHeaders
; index
++)
5447 if (strcmpiW(request
->custHeaders
[index
].lpszField
, lpszField
))
5450 if (request_only
&& !(request
->custHeaders
[index
].wFlags
& HDR_ISREQUEST
))
5453 if (!request_only
&& (request
->custHeaders
[index
].wFlags
& HDR_ISREQUEST
))
5456 if (requested_index
== 0)
5461 if (index
>= request
->nCustHeaders
)
5464 TRACE("Return: %d\n", index
);
5469 /***********************************************************************
5470 * HTTP_InsertCustomHeader (internal)
5472 * Insert header into array
5475 static DWORD
HTTP_InsertCustomHeader(http_request_t
*request
, LPHTTPHEADERW lpHdr
)
5478 LPHTTPHEADERW lph
= NULL
;
5480 TRACE("--> %s: %s\n", debugstr_w(lpHdr
->lpszField
), debugstr_w(lpHdr
->lpszValue
));
5481 count
= request
->nCustHeaders
+ 1;
5483 lph
= HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, request
->custHeaders
, sizeof(HTTPHEADERW
) * count
);
5485 lph
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(HTTPHEADERW
) * count
);
5488 return ERROR_OUTOFMEMORY
;
5490 request
->custHeaders
= lph
;
5491 request
->custHeaders
[count
-1].lpszField
= heap_strdupW(lpHdr
->lpszField
);
5492 request
->custHeaders
[count
-1].lpszValue
= heap_strdupW(lpHdr
->lpszValue
);
5493 request
->custHeaders
[count
-1].wFlags
= lpHdr
->wFlags
;
5494 request
->custHeaders
[count
-1].wCount
= lpHdr
->wCount
;
5495 request
->nCustHeaders
++;
5497 return ERROR_SUCCESS
;
5501 /***********************************************************************
5502 * HTTP_DeleteCustomHeader (internal)
5504 * Delete header from array
5505 * If this function is called, the indexs may change.
5507 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*request
, DWORD index
)
5509 if( request
->nCustHeaders
<= 0 )
5511 if( index
>= request
->nCustHeaders
)
5513 request
->nCustHeaders
--;
5515 HeapFree(GetProcessHeap(), 0, request
->custHeaders
[index
].lpszField
);
5516 HeapFree(GetProcessHeap(), 0, request
->custHeaders
[index
].lpszValue
);
5518 memmove( &request
->custHeaders
[index
], &request
->custHeaders
[index
+1],
5519 (request
->nCustHeaders
- index
)* sizeof(HTTPHEADERW
) );
5520 memset( &request
->custHeaders
[request
->nCustHeaders
], 0, sizeof(HTTPHEADERW
) );
5526 /***********************************************************************
5527 * HTTP_VerifyValidHeader (internal)
5529 * Verify the given header is not invalid for the given http request
5532 static BOOL
HTTP_VerifyValidHeader(http_request_t
*request
, LPCWSTR field
)
5534 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5535 if (!strcmpW(request
->version
, g_szHttp1_0
) && !strcmpiW(field
, szAccept_Encoding
))
5536 return ERROR_HTTP_INVALID_HEADER
;
5538 return ERROR_SUCCESS
;
5541 /***********************************************************************
5542 * IsHostInProxyBypassList (@)
5547 BOOL WINAPI
IsHostInProxyBypassList(DWORD flags
, LPCSTR szHost
, DWORD length
)
5549 FIXME("STUB: flags=%d host=%s length=%d\n",flags
,szHost
,length
);
5553 /***********************************************************************
5554 * InternetShowSecurityInfoByURLA (@)
5556 BOOL WINAPI
InternetShowSecurityInfoByURLA(LPCSTR url
, HWND window
)
5558 FIXME("stub: %s %p\n", url
, window
);
5562 /***********************************************************************
5563 * InternetShowSecurityInfoByURLW (@)
5565 BOOL WINAPI
InternetShowSecurityInfoByURLW(LPCWSTR url
, HWND window
)
5567 FIXME("stub: %s %p\n", debugstr_w(url
), window
);