2 * Wininet - Http Implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2002 CodeWeavers Inc.
6 * Copyright 2002 TransGaming Technologies Inc.
7 * Copyright 2004 Mike McCormack for CodeWeavers
8 * Copyright 2005 Aric Stewart for CodeWeavers
9 * Copyright 2006 Robert Shearman for CodeWeavers
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "wine/port.h"
32 #if defined(__MINGW32__) || defined (_MSC_VER)
36 #include <sys/types.h>
37 #ifdef HAVE_SYS_SOCKET_H
38 # include <sys/socket.h>
40 #ifdef HAVE_ARPA_INET_H
41 # include <arpa/inet.h>
59 #define NO_SHLWAPI_STREAM
60 #define NO_SHLWAPI_REG
61 #define NO_SHLWAPI_STRFCNS
62 #define NO_SHLWAPI_GDI
68 #include "wine/debug.h"
69 #include "wine/exception.h"
70 #include "wine/unicode.h"
72 #include "inet_ntop.c"
74 WINE_DEFAULT_DEBUG_CHANNEL(wininet
);
76 static const WCHAR g_szHttp1_0
[] = {'H','T','T','P','/','1','.','0',0};
77 static const WCHAR g_szHttp1_1
[] = {'H','T','T','P','/','1','.','1',0};
78 static const WCHAR hostW
[] = { 'H','o','s','t',0 };
79 static const WCHAR szAuthorization
[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
80 static const WCHAR szProxy_Authorization
[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
81 static const WCHAR szStatus
[] = { 'S','t','a','t','u','s',0 };
82 static const WCHAR szKeepAlive
[] = {'K','e','e','p','-','A','l','i','v','e',0};
83 static const WCHAR szGET
[] = { 'G','E','T', 0 };
84 static const WCHAR szHEAD
[] = { 'H','E','A','D', 0 };
85 static const WCHAR szCrLf
[] = {'\r','\n', 0};
87 static const WCHAR szAccept
[] = { 'A','c','c','e','p','t',0 };
88 static const WCHAR szAccept_Charset
[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
89 static const WCHAR szAccept_Encoding
[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
90 static const WCHAR szAccept_Language
[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
91 static const WCHAR szAccept_Ranges
[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
92 static const WCHAR szAge
[] = { 'A','g','e',0 };
93 static const WCHAR szAllow
[] = { 'A','l','l','o','w',0 };
94 static const WCHAR szCache_Control
[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
95 static const WCHAR szConnection
[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
96 static const WCHAR szContent_Base
[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
97 static const WCHAR szContent_Encoding
[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
98 static const WCHAR szContent_ID
[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
99 static const WCHAR szContent_Language
[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
100 static const WCHAR szContent_Length
[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
101 static const WCHAR szContent_Location
[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
102 static const WCHAR szContent_MD5
[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
103 static const WCHAR szContent_Range
[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
104 static const WCHAR szContent_Transfer_Encoding
[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
105 static const WCHAR szContent_Type
[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
106 static const WCHAR szCookie
[] = { 'C','o','o','k','i','e',0 };
107 static const WCHAR szDate
[] = { 'D','a','t','e',0 };
108 static const WCHAR szFrom
[] = { 'F','r','o','m',0 };
109 static const WCHAR szETag
[] = { 'E','T','a','g',0 };
110 static const WCHAR szExpect
[] = { 'E','x','p','e','c','t',0 };
111 static const WCHAR szExpires
[] = { 'E','x','p','i','r','e','s',0 };
112 static const WCHAR szIf_Match
[] = { 'I','f','-','M','a','t','c','h',0 };
113 static const WCHAR szIf_Modified_Since
[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
114 static const WCHAR szIf_None_Match
[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
115 static const WCHAR szIf_Range
[] = { 'I','f','-','R','a','n','g','e',0 };
116 static const WCHAR szIf_Unmodified_Since
[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
117 static const WCHAR szLast_Modified
[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
118 static const WCHAR szLocation
[] = { 'L','o','c','a','t','i','o','n',0 };
119 static const WCHAR szMax_Forwards
[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
120 static const WCHAR szMime_Version
[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
121 static const WCHAR szPragma
[] = { 'P','r','a','g','m','a',0 };
122 static const WCHAR szProxy_Authenticate
[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
123 static const WCHAR szProxy_Connection
[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
124 static const WCHAR szPublic
[] = { 'P','u','b','l','i','c',0 };
125 static const WCHAR szRange
[] = { 'R','a','n','g','e',0 };
126 static const WCHAR szReferer
[] = { 'R','e','f','e','r','e','r',0 };
127 static const WCHAR szRetry_After
[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
128 static const WCHAR szServer
[] = { 'S','e','r','v','e','r',0 };
129 static const WCHAR szSet_Cookie
[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
130 static const WCHAR szTransfer_Encoding
[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
131 static const WCHAR szUnless_Modified_Since
[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
132 static const WCHAR szUpgrade
[] = { 'U','p','g','r','a','d','e',0 };
133 static const WCHAR szURI
[] = { 'U','R','I',0 };
134 static const WCHAR szUser_Agent
[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
135 static const WCHAR szVary
[] = { 'V','a','r','y',0 };
136 static const WCHAR szVia
[] = { 'V','i','a',0 };
137 static const WCHAR szWarning
[] = { 'W','a','r','n','i','n','g',0 };
138 static const WCHAR szWWW_Authenticate
[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
140 #define MAXHOSTNAME 100
141 #define MAX_FIELD_VALUE_LEN 256
142 #define MAX_FIELD_LEN 256
144 #define HTTP_REFERER szReferer
145 #define HTTP_ACCEPT szAccept
146 #define HTTP_USERAGENT szUser_Agent
148 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
149 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
150 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
151 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
152 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
153 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
154 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
156 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
167 unsigned int auth_data_len
;
168 BOOL finished
; /* finished authenticating */
172 struct gzip_stream_t
{
182 typedef struct _authorizationData
188 LPSTR lpszAuthorization
;
189 UINT AuthorizationLen
;
192 static struct list basicAuthorizationCache
= LIST_INIT(basicAuthorizationCache
);
194 static CRITICAL_SECTION authcache_cs
;
195 static CRITICAL_SECTION_DEBUG critsect_debug
=
198 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
199 0, 0, { (DWORD_PTR
)(__FILE__
": authcache_cs") }
201 static CRITICAL_SECTION authcache_cs
= { &critsect_debug
, -1, 0, 0, 0, 0 };
203 static BOOL
HTTP_OpenConnection(http_request_t
*req
);
204 static BOOL
HTTP_GetResponseHeaders(http_request_t
*req
, BOOL clear
);
205 static BOOL
HTTP_ProcessHeader(http_request_t
*req
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
);
206 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
);
207 static BOOL
HTTP_InsertCustomHeader(http_request_t
*req
, LPHTTPHEADERW lpHdr
);
208 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*req
, LPCWSTR lpszField
, INT index
, BOOL Request
);
209 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*req
, DWORD index
);
210 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
);
211 static BOOL
HTTP_HttpQueryInfoW(http_request_t
*, DWORD
, LPVOID
, LPDWORD
, LPDWORD
);
212 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*req
, LPCWSTR lpszUrl
);
213 static BOOL
HTTP_HandleRedirect(http_request_t
*req
, LPCWSTR lpszUrl
);
214 static UINT
HTTP_DecodeBase64(LPCWSTR base64
, LPSTR bin
);
215 static BOOL
HTTP_VerifyValidHeader(http_request_t
*req
, LPCWSTR field
);
216 static void HTTP_DrainContent(http_request_t
*req
);
217 static BOOL
HTTP_FinishedReading(http_request_t
*req
);
219 static LPHTTPHEADERW
HTTP_GetHeader(http_request_t
*req
, LPCWSTR head
)
222 HeaderIndex
= HTTP_GetCustomHeaderIndex(req
, head
, 0, TRUE
);
223 if (HeaderIndex
== -1)
226 return &req
->pCustHeaders
[HeaderIndex
];
231 static voidpf
wininet_zalloc(voidpf opaque
, uInt items
, uInt size
)
233 return HeapAlloc(GetProcessHeap(), 0, items
*size
);
236 static void wininet_zfree(voidpf opaque
, voidpf address
)
238 HeapFree(GetProcessHeap(), 0, address
);
241 static void init_gzip_stream(http_request_t
*req
)
243 gzip_stream_t
*gzip_stream
;
246 gzip_stream
= HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t
));
247 gzip_stream
->zstream
.zalloc
= wininet_zalloc
;
248 gzip_stream
->zstream
.zfree
= wininet_zfree
;
249 gzip_stream
->zstream
.opaque
= NULL
;
250 gzip_stream
->zstream
.next_in
= NULL
;
251 gzip_stream
->zstream
.avail_in
= 0;
252 gzip_stream
->zstream
.next_out
= NULL
;
253 gzip_stream
->zstream
.avail_out
= 0;
254 gzip_stream
->buf_pos
= 0;
255 gzip_stream
->buf_size
= 0;
256 gzip_stream
->end_of_data
= FALSE
;
258 zres
= inflateInit2(&gzip_stream
->zstream
, 0x1f);
260 ERR("inflateInit failed: %d\n", zres
);
261 HeapFree(GetProcessHeap(), 0, gzip_stream
);
265 req
->gzip_stream
= gzip_stream
;
267 index
= HTTP_GetCustomHeaderIndex(req
, szContent_Length
, 0, FALSE
);
269 HTTP_DeleteCustomHeader(req
, index
);
274 static void init_gzip_stream(http_request_t
*req
)
276 ERR("gzip stream not supported, missing zlib.\n");
281 /* set the request content length based on the headers */
282 static DWORD
set_content_length( http_request_t
*lpwhr
)
284 static const WCHAR szChunked
[] = {'c','h','u','n','k','e','d',0};
288 size
= sizeof(lpwhr
->dwContentLength
);
289 if (!HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_CONTENT_LENGTH
,
290 &lpwhr
->dwContentLength
, &size
, NULL
))
291 lpwhr
->dwContentLength
= ~0u;
293 size
= sizeof(encoding
);
294 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_TRANSFER_ENCODING
, encoding
, &size
, NULL
) &&
295 !strcmpiW(encoding
, szChunked
))
297 lpwhr
->dwContentLength
= ~0u;
298 lpwhr
->read_chunked
= TRUE
;
301 if(lpwhr
->decoding
) {
304 static const WCHAR gzipW
[] = {'g','z','i','p',0};
306 encoding_idx
= HTTP_GetCustomHeaderIndex(lpwhr
, szContent_Encoding
, 0, FALSE
);
307 if(encoding_idx
!= -1 && !strcmpiW(lpwhr
->pCustHeaders
[encoding_idx
].lpszValue
, gzipW
))
308 init_gzip_stream(lpwhr
);
311 return lpwhr
->dwContentLength
;
314 /***********************************************************************
315 * HTTP_Tokenize (internal)
317 * Tokenize a string, allocating memory for the tokens.
319 static LPWSTR
* HTTP_Tokenize(LPCWSTR string
, LPCWSTR token_string
)
321 LPWSTR
* token_array
;
328 /* empty string has no tokens */
332 for (i
= 0; string
[i
]; i
++)
334 if (!strncmpW(string
+i
, token_string
, strlenW(token_string
)))
338 /* we want to skip over separators, but not the null terminator */
339 for (j
= 0; j
< strlenW(token_string
) - 1; j
++)
347 /* add 1 for terminating NULL */
348 token_array
= HeapAlloc(GetProcessHeap(), 0, (tokens
+1) * sizeof(*token_array
));
349 token_array
[tokens
] = NULL
;
352 for (i
= 0; i
< tokens
; i
++)
355 next_token
= strstrW(string
, token_string
);
356 if (!next_token
) next_token
= string
+strlenW(string
);
357 len
= next_token
- string
;
358 token_array
[i
] = HeapAlloc(GetProcessHeap(), 0, (len
+1)*sizeof(WCHAR
));
359 memcpy(token_array
[i
], string
, len
*sizeof(WCHAR
));
360 token_array
[i
][len
] = '\0';
361 string
= next_token
+strlenW(token_string
);
366 /***********************************************************************
367 * HTTP_FreeTokens (internal)
369 * Frees memory returned from HTTP_Tokenize.
371 static void HTTP_FreeTokens(LPWSTR
* token_array
)
374 for (i
= 0; token_array
[i
]; i
++)
375 HeapFree(GetProcessHeap(), 0, token_array
[i
]);
376 HeapFree(GetProcessHeap(), 0, token_array
);
379 /* **********************************************************************
381 * Helper functions for the HttpSendRequest(Ex) functions
384 static void AsyncHttpSendRequestProc(WORKREQUEST
*workRequest
)
386 struct WORKREQ_HTTPSENDREQUESTW
const *req
= &workRequest
->u
.HttpSendRequestW
;
387 http_request_t
*lpwhr
= (http_request_t
*) workRequest
->hdr
;
389 TRACE("%p\n", lpwhr
);
391 HTTP_HttpSendRequestW(lpwhr
, req
->lpszHeader
,
392 req
->dwHeaderLength
, req
->lpOptional
, req
->dwOptionalLength
,
393 req
->dwContentLength
, req
->bEndRequest
);
395 HeapFree(GetProcessHeap(), 0, req
->lpszHeader
);
398 static void HTTP_FixURL(http_request_t
*lpwhr
)
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
== lpwhr
->lpszPath
)
405 lpwhr
->lpszPath
= heap_strdupW(szSlash
);
406 else /* remove \r and \n*/
408 int nLen
= strlenW(lpwhr
->lpszPath
);
409 while ((nLen
>0 ) && ((lpwhr
->lpszPath
[nLen
-1] == '\r')||(lpwhr
->lpszPath
[nLen
-1] == '\n')))
412 lpwhr
->lpszPath
[nLen
]='\0';
414 /* Replace '\' with '/' */
417 if (lpwhr
->lpszPath
[nLen
] == '\\') lpwhr
->lpszPath
[nLen
]='/';
421 if(CSTR_EQUAL
!= CompareStringW( LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
422 lpwhr
->lpszPath
, strlenW(lpwhr
->lpszPath
), szHttp
, strlenW(szHttp
) )
423 && lpwhr
->lpszPath
[0] != '/') /* not an absolute path ?? --> fix it !! */
425 WCHAR
*fixurl
= HeapAlloc(GetProcessHeap(), 0,
426 (strlenW(lpwhr
->lpszPath
) + 2)*sizeof(WCHAR
));
428 strcpyW(fixurl
+ 1, lpwhr
->lpszPath
);
429 HeapFree( GetProcessHeap(), 0, lpwhr
->lpszPath
);
430 lpwhr
->lpszPath
= fixurl
;
434 static LPWSTR
HTTP_BuildHeaderRequestString( http_request_t
*lpwhr
, 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
= (lpwhr
->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
< lpwhr
->nCustHeaders
; i
++)
461 if (lpwhr
->pCustHeaders
[i
].wFlags
& HDR_ISREQUEST
)
464 req
[n
++] = lpwhr
->pCustHeaders
[i
].lpszField
;
466 req
[n
++] = lpwhr
->pCustHeaders
[i
].lpszValue
;
468 TRACE("Adding custom header %s (%s)\n",
469 debugstr_w(lpwhr
->pCustHeaders
[i
].lpszField
),
470 debugstr_w(lpwhr
->pCustHeaders
[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
*lpwhr
)
497 LPHTTPHEADERW setCookieHeader
;
499 while((HeaderIndex
= HTTP_GetCustomHeaderIndex(lpwhr
, szSet_Cookie
, numCookies
, FALSE
)) != -1)
501 setCookieHeader
= &lpwhr
->pCustHeaders
[HeaderIndex
];
503 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
) && setCookieHeader
->lpszValue
)
506 static const WCHAR szFmt
[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
510 Host
= HTTP_GetHeader(lpwhr
, hostW
);
511 len
= lstrlenW(Host
->lpszValue
) + 9 + lstrlenW(lpwhr
->lpszPath
);
512 buf_url
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
513 sprintfW(buf_url
, szFmt
, Host
->lpszValue
, lpwhr
->lpszPath
);
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 authorizationData
*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
, authorizationData
, entry
)
601 if (!strcmpiW(host
,ad
->lpszwHost
) && !strcmpW(realm
,ad
->lpszwRealm
))
603 TRACE("Authorization found in cache\n");
604 *auth_data
= HeapAlloc(GetProcessHeap(),0,ad
->AuthorizationLen
);
605 memcpy(*auth_data
,ad
->lpszAuthorization
,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 authorizationData
* 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 authorizationData
*check
= LIST_ENTRY(cursor
,authorizationData
,entry
);
625 if (!strcmpiW(host
,check
->lpszwHost
) && !strcmpW(realm
,check
->lpszwRealm
))
634 TRACE("Found match in cache, replacing\n");
635 HeapFree(GetProcessHeap(),0,ad
->lpszAuthorization
);
636 ad
->lpszAuthorization
= HeapAlloc(GetProcessHeap(),0,auth_data_len
);
637 memcpy(ad
->lpszAuthorization
, auth_data
, auth_data_len
);
638 ad
->AuthorizationLen
= auth_data_len
;
642 ad
= HeapAlloc(GetProcessHeap(),0,sizeof(authorizationData
));
643 ad
->lpszwHost
= heap_strdupW(host
);
644 ad
->lpszwRealm
= heap_strdupW(realm
);
645 ad
->lpszAuthorization
= HeapAlloc(GetProcessHeap(),0,auth_data_len
);
646 memcpy(ad
->lpszAuthorization
, 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
HTTP_DoAuthorization( http_request_t
*lpwhr
, LPCWSTR pszAuthValue
,
655 struct HttpAuthInfo
**ppAuthInfo
,
656 LPWSTR domain_and_username
, LPWSTR password
,
659 SECURITY_STATUS sec_status
;
660 struct HttpAuthInfo
*pAuthInfo
= *ppAuthInfo
;
662 LPWSTR szRealm
= NULL
;
664 TRACE("%s\n", debugstr_w(pszAuthValue
));
671 pAuthInfo
= HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo
));
675 SecInvalidateHandle(&pAuthInfo
->cred
);
676 SecInvalidateHandle(&pAuthInfo
->ctx
);
677 memset(&pAuthInfo
->exp
, 0, sizeof(pAuthInfo
->exp
));
679 pAuthInfo
->auth_data
= NULL
;
680 pAuthInfo
->auth_data_len
= 0;
681 pAuthInfo
->finished
= FALSE
;
683 if (is_basic_auth_value(pszAuthValue
,NULL
))
685 static const WCHAR szBasic
[] = {'B','a','s','i','c',0};
686 pAuthInfo
->scheme
= heap_strdupW(szBasic
);
687 if (!pAuthInfo
->scheme
)
689 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
696 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity
;
698 pAuthInfo
->scheme
= heap_strdupW(pszAuthValue
);
699 if (!pAuthInfo
->scheme
)
701 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
705 if (domain_and_username
)
707 WCHAR
*user
= strchrW(domain_and_username
, '\\');
708 WCHAR
*domain
= domain_and_username
;
710 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
712 pAuthData
= &nt_auth_identity
;
717 user
= domain_and_username
;
721 nt_auth_identity
.Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
722 nt_auth_identity
.User
= user
;
723 nt_auth_identity
.UserLength
= strlenW(nt_auth_identity
.User
);
724 nt_auth_identity
.Domain
= domain
;
725 nt_auth_identity
.DomainLength
= domain
? user
- domain
- 1 : 0;
726 nt_auth_identity
.Password
= password
;
727 nt_auth_identity
.PasswordLength
= strlenW(nt_auth_identity
.Password
);
730 /* use default credentials */
733 sec_status
= AcquireCredentialsHandleW(NULL
, pAuthInfo
->scheme
,
734 SECPKG_CRED_OUTBOUND
, NULL
,
736 NULL
, &pAuthInfo
->cred
,
738 if (sec_status
== SEC_E_OK
)
740 PSecPkgInfoW sec_pkg_info
;
741 sec_status
= QuerySecurityPackageInfoW(pAuthInfo
->scheme
, &sec_pkg_info
);
742 if (sec_status
== SEC_E_OK
)
744 pAuthInfo
->max_token
= sec_pkg_info
->cbMaxToken
;
745 FreeContextBuffer(sec_pkg_info
);
748 if (sec_status
!= SEC_E_OK
)
750 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
751 debugstr_w(pAuthInfo
->scheme
), sec_status
);
752 HeapFree(GetProcessHeap(), 0, pAuthInfo
->scheme
);
753 HeapFree(GetProcessHeap(), 0, pAuthInfo
);
757 *ppAuthInfo
= pAuthInfo
;
759 else if (pAuthInfo
->finished
)
762 if ((strlenW(pszAuthValue
) < strlenW(pAuthInfo
->scheme
)) ||
763 strncmpiW(pszAuthValue
, pAuthInfo
->scheme
, strlenW(pAuthInfo
->scheme
)))
765 ERR("authentication scheme changed from %s to %s\n",
766 debugstr_w(pAuthInfo
->scheme
), debugstr_w(pszAuthValue
));
770 if (is_basic_auth_value(pszAuthValue
,&szRealm
))
774 char *auth_data
= NULL
;
775 UINT auth_data_len
= 0;
777 TRACE("basic authentication realm %s\n",debugstr_w(szRealm
));
779 if (!domain_and_username
)
782 auth_data_len
= retrieve_cached_basic_authorization(host
, szRealm
,&auth_data
);
783 if (auth_data_len
== 0)
785 HeapFree(GetProcessHeap(),0,szRealm
);
791 userlen
= WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, lstrlenW(domain_and_username
), NULL
, 0, NULL
, NULL
);
792 passlen
= WideCharToMultiByte(CP_UTF8
, 0, password
, lstrlenW(password
), NULL
, 0, NULL
, NULL
);
794 /* length includes a nul terminator, which will be re-used for the ':' */
795 auth_data
= HeapAlloc(GetProcessHeap(), 0, userlen
+ 1 + passlen
);
798 HeapFree(GetProcessHeap(),0,szRealm
);
802 WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, -1, auth_data
, userlen
, NULL
, NULL
);
803 auth_data
[userlen
] = ':';
804 WideCharToMultiByte(CP_UTF8
, 0, password
, -1, &auth_data
[userlen
+1], passlen
, NULL
, NULL
);
805 auth_data_len
= userlen
+ 1 + passlen
;
807 cache_basic_authorization(host
, szRealm
, auth_data
, auth_data_len
);
810 pAuthInfo
->auth_data
= auth_data
;
811 pAuthInfo
->auth_data_len
= auth_data_len
;
812 pAuthInfo
->finished
= TRUE
;
813 HeapFree(GetProcessHeap(),0,szRealm
);
820 SecBufferDesc out_desc
, in_desc
;
822 unsigned char *buffer
;
823 ULONG context_req
= ISC_REQ_CONNECTION
| ISC_REQ_USE_DCE_STYLE
|
824 ISC_REQ_MUTUAL_AUTH
| ISC_REQ_DELEGATE
;
826 in
.BufferType
= SECBUFFER_TOKEN
;
830 in_desc
.ulVersion
= 0;
831 in_desc
.cBuffers
= 1;
832 in_desc
.pBuffers
= &in
;
834 pszAuthData
= pszAuthValue
+ strlenW(pAuthInfo
->scheme
);
835 if (*pszAuthData
== ' ')
838 in
.cbBuffer
= HTTP_DecodeBase64(pszAuthData
, NULL
);
839 in
.pvBuffer
= HeapAlloc(GetProcessHeap(), 0, in
.cbBuffer
);
840 HTTP_DecodeBase64(pszAuthData
, in
.pvBuffer
);
843 buffer
= HeapAlloc(GetProcessHeap(), 0, pAuthInfo
->max_token
);
845 out
.BufferType
= SECBUFFER_TOKEN
;
846 out
.cbBuffer
= pAuthInfo
->max_token
;
847 out
.pvBuffer
= buffer
;
849 out_desc
.ulVersion
= 0;
850 out_desc
.cBuffers
= 1;
851 out_desc
.pBuffers
= &out
;
853 sec_status
= InitializeSecurityContextW(first
? &pAuthInfo
->cred
: NULL
,
854 first
? NULL
: &pAuthInfo
->ctx
,
855 first
? lpwhr
->lpHttpSession
->lpszServerName
: NULL
,
856 context_req
, 0, SECURITY_NETWORK_DREP
,
857 in
.pvBuffer
? &in_desc
: NULL
,
858 0, &pAuthInfo
->ctx
, &out_desc
,
859 &pAuthInfo
->attr
, &pAuthInfo
->exp
);
860 if (sec_status
== SEC_E_OK
)
862 pAuthInfo
->finished
= TRUE
;
863 pAuthInfo
->auth_data
= out
.pvBuffer
;
864 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
865 TRACE("sending last auth packet\n");
867 else if (sec_status
== SEC_I_CONTINUE_NEEDED
)
869 pAuthInfo
->auth_data
= out
.pvBuffer
;
870 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
871 TRACE("sending next auth packet\n");
875 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status
);
876 HeapFree(GetProcessHeap(), 0, out
.pvBuffer
);
877 destroy_authinfo(pAuthInfo
);
886 /***********************************************************************
887 * HTTP_HttpAddRequestHeadersW (internal)
889 static BOOL
HTTP_HttpAddRequestHeadersW(http_request_t
*lpwhr
,
890 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
895 BOOL bSuccess
= FALSE
;
898 TRACE("copying header: %s\n", debugstr_wn(lpszHeader
, dwHeaderLength
));
900 if( dwHeaderLength
== ~0U )
901 len
= strlenW(lpszHeader
);
903 len
= dwHeaderLength
;
904 buffer
= HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR
)*(len
+1) );
905 lstrcpynW( buffer
, lpszHeader
, len
+ 1);
911 LPWSTR
* pFieldAndValue
;
915 while (*lpszEnd
!= '\0')
917 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
922 if (*lpszStart
== '\0')
925 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
928 lpszEnd
++; /* Jump over newline */
930 TRACE("interpreting header %s\n", debugstr_w(lpszStart
));
931 if (*lpszStart
== '\0')
933 /* Skip 0-length headers */
938 pFieldAndValue
= HTTP_InterpretHttpHeader(lpszStart
);
941 bSuccess
= HTTP_VerifyValidHeader(lpwhr
, pFieldAndValue
[0]);
943 bSuccess
= HTTP_ProcessHeader(lpwhr
, pFieldAndValue
[0],
944 pFieldAndValue
[1], dwModifier
| HTTP_ADDHDR_FLAG_REQ
);
945 HTTP_FreeTokens(pFieldAndValue
);
951 HeapFree(GetProcessHeap(), 0, buffer
);
956 /***********************************************************************
957 * HttpAddRequestHeadersW (WININET.@)
959 * Adds one or more HTTP header to the request handler
962 * On Windows if dwHeaderLength includes the trailing '\0', then
963 * HttpAddRequestHeadersW() adds it too. However this results in an
964 * invalid Http header which is rejected by some servers so we probably
965 * don't need to match Windows on that point.
972 BOOL WINAPI
HttpAddRequestHeadersW(HINTERNET hHttpRequest
,
973 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
975 BOOL bSuccess
= FALSE
;
976 http_request_t
*lpwhr
;
978 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_wn(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
983 lpwhr
= (http_request_t
*) WININET_GetObject( hHttpRequest
);
984 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
986 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
989 bSuccess
= HTTP_HttpAddRequestHeadersW( lpwhr
, lpszHeader
, dwHeaderLength
, dwModifier
);
992 WININET_Release( &lpwhr
->hdr
);
997 /***********************************************************************
998 * HttpAddRequestHeadersA (WININET.@)
1000 * Adds one or more HTTP header to the request handler
1007 BOOL WINAPI
HttpAddRequestHeadersA(HINTERNET hHttpRequest
,
1008 LPCSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1014 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_an(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
1016 len
= MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, NULL
, 0 );
1017 hdr
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
1018 MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, hdr
, len
);
1019 if( dwHeaderLength
!= ~0U )
1020 dwHeaderLength
= len
;
1022 r
= HttpAddRequestHeadersW( hHttpRequest
, hdr
, dwHeaderLength
, dwModifier
);
1024 HeapFree( GetProcessHeap(), 0, hdr
);
1029 /***********************************************************************
1030 * HttpEndRequestA (WININET.@)
1032 * Ends an HTTP request that was started by HttpSendRequestEx
1035 * TRUE if successful
1039 BOOL WINAPI
HttpEndRequestA(HINTERNET hRequest
,
1040 LPINTERNET_BUFFERSA lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
1042 TRACE("(%p, %p, %08x, %08lx)\n", hRequest
, lpBuffersOut
, dwFlags
, dwContext
);
1046 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
1050 return HttpEndRequestW(hRequest
, NULL
, dwFlags
, dwContext
);
1053 static BOOL
HTTP_HttpEndRequestW(http_request_t
*lpwhr
, DWORD dwFlags
, DWORD_PTR dwContext
)
1058 INTERNET_ASYNC_RESULT iar
;
1060 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1061 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
1063 responseLen
= HTTP_GetResponseHeaders(lpwhr
, TRUE
);
1067 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1068 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
, sizeof(DWORD
));
1070 /* process cookies here. Is this right? */
1071 HTTP_ProcessCookies(lpwhr
);
1073 if (!set_content_length( lpwhr
)) HTTP_FinishedReading(lpwhr
);
1075 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
))
1077 DWORD dwCode
,dwCodeLength
= sizeof(DWORD
);
1078 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_STATUS_CODE
, &dwCode
, &dwCodeLength
, NULL
) &&
1079 (dwCode
== 302 || dwCode
== 301 || dwCode
== 303))
1081 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
1082 dwBufferSize
=sizeof(szNewLocation
);
1083 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_LOCATION
, szNewLocation
, &dwBufferSize
, NULL
))
1085 if (strcmpW(lpwhr
->lpszVerb
, szGET
) && strcmpW(lpwhr
->lpszVerb
, szHEAD
))
1087 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVerb
);
1088 lpwhr
->lpszVerb
= heap_strdupW(szGET
);
1090 HTTP_DrainContent(lpwhr
);
1091 if ((new_url
= HTTP_GetRedirectURL( lpwhr
, szNewLocation
)))
1093 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
1094 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
1095 rc
= HTTP_HandleRedirect(lpwhr
, new_url
);
1097 rc
= HTTP_HttpSendRequestW(lpwhr
, NULL
, 0, NULL
, 0, 0, TRUE
);
1098 HeapFree( GetProcessHeap(), 0, new_url
);
1104 iar
.dwResult
= (DWORD_PTR
)lpwhr
->hdr
.hInternet
;
1105 iar
.dwError
= rc
? 0 : INTERNET_GetLastError();
1107 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1108 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
1109 sizeof(INTERNET_ASYNC_RESULT
));
1113 static void AsyncHttpEndRequestProc(WORKREQUEST
*work
)
1115 struct WORKREQ_HTTPENDREQUESTW
const *req
= &work
->u
.HttpEndRequestW
;
1116 http_request_t
*lpwhr
= (http_request_t
*)work
->hdr
;
1118 TRACE("%p\n", lpwhr
);
1120 HTTP_HttpEndRequestW(lpwhr
, req
->dwFlags
, req
->dwContext
);
1123 /***********************************************************************
1124 * HttpEndRequestW (WININET.@)
1126 * Ends an HTTP request that was started by HttpSendRequestEx
1129 * TRUE if successful
1133 BOOL WINAPI
HttpEndRequestW(HINTERNET hRequest
,
1134 LPINTERNET_BUFFERSW lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
1137 http_request_t
*lpwhr
;
1143 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
1147 lpwhr
= (http_request_t
*) WININET_GetObject( hRequest
);
1149 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
1151 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
1153 WININET_Release( &lpwhr
->hdr
);
1156 lpwhr
->hdr
.dwFlags
|= dwFlags
;
1158 if (lpwhr
->lpHttpSession
->lpAppInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
1161 struct WORKREQ_HTTPENDREQUESTW
*request
;
1163 work
.asyncproc
= AsyncHttpEndRequestProc
;
1164 work
.hdr
= WININET_AddRef( &lpwhr
->hdr
);
1166 request
= &work
.u
.HttpEndRequestW
;
1167 request
->dwFlags
= dwFlags
;
1168 request
->dwContext
= dwContext
;
1170 INTERNET_AsyncCall(&work
);
1171 INTERNET_SetLastError(ERROR_IO_PENDING
);
1174 rc
= HTTP_HttpEndRequestW(lpwhr
, dwFlags
, dwContext
);
1176 WININET_Release( &lpwhr
->hdr
);
1177 TRACE("%i <--\n",rc
);
1181 /***********************************************************************
1182 * HttpOpenRequestW (WININET.@)
1184 * Open a HTTP request handle
1187 * HINTERNET a HTTP request handle on success
1191 HINTERNET WINAPI
HttpOpenRequestW(HINTERNET hHttpSession
,
1192 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
1193 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
1194 DWORD dwFlags
, DWORD_PTR dwContext
)
1196 http_session_t
*lpwhs
;
1197 HINTERNET handle
= NULL
;
1199 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
1200 debugstr_w(lpszVerb
), debugstr_w(lpszObjectName
),
1201 debugstr_w(lpszVersion
), debugstr_w(lpszReferrer
), lpszAcceptTypes
,
1202 dwFlags
, dwContext
);
1203 if(lpszAcceptTypes
!=NULL
)
1206 for(i
=0;lpszAcceptTypes
[i
]!=NULL
;i
++)
1207 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes
[i
]));
1210 lpwhs
= (http_session_t
*) WININET_GetObject( hHttpSession
);
1211 if (NULL
== lpwhs
|| lpwhs
->hdr
.htype
!= WH_HHTTPSESSION
)
1213 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
1218 * My tests seem to show that the windows version does not
1219 * become asynchronous until after this point. And anyhow
1220 * if this call was asynchronous then how would you get the
1221 * necessary HINTERNET pointer returned by this function.
1224 handle
= HTTP_HttpOpenRequestW(lpwhs
, lpszVerb
, lpszObjectName
,
1225 lpszVersion
, lpszReferrer
, lpszAcceptTypes
,
1226 dwFlags
, dwContext
);
1229 WININET_Release( &lpwhs
->hdr
);
1230 TRACE("returning %p\n", handle
);
1235 /***********************************************************************
1236 * HttpOpenRequestA (WININET.@)
1238 * Open a HTTP request handle
1241 * HINTERNET a HTTP request handle on success
1245 HINTERNET WINAPI
HttpOpenRequestA(HINTERNET hHttpSession
,
1246 LPCSTR lpszVerb
, LPCSTR lpszObjectName
, LPCSTR lpszVersion
,
1247 LPCSTR lpszReferrer
, LPCSTR
*lpszAcceptTypes
,
1248 DWORD dwFlags
, DWORD_PTR dwContext
)
1250 LPWSTR szVerb
= NULL
, szObjectName
= NULL
;
1251 LPWSTR szVersion
= NULL
, szReferrer
= NULL
, *szAcceptTypes
= NULL
;
1252 INT acceptTypesCount
;
1253 HINTERNET rc
= FALSE
;
1256 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
1257 debugstr_a(lpszVerb
), debugstr_a(lpszObjectName
),
1258 debugstr_a(lpszVersion
), debugstr_a(lpszReferrer
), lpszAcceptTypes
,
1259 dwFlags
, dwContext
);
1263 szVerb
= heap_strdupAtoW(lpszVerb
);
1270 szObjectName
= heap_strdupAtoW(lpszObjectName
);
1271 if ( !szObjectName
)
1277 szVersion
= heap_strdupAtoW(lpszVersion
);
1284 szReferrer
= heap_strdupAtoW(lpszReferrer
);
1289 if (lpszAcceptTypes
)
1291 acceptTypesCount
= 0;
1292 types
= lpszAcceptTypes
;
1297 /* find out how many there are */
1298 if (*types
&& **types
)
1300 TRACE("accept type: %s\n", debugstr_a(*types
));
1306 WARN("invalid accept type pointer\n");
1311 szAcceptTypes
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
*) * (acceptTypesCount
+1));
1312 if (!szAcceptTypes
) goto end
;
1314 acceptTypesCount
= 0;
1315 types
= lpszAcceptTypes
;
1320 if (*types
&& **types
)
1321 szAcceptTypes
[acceptTypesCount
++] = heap_strdupAtoW(*types
);
1325 /* ignore invalid pointer */
1330 szAcceptTypes
[acceptTypesCount
] = NULL
;
1333 rc
= HttpOpenRequestW(hHttpSession
, szVerb
, szObjectName
,
1334 szVersion
, szReferrer
,
1335 (LPCWSTR
*)szAcceptTypes
, dwFlags
, dwContext
);
1340 acceptTypesCount
= 0;
1341 while (szAcceptTypes
[acceptTypesCount
])
1343 HeapFree(GetProcessHeap(), 0, szAcceptTypes
[acceptTypesCount
]);
1346 HeapFree(GetProcessHeap(), 0, szAcceptTypes
);
1348 HeapFree(GetProcessHeap(), 0, szReferrer
);
1349 HeapFree(GetProcessHeap(), 0, szVersion
);
1350 HeapFree(GetProcessHeap(), 0, szObjectName
);
1351 HeapFree(GetProcessHeap(), 0, szVerb
);
1356 /***********************************************************************
1359 static UINT
HTTP_EncodeBase64( LPCSTR bin
, unsigned int len
, LPWSTR base64
)
1362 static const CHAR HTTP_Base64Enc
[] =
1363 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1367 /* first 6 bits, all from bin[0] */
1368 base64
[n
++] = HTTP_Base64Enc
[(bin
[0] & 0xfc) >> 2];
1369 x
= (bin
[0] & 3) << 4;
1371 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1374 base64
[n
++] = HTTP_Base64Enc
[x
];
1379 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[1]&0xf0) >> 4 ) ];
1380 x
= ( bin
[1] & 0x0f ) << 2;
1382 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1385 base64
[n
++] = HTTP_Base64Enc
[x
];
1389 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[2]&0xc0 ) >> 6 ) ];
1391 /* last 6 bits, all from bin [2] */
1392 base64
[n
++] = HTTP_Base64Enc
[ bin
[2] & 0x3f ];
1400 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1401 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1402 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1403 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1404 static const signed char HTTP_Base64Dec
[256] =
1406 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1407 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1408 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1409 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1410 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1411 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1412 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1413 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1414 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1415 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1416 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1417 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1418 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1419 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1420 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1421 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1422 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1423 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1424 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1425 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1426 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1427 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1428 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1429 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1430 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1431 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1435 /***********************************************************************
1438 static UINT
HTTP_DecodeBase64( LPCWSTR base64
, LPSTR bin
)
1446 if (base64
[0] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1447 ((in
[0] = HTTP_Base64Dec
[base64
[0]]) == -1) ||
1448 base64
[1] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1449 ((in
[1] = HTTP_Base64Dec
[base64
[1]]) == -1))
1451 WARN("invalid base64: %s\n", debugstr_w(base64
));
1455 bin
[n
] = (unsigned char) (in
[0] << 2 | in
[1] >> 4);
1458 if ((base64
[2] == '=') && (base64
[3] == '='))
1460 if (base64
[2] > ARRAYSIZE(HTTP_Base64Dec
) ||
1461 ((in
[2] = HTTP_Base64Dec
[base64
[2]]) == -1))
1463 WARN("invalid base64: %s\n", debugstr_w(&base64
[2]));
1467 bin
[n
] = (unsigned char) (in
[1] << 4 | in
[2] >> 2);
1470 if (base64
[3] == '=')
1472 if (base64
[3] > ARRAYSIZE(HTTP_Base64Dec
) ||
1473 ((in
[3] = HTTP_Base64Dec
[base64
[3]]) == -1))
1475 WARN("invalid base64: %s\n", debugstr_w(&base64
[3]));
1479 bin
[n
] = (unsigned char) (((in
[2] << 6) & 0xc0) | in
[3]);
1488 /***********************************************************************
1489 * HTTP_InsertAuthorization
1491 * Insert or delete the authorization field in the request header.
1493 static BOOL
HTTP_InsertAuthorization( http_request_t
*lpwhr
, struct HttpAuthInfo
*pAuthInfo
, LPCWSTR header
)
1497 static const WCHAR wszSpace
[] = {' ',0};
1498 static const WCHAR wszBasic
[] = {'B','a','s','i','c',0};
1500 WCHAR
*authorization
= NULL
;
1502 if (pAuthInfo
->auth_data_len
)
1504 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1505 len
= strlenW(pAuthInfo
->scheme
)+1+((pAuthInfo
->auth_data_len
+2)*4)/3;
1506 authorization
= HeapAlloc(GetProcessHeap(), 0, (len
+1)*sizeof(WCHAR
));
1510 strcpyW(authorization
, pAuthInfo
->scheme
);
1511 strcatW(authorization
, wszSpace
);
1512 HTTP_EncodeBase64(pAuthInfo
->auth_data
,
1513 pAuthInfo
->auth_data_len
,
1514 authorization
+strlenW(authorization
));
1516 /* clear the data as it isn't valid now that it has been sent to the
1517 * server, unless it's Basic authentication which doesn't do
1518 * connection tracking */
1519 if (strcmpiW(pAuthInfo
->scheme
, wszBasic
))
1521 HeapFree(GetProcessHeap(), 0, pAuthInfo
->auth_data
);
1522 pAuthInfo
->auth_data
= NULL
;
1523 pAuthInfo
->auth_data_len
= 0;
1527 TRACE("Inserting authorization: %s\n", debugstr_w(authorization
));
1529 HTTP_ProcessHeader(lpwhr
, header
, authorization
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
1531 HeapFree(GetProcessHeap(), 0, authorization
);
1536 static WCHAR
*HTTP_BuildProxyRequestUrl(http_request_t
*req
)
1538 WCHAR new_location
[INTERNET_MAX_URL_LENGTH
], *url
;
1541 size
= sizeof(new_location
);
1542 if (HTTP_HttpQueryInfoW(req
, HTTP_QUERY_LOCATION
, new_location
, &size
, NULL
))
1544 if (!(url
= HeapAlloc( GetProcessHeap(), 0, size
+ sizeof(WCHAR
) ))) return NULL
;
1545 strcpyW( url
, new_location
);
1549 static const WCHAR slash
[] = { '/',0 };
1550 static const WCHAR format
[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1551 static const WCHAR formatSSL
[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1552 http_session_t
*session
= req
->lpHttpSession
;
1554 size
= 16; /* "https://" + sizeof(port#) + ":/\0" */
1555 size
+= strlenW( session
->lpszHostName
) + strlenW( req
->lpszPath
);
1557 if (!(url
= HeapAlloc( GetProcessHeap(), 0, size
* sizeof(WCHAR
) ))) return NULL
;
1559 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1560 sprintfW( url
, formatSSL
, session
->lpszHostName
, session
->nHostPort
);
1562 sprintfW( url
, format
, session
->lpszHostName
, session
->nHostPort
);
1563 if (req
->lpszPath
[0] != '/') strcatW( url
, slash
);
1564 strcatW( url
, req
->lpszPath
);
1566 TRACE("url=%s\n", debugstr_w(url
));
1570 /***********************************************************************
1571 * HTTP_DealWithProxy
1573 static BOOL
HTTP_DealWithProxy(appinfo_t
*hIC
, http_session_t
*lpwhs
, http_request_t
*lpwhr
)
1575 WCHAR buf
[MAXHOSTNAME
];
1576 WCHAR proxy
[MAXHOSTNAME
+ 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1577 static WCHAR szNul
[] = { 0 };
1578 URL_COMPONENTSW UrlComponents
;
1579 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/',0 };
1580 static const WCHAR szFormat
[] = { 'h','t','t','p',':','/','/','%','s',0 };
1582 memset( &UrlComponents
, 0, sizeof UrlComponents
);
1583 UrlComponents
.dwStructSize
= sizeof UrlComponents
;
1584 UrlComponents
.lpszHostName
= buf
;
1585 UrlComponents
.dwHostNameLength
= MAXHOSTNAME
;
1587 if( CSTR_EQUAL
!= CompareStringW(LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
1588 hIC
->lpszProxy
,strlenW(szHttp
),szHttp
,strlenW(szHttp
)) )
1589 sprintfW(proxy
, szFormat
, hIC
->lpszProxy
);
1591 strcpyW(proxy
, hIC
->lpszProxy
);
1592 if( !InternetCrackUrlW(proxy
, 0, 0, &UrlComponents
) )
1594 if( UrlComponents
.dwHostNameLength
== 0 )
1597 if( !lpwhr
->lpszPath
)
1598 lpwhr
->lpszPath
= szNul
;
1600 if(UrlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
1601 UrlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
1603 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszServerName
);
1604 lpwhs
->lpszServerName
= heap_strdupW(UrlComponents
.lpszHostName
);
1605 lpwhs
->nServerPort
= UrlComponents
.nPort
;
1607 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs
->lpszServerName
), lpwhs
->nServerPort
);
1611 #ifndef INET6_ADDRSTRLEN
1612 #define INET6_ADDRSTRLEN 46
1615 static BOOL
HTTP_ResolveName(http_request_t
*lpwhr
)
1617 char szaddr
[INET6_ADDRSTRLEN
];
1618 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
1621 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1622 INTERNET_STATUS_RESOLVING_NAME
,
1623 lpwhs
->lpszServerName
,
1624 strlenW(lpwhs
->lpszServerName
)+1);
1626 lpwhs
->sa_len
= sizeof(lpwhs
->socketAddress
);
1627 if (!GetAddress(lpwhs
->lpszServerName
, lpwhs
->nServerPort
,
1628 (struct sockaddr
*)&lpwhs
->socketAddress
, &lpwhs
->sa_len
))
1630 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED
);
1634 switch (lpwhs
->socketAddress
.ss_family
)
1637 addr
= &((struct sockaddr_in
*)&lpwhs
->socketAddress
)->sin_addr
;
1640 addr
= &((struct sockaddr_in6
*)&lpwhs
->socketAddress
)->sin6_addr
;
1643 WARN("unsupported family %d\n", lpwhs
->socketAddress
.ss_family
);
1644 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED
);
1647 inet_ntop(lpwhs
->socketAddress
.ss_family
, addr
, szaddr
, sizeof(szaddr
));
1648 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1649 INTERNET_STATUS_NAME_RESOLVED
,
1650 szaddr
, strlen(szaddr
)+1);
1652 TRACE("resolved %s to %s\n", debugstr_w(lpwhs
->lpszServerName
), szaddr
);
1657 /***********************************************************************
1658 * HTTPREQ_Destroy (internal)
1660 * Deallocate request handle
1663 static void HTTPREQ_Destroy(object_header_t
*hdr
)
1665 http_request_t
*lpwhr
= (http_request_t
*) hdr
;
1670 if(lpwhr
->hCacheFile
)
1671 CloseHandle(lpwhr
->hCacheFile
);
1673 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszCacheFile
);
1675 DeleteCriticalSection( &lpwhr
->read_section
);
1676 WININET_Release(&lpwhr
->lpHttpSession
->hdr
);
1678 destroy_authinfo(lpwhr
->pAuthInfo
);
1679 destroy_authinfo(lpwhr
->pProxyAuthInfo
);
1681 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszPath
);
1682 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVerb
);
1683 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszRawHeaders
);
1684 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVersion
);
1685 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszStatusText
);
1687 for (i
= 0; i
< lpwhr
->nCustHeaders
; i
++)
1689 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[i
].lpszField
);
1690 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[i
].lpszValue
);
1694 if(lpwhr
->gzip_stream
) {
1695 if(!lpwhr
->gzip_stream
->end_of_data
)
1696 inflateEnd(&lpwhr
->gzip_stream
->zstream
);
1697 HeapFree(GetProcessHeap(), 0, lpwhr
->gzip_stream
);
1701 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
);
1702 HeapFree(GetProcessHeap(), 0, lpwhr
);
1705 static void HTTPREQ_CloseConnection(object_header_t
*hdr
)
1707 http_request_t
*lpwhr
= (http_request_t
*) hdr
;
1709 TRACE("%p\n",lpwhr
);
1711 if (!NETCON_connected(&lpwhr
->netConnection
))
1714 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1715 INTERNET_STATUS_CLOSING_CONNECTION
, 0, 0);
1717 NETCON_close(&lpwhr
->netConnection
);
1719 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
1720 INTERNET_STATUS_CONNECTION_CLOSED
, 0, 0);
1723 static BOOL
HTTP_GetRequestURL(http_request_t
*req
, LPWSTR buf
)
1725 LPHTTPHEADERW host_header
;
1727 static const WCHAR formatW
[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1729 host_header
= HTTP_GetHeader(req
, hostW
);
1733 sprintfW(buf
, formatW
, host_header
->lpszValue
, req
->lpszPath
); /* FIXME */
1737 static BOOL
HTTP_KeepAlive(http_request_t
*lpwhr
)
1739 WCHAR szVersion
[10];
1740 WCHAR szConnectionResponse
[20];
1741 DWORD dwBufferSize
= sizeof(szVersion
);
1742 BOOL keepalive
= FALSE
;
1744 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1745 * the connection is keep-alive by default */
1746 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_VERSION
, szVersion
,
1747 &dwBufferSize
, NULL
) &&
1748 !strcmpiW(szVersion
, g_szHttp1_1
))
1753 dwBufferSize
= sizeof(szConnectionResponse
);
1754 if (HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_PROXY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) ||
1755 HTTP_HttpQueryInfoW(lpwhr
, HTTP_QUERY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
))
1757 keepalive
= !strcmpiW(szConnectionResponse
, szKeepAlive
);
1763 static DWORD
HTTPREQ_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
1765 http_request_t
*req
= (http_request_t
*)hdr
;
1768 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO
:
1770 http_session_t
*lpwhs
= req
->lpHttpSession
;
1771 INTERNET_DIAGNOSTIC_SOCKET_INFO
*info
= buffer
;
1773 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1775 if (*size
< sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
))
1776 return ERROR_INSUFFICIENT_BUFFER
;
1777 *size
= sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
);
1778 /* FIXME: can't get a SOCKET from our connection since we don't use
1782 /* FIXME: get source port from req->netConnection */
1783 info
->SourcePort
= 0;
1784 info
->DestPort
= lpwhs
->nHostPort
;
1786 if (HTTP_KeepAlive(req
))
1787 info
->Flags
|= IDSI_FLAG_KEEP_ALIVE
;
1788 if (lpwhs
->lpAppInfo
->lpszProxy
&& lpwhs
->lpAppInfo
->lpszProxy
[0] != 0)
1789 info
->Flags
|= IDSI_FLAG_PROXY
;
1790 if (req
->netConnection
.useSSL
)
1791 info
->Flags
|= IDSI_FLAG_SECURE
;
1793 return ERROR_SUCCESS
;
1796 case INTERNET_OPTION_SECURITY_FLAGS
:
1798 http_session_t
*lpwhs
;
1799 lpwhs
= req
->lpHttpSession
;
1801 if (*size
< sizeof(ULONG
))
1802 return ERROR_INSUFFICIENT_BUFFER
;
1804 *size
= sizeof(DWORD
);
1805 if (lpwhs
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1806 *(DWORD
*)buffer
= SECURITY_FLAG_SECURE
;
1808 *(DWORD
*)buffer
= 0;
1809 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD
*)buffer
);
1810 return ERROR_SUCCESS
;
1813 case INTERNET_OPTION_HANDLE_TYPE
:
1814 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1816 if (*size
< sizeof(ULONG
))
1817 return ERROR_INSUFFICIENT_BUFFER
;
1819 *size
= sizeof(DWORD
);
1820 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_HTTP_REQUEST
;
1821 return ERROR_SUCCESS
;
1823 case INTERNET_OPTION_URL
: {
1824 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1829 static const WCHAR httpW
[] = {'h','t','t','p',':','/','/',0};
1831 TRACE("INTERNET_OPTION_URL\n");
1833 host
= HTTP_GetHeader(req
, hostW
);
1834 strcpyW(url
, httpW
);
1835 strcatW(url
, host
->lpszValue
);
1836 if (NULL
!= (pch
= strchrW(url
+ strlenW(httpW
), ':')))
1838 strcatW(url
, req
->lpszPath
);
1840 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url
));
1843 len
= (strlenW(url
)+1) * sizeof(WCHAR
);
1845 return ERROR_INSUFFICIENT_BUFFER
;
1848 strcpyW(buffer
, url
);
1849 return ERROR_SUCCESS
;
1851 len
= WideCharToMultiByte(CP_ACP
, 0, url
, -1, buffer
, *size
, NULL
, NULL
);
1853 return ERROR_INSUFFICIENT_BUFFER
;
1856 return ERROR_SUCCESS
;
1860 case INTERNET_OPTION_CACHE_TIMESTAMPS
: {
1861 INTERNET_CACHE_ENTRY_INFOW
*info
;
1862 INTERNET_CACHE_TIMESTAMPS
*ts
= buffer
;
1863 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1864 DWORD nbytes
, error
;
1867 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1869 if (*size
< sizeof(*ts
))
1871 *size
= sizeof(*ts
);
1872 return ERROR_INSUFFICIENT_BUFFER
;
1875 HTTP_GetRequestURL(req
, url
);
1876 ret
= GetUrlCacheEntryInfoW(url
, NULL
, &nbytes
);
1877 error
= GetLastError();
1878 if (!ret
&& error
== ERROR_INSUFFICIENT_BUFFER
)
1880 if (!(info
= HeapAlloc(GetProcessHeap(), 0, nbytes
)))
1881 return ERROR_OUTOFMEMORY
;
1883 GetUrlCacheEntryInfoW(url
, info
, &nbytes
);
1885 ts
->ftExpires
= info
->ExpireTime
;
1886 ts
->ftLastModified
= info
->LastModifiedTime
;
1888 HeapFree(GetProcessHeap(), 0, info
);
1889 *size
= sizeof(*ts
);
1890 return ERROR_SUCCESS
;
1895 case INTERNET_OPTION_DATAFILE_NAME
: {
1898 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1900 if(!req
->lpszCacheFile
) {
1902 return ERROR_INTERNET_ITEM_NOT_FOUND
;
1906 req_size
= (lstrlenW(req
->lpszCacheFile
)+1) * sizeof(WCHAR
);
1907 if(*size
< req_size
)
1908 return ERROR_INSUFFICIENT_BUFFER
;
1911 memcpy(buffer
, req
->lpszCacheFile
, *size
);
1912 return ERROR_SUCCESS
;
1914 req_size
= WideCharToMultiByte(CP_ACP
, 0, req
->lpszCacheFile
, -1, NULL
, 0, NULL
, NULL
);
1915 if (req_size
> *size
)
1916 return ERROR_INSUFFICIENT_BUFFER
;
1918 *size
= WideCharToMultiByte(CP_ACP
, 0, req
->lpszCacheFile
,
1919 -1, buffer
, *size
, NULL
, NULL
);
1920 return ERROR_SUCCESS
;
1924 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT
: {
1925 PCCERT_CONTEXT context
;
1927 if(*size
< sizeof(INTERNET_CERTIFICATE_INFOW
)) {
1928 *size
= sizeof(INTERNET_CERTIFICATE_INFOW
);
1929 return ERROR_INSUFFICIENT_BUFFER
;
1932 context
= (PCCERT_CONTEXT
)NETCON_GetCert(&(req
->netConnection
));
1934 INTERNET_CERTIFICATE_INFOW
*info
= (INTERNET_CERTIFICATE_INFOW
*)buffer
;
1937 memset(info
, 0, sizeof(INTERNET_CERTIFICATE_INFOW
));
1938 info
->ftExpiry
= context
->pCertInfo
->NotAfter
;
1939 info
->ftStart
= context
->pCertInfo
->NotBefore
;
1941 len
= CertNameToStrW(context
->dwCertEncodingType
,
1942 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1943 info
->lpszSubjectInfo
= LocalAlloc(0, len
*sizeof(WCHAR
));
1944 if(info
->lpszSubjectInfo
)
1945 CertNameToStrW(context
->dwCertEncodingType
,
1946 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
,
1947 info
->lpszSubjectInfo
, len
);
1948 len
= CertNameToStrW(context
->dwCertEncodingType
,
1949 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1950 info
->lpszIssuerInfo
= LocalAlloc(0, len
*sizeof(WCHAR
));
1951 if (info
->lpszIssuerInfo
)
1952 CertNameToStrW(context
->dwCertEncodingType
,
1953 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
,
1954 info
->lpszIssuerInfo
, len
);
1956 INTERNET_CERTIFICATE_INFOA
*infoA
= (INTERNET_CERTIFICATE_INFOA
*)info
;
1958 len
= CertNameToStrA(context
->dwCertEncodingType
,
1959 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1960 infoA
->lpszSubjectInfo
= LocalAlloc(0, len
);
1961 if(infoA
->lpszSubjectInfo
)
1962 CertNameToStrA(context
->dwCertEncodingType
,
1963 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
,
1964 infoA
->lpszSubjectInfo
, len
);
1965 len
= CertNameToStrA(context
->dwCertEncodingType
,
1966 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
, NULL
, 0);
1967 infoA
->lpszIssuerInfo
= LocalAlloc(0, len
);
1968 if(infoA
->lpszIssuerInfo
)
1969 CertNameToStrA(context
->dwCertEncodingType
,
1970 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
,
1971 infoA
->lpszIssuerInfo
, len
);
1975 * Contrary to MSDN, these do not appear to be set.
1977 * lpszSignatureAlgName
1978 * lpszEncryptionAlgName
1981 CertFreeCertificateContext(context
);
1982 return ERROR_SUCCESS
;
1987 return INET_QueryOption(option
, buffer
, size
, unicode
);
1990 static DWORD
HTTPREQ_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
1992 http_request_t
*req
= (http_request_t
*)hdr
;
1995 case INTERNET_OPTION_SEND_TIMEOUT
:
1996 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
1997 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1999 if (size
!= sizeof(DWORD
))
2000 return ERROR_INVALID_PARAMETER
;
2002 return NETCON_set_timeout(&req
->netConnection
, option
== INTERNET_OPTION_SEND_TIMEOUT
,
2005 case INTERNET_OPTION_USERNAME
:
2006 HeapFree(GetProcessHeap(), 0, req
->lpHttpSession
->lpszUserName
);
2007 if (!(req
->lpHttpSession
->lpszUserName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
2008 return ERROR_SUCCESS
;
2010 case INTERNET_OPTION_PASSWORD
:
2011 HeapFree(GetProcessHeap(), 0, req
->lpHttpSession
->lpszPassword
);
2012 if (!(req
->lpHttpSession
->lpszPassword
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
2013 return ERROR_SUCCESS
;
2014 case INTERNET_OPTION_HTTP_DECODING
:
2015 if(size
!= sizeof(BOOL
))
2016 return ERROR_INVALID_PARAMETER
;
2017 req
->decoding
= *(BOOL
*)buffer
;
2018 return ERROR_SUCCESS
;
2021 return ERROR_INTERNET_INVALID_OPTION
;
2024 /* read some more data into the read buffer (the read section must be held) */
2025 static BOOL
read_more_data( http_request_t
*req
, int maxlen
)
2031 /* move existing data to the start of the buffer */
2033 memmove( req
->read_buf
, req
->read_buf
+ req
->read_pos
, req
->read_size
);
2037 if (maxlen
== -1) maxlen
= sizeof(req
->read_buf
);
2039 if(!NETCON_recv( &req
->netConnection
, req
->read_buf
+ req
->read_size
,
2040 maxlen
- req
->read_size
, 0, &len
))
2043 req
->read_size
+= len
;
2047 /* remove some amount of data from the read buffer (the read section must be held) */
2048 static void remove_data( http_request_t
*req
, int count
)
2050 if (!(req
->read_size
-= count
)) req
->read_pos
= 0;
2051 else req
->read_pos
+= count
;
2054 static BOOL
read_line( http_request_t
*req
, LPSTR buffer
, DWORD
*len
)
2056 int count
, bytes_read
, pos
= 0;
2058 EnterCriticalSection( &req
->read_section
);
2061 BYTE
*eol
= memchr( req
->read_buf
+ req
->read_pos
, '\n', req
->read_size
);
2065 count
= eol
- (req
->read_buf
+ req
->read_pos
);
2066 bytes_read
= count
+ 1;
2068 else count
= bytes_read
= req
->read_size
;
2070 count
= min( count
, *len
- pos
);
2071 memcpy( buffer
+ pos
, req
->read_buf
+ req
->read_pos
, count
);
2073 remove_data( req
, bytes_read
);
2076 if (!read_more_data( req
, -1 ) || !req
->read_size
)
2079 TRACE( "returning empty string\n" );
2080 LeaveCriticalSection( &req
->read_section
);
2084 LeaveCriticalSection( &req
->read_section
);
2088 if (pos
&& buffer
[pos
- 1] == '\r') pos
--;
2091 buffer
[*len
- 1] = 0;
2092 TRACE( "returning %s\n", debugstr_a(buffer
));
2096 /* discard data contents until we reach end of line (the read section must be held) */
2097 static BOOL
discard_eol( http_request_t
*req
)
2101 BYTE
*eol
= memchr( req
->read_buf
+ req
->read_pos
, '\n', req
->read_size
);
2104 remove_data( req
, (eol
+ 1) - (req
->read_buf
+ req
->read_pos
) );
2107 req
->read_pos
= req
->read_size
= 0; /* discard everything */
2108 if (!read_more_data( req
, -1 )) return FALSE
;
2109 } while (req
->read_size
);
2113 /* read the size of the next chunk (the read section must be held) */
2114 static BOOL
start_next_chunk( http_request_t
*req
)
2116 DWORD chunk_size
= 0;
2118 if (!req
->dwContentLength
) return TRUE
;
2119 if (req
->dwContentLength
== req
->dwContentRead
)
2121 /* read terminator for the previous chunk */
2122 if (!discard_eol( req
)) return FALSE
;
2123 req
->dwContentLength
= ~0u;
2124 req
->dwContentRead
= 0;
2128 while (req
->read_size
)
2130 char ch
= req
->read_buf
[req
->read_pos
];
2131 if (ch
>= '0' && ch
<= '9') chunk_size
= chunk_size
* 16 + ch
- '0';
2132 else if (ch
>= 'a' && ch
<= 'f') chunk_size
= chunk_size
* 16 + ch
- 'a' + 10;
2133 else if (ch
>= 'A' && ch
<= 'F') chunk_size
= chunk_size
* 16 + ch
- 'A' + 10;
2134 else if (ch
== ';' || ch
== '\r' || ch
== '\n')
2136 TRACE( "reading %u byte chunk\n", chunk_size
);
2137 req
->dwContentLength
= chunk_size
;
2138 req
->dwContentRead
= 0;
2139 if (!discard_eol( req
)) return FALSE
;
2142 remove_data( req
, 1 );
2144 if (!read_more_data( req
, -1 )) return FALSE
;
2145 if (!req
->read_size
)
2147 req
->dwContentLength
= req
->dwContentRead
= 0;
2153 /* check if we have reached the end of the data to read (the read section must be held) */
2154 static BOOL
end_of_read_data( http_request_t
*req
)
2156 if (req
->gzip_stream
) return req
->gzip_stream
->end_of_data
&& !req
->gzip_stream
->buf_size
;
2157 if (req
->read_chunked
) return (req
->dwContentLength
== 0);
2158 if (req
->dwContentLength
== ~0u) return FALSE
;
2159 return (req
->dwContentLength
== req
->dwContentRead
);
2162 /* fetch some more data into the read buffer (the read section must be held) */
2163 static BOOL
refill_buffer( http_request_t
*req
)
2165 int len
= sizeof(req
->read_buf
);
2167 if (req
->read_chunked
&& (req
->dwContentLength
== ~0u || req
->dwContentLength
== req
->dwContentRead
))
2169 if (!start_next_chunk( req
)) return FALSE
;
2172 if (req
->dwContentLength
!= ~0u) len
= min( len
, req
->dwContentLength
- req
->dwContentRead
);
2173 if (len
<= req
->read_size
) return TRUE
;
2175 if (!read_more_data( req
, len
)) return FALSE
;
2176 if (!req
->read_size
) req
->dwContentLength
= req
->dwContentRead
= 0;
2180 static DWORD
read_gzip_data(http_request_t
*req
, BYTE
*buf
, int size
, BOOL sync
, int *read_ret
)
2182 DWORD ret
= ERROR_SUCCESS
;
2186 z_stream
*zstream
= &req
->gzip_stream
->zstream
;
2189 while(read
< size
&& !req
->gzip_stream
->end_of_data
) {
2190 if(!req
->read_size
) {
2191 if(!sync
|| !refill_buffer(req
))
2195 zstream
->next_in
= req
->read_buf
+req
->read_pos
;
2196 zstream
->avail_in
= req
->read_size
;
2197 zstream
->next_out
= buf
+read
;
2198 zstream
->avail_out
= size
-read
;
2199 zres
= inflate(zstream
, Z_FULL_FLUSH
);
2200 read
= size
- zstream
->avail_out
;
2201 remove_data(req
, req
->read_size
-zstream
->avail_in
);
2202 if(zres
== Z_STREAM_END
) {
2203 TRACE("end of data\n");
2204 req
->gzip_stream
->end_of_data
= TRUE
;
2205 inflateEnd(&req
->gzip_stream
->zstream
);
2206 }else if(zres
!= Z_OK
) {
2207 WARN("inflate failed %d\n", zres
);
2209 ret
= ERROR_INTERNET_DECODING_FAILED
;
2219 static void refill_gzip_buffer(http_request_t
*req
)
2224 if(!req
->gzip_stream
|| !req
->read_size
|| req
->gzip_stream
->buf_size
== sizeof(req
->gzip_stream
->buf
))
2227 if(req
->gzip_stream
->buf_pos
) {
2228 if(req
->gzip_stream
->buf_size
)
2229 memmove(req
->gzip_stream
->buf
, req
->gzip_stream
->buf
+ req
->gzip_stream
->buf_pos
, req
->gzip_stream
->buf_size
);
2230 req
->gzip_stream
->buf_pos
= 0;
2233 res
= read_gzip_data(req
, req
->gzip_stream
->buf
+ req
->gzip_stream
->buf_size
,
2234 sizeof(req
->gzip_stream
->buf
) - req
->gzip_stream
->buf_size
, FALSE
, &len
);
2235 if(res
== ERROR_SUCCESS
)
2236 req
->gzip_stream
->buf_size
+= len
;
2239 /* return the size of data available to be read immediately (the read section must be held) */
2240 static DWORD
get_avail_data( http_request_t
*req
)
2242 if (req
->gzip_stream
) {
2243 refill_gzip_buffer(req
);
2244 return req
->gzip_stream
->buf_size
;
2246 if (req
->read_chunked
&& (req
->dwContentLength
== ~0u || req
->dwContentLength
== req
->dwContentRead
))
2248 return min( req
->read_size
, req
->dwContentLength
- req
->dwContentRead
);
2251 static void HTTP_ReceiveRequestData(http_request_t
*req
, BOOL first_notif
)
2253 INTERNET_ASYNC_RESULT iar
;
2257 EnterCriticalSection( &req
->read_section
);
2258 if (refill_buffer( req
)) {
2259 iar
.dwResult
= (DWORD_PTR
)req
->hdr
.hInternet
;
2260 iar
.dwError
= first_notif
? 0 : get_avail_data(req
);
2263 iar
.dwError
= INTERNET_GetLastError();
2265 LeaveCriticalSection( &req
->read_section
);
2267 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2268 sizeof(INTERNET_ASYNC_RESULT
));
2271 /* read data from the http connection (the read section must be held) */
2272 static DWORD
HTTPREQ_Read(http_request_t
*req
, void *buffer
, DWORD size
, DWORD
*read
, BOOL sync
)
2274 BOOL finished_reading
= FALSE
;
2275 int len
, bytes_read
= 0;
2276 DWORD ret
= ERROR_SUCCESS
;
2278 EnterCriticalSection( &req
->read_section
);
2280 if (req
->read_chunked
&& (req
->dwContentLength
== ~0u || req
->dwContentLength
== req
->dwContentRead
))
2282 if (!start_next_chunk( req
)) goto done
;
2285 if(req
->gzip_stream
) {
2286 if(req
->gzip_stream
->buf_size
) {
2287 bytes_read
= min(req
->gzip_stream
->buf_size
, size
);
2288 memcpy(buffer
, req
->gzip_stream
->buf
+ req
->gzip_stream
->buf_pos
, bytes_read
);
2289 req
->gzip_stream
->buf_pos
+= bytes_read
;
2290 req
->gzip_stream
->buf_size
-= bytes_read
;
2291 }else if(!req
->read_size
&& !req
->gzip_stream
->end_of_data
) {
2295 if(size
> bytes_read
) {
2296 ret
= read_gzip_data(req
, (BYTE
*)buffer
+bytes_read
, size
-bytes_read
, sync
, &len
);
2297 if(ret
== ERROR_SUCCESS
)
2301 finished_reading
= req
->gzip_stream
->end_of_data
&& !req
->gzip_stream
->buf_size
;
2303 if (req
->dwContentLength
!= ~0u) size
= min( size
, req
->dwContentLength
- req
->dwContentRead
);
2305 if (req
->read_size
) {
2306 bytes_read
= min( req
->read_size
, size
);
2307 memcpy( buffer
, req
->read_buf
+ req
->read_pos
, bytes_read
);
2308 remove_data( req
, bytes_read
);
2311 if (size
> bytes_read
&& (!bytes_read
|| sync
)) {
2312 if (NETCON_recv( &req
->netConnection
, (char *)buffer
+ bytes_read
, size
- bytes_read
,
2313 sync
? MSG_WAITALL
: 0, &len
))
2315 /* always return success, even if the network layer returns an error */
2318 finished_reading
= !bytes_read
&& req
->dwContentRead
== req
->dwContentLength
;
2321 req
->dwContentRead
+= bytes_read
;
2324 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read
, req
->dwContentRead
, req
->dwContentLength
);
2325 LeaveCriticalSection( &req
->read_section
);
2327 if(ret
== ERROR_SUCCESS
&& req
->lpszCacheFile
) {
2329 DWORD dwBytesWritten
;
2331 res
= WriteFile(req
->hCacheFile
, buffer
, bytes_read
, &dwBytesWritten
, NULL
);
2333 WARN("WriteFile failed: %u\n", GetLastError());
2336 if(finished_reading
)
2337 HTTP_FinishedReading(req
);
2343 static DWORD
HTTPREQ_ReadFile(object_header_t
*hdr
, void *buffer
, DWORD size
, DWORD
*read
)
2345 http_request_t
*req
= (http_request_t
*)hdr
;
2346 return HTTPREQ_Read(req
, buffer
, size
, read
, TRUE
);
2349 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST
*workRequest
)
2351 struct WORKREQ_INTERNETREADFILEEXA
const *data
= &workRequest
->u
.InternetReadFileExA
;
2352 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2353 INTERNET_ASYNC_RESULT iar
;
2356 TRACE("INTERNETREADFILEEXA %p\n", workRequest
->hdr
);
2358 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2359 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2361 iar
.dwResult
= res
== ERROR_SUCCESS
;
2364 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2365 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2366 sizeof(INTERNET_ASYNC_RESULT
));
2369 static DWORD
HTTPREQ_ReadFileExA(object_header_t
*hdr
, INTERNET_BUFFERSA
*buffers
,
2370 DWORD flags
, DWORD_PTR context
)
2372 http_request_t
*req
= (http_request_t
*)hdr
;
2375 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2376 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2378 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2379 return ERROR_INVALID_PARAMETER
;
2381 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2383 if ((hdr
->dwFlags
& INTERNET_FLAG_ASYNC
) && !get_avail_data(req
))
2385 WORKREQUEST workRequest
;
2387 if (TryEnterCriticalSection( &req
->read_section
))
2389 if (get_avail_data(req
))
2391 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2392 &buffers
->dwBufferLength
, FALSE
);
2393 LeaveCriticalSection( &req
->read_section
);
2396 LeaveCriticalSection( &req
->read_section
);
2399 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExAProc
;
2400 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2401 workRequest
.u
.InternetReadFileExA
.lpBuffersOut
= buffers
;
2403 INTERNET_AsyncCall(&workRequest
);
2405 return ERROR_IO_PENDING
;
2408 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
, &buffers
->dwBufferLength
,
2409 !(flags
& IRF_NO_WAIT
));
2412 if (res
== ERROR_SUCCESS
) {
2413 DWORD size
= buffers
->dwBufferLength
;
2414 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2415 &size
, sizeof(size
));
2421 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST
*workRequest
)
2423 struct WORKREQ_INTERNETREADFILEEXW
const *data
= &workRequest
->u
.InternetReadFileExW
;
2424 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2425 INTERNET_ASYNC_RESULT iar
;
2428 TRACE("INTERNETREADFILEEXW %p\n", workRequest
->hdr
);
2430 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2431 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2433 iar
.dwResult
= res
== ERROR_SUCCESS
;
2436 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2437 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2438 sizeof(INTERNET_ASYNC_RESULT
));
2441 static DWORD
HTTPREQ_ReadFileExW(object_header_t
*hdr
, INTERNET_BUFFERSW
*buffers
,
2442 DWORD flags
, DWORD_PTR context
)
2445 http_request_t
*req
= (http_request_t
*)hdr
;
2448 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2449 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2451 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2452 return ERROR_INVALID_PARAMETER
;
2454 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2456 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2458 WORKREQUEST workRequest
;
2460 if (TryEnterCriticalSection( &req
->read_section
))
2462 if (get_avail_data(req
))
2464 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2465 &buffers
->dwBufferLength
, FALSE
);
2466 LeaveCriticalSection( &req
->read_section
);
2469 LeaveCriticalSection( &req
->read_section
);
2472 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExWProc
;
2473 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2474 workRequest
.u
.InternetReadFileExW
.lpBuffersOut
= buffers
;
2476 INTERNET_AsyncCall(&workRequest
);
2478 return ERROR_IO_PENDING
;
2481 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
, &buffers
->dwBufferLength
,
2482 !(flags
& IRF_NO_WAIT
));
2485 if (res
== ERROR_SUCCESS
) {
2486 DWORD size
= buffers
->dwBufferLength
;
2487 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2488 &size
, sizeof(size
));
2494 static BOOL
HTTPREQ_WriteFile(object_header_t
*hdr
, const void *buffer
, DWORD size
, DWORD
*written
)
2497 http_request_t
*lpwhr
= (http_request_t
*)hdr
;
2499 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
2502 if ((ret
= NETCON_send(&lpwhr
->netConnection
, buffer
, size
, 0, (LPINT
)written
)))
2503 lpwhr
->dwBytesWritten
+= *written
;
2505 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->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
->lpHttpSession
->lpAppInfo
->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
->dwContentLength
- req
->dwContentRead
);
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 HINTERNET WINAPI
HTTP_HttpOpenRequestW(http_session_t
*lpwhs
,
2587 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
2588 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
2589 DWORD dwFlags
, DWORD_PTR dwContext
)
2591 appinfo_t
*hIC
= NULL
;
2592 http_request_t
*lpwhr
;
2593 LPWSTR lpszHostName
= NULL
;
2594 HINTERNET handle
= NULL
;
2595 static const WCHAR szHostForm
[] = {'%','s',':','%','u',0};
2600 assert( lpwhs
->hdr
.htype
== WH_HHTTPSESSION
);
2601 hIC
= lpwhs
->lpAppInfo
;
2603 lpwhr
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(http_request_t
));
2606 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
2609 lpwhr
->hdr
.htype
= WH_HHTTPREQ
;
2610 lpwhr
->hdr
.vtbl
= &HTTPREQVtbl
;
2611 lpwhr
->hdr
.dwFlags
= dwFlags
;
2612 lpwhr
->hdr
.dwContext
= dwContext
;
2613 lpwhr
->hdr
.refs
= 1;
2614 lpwhr
->hdr
.lpfnStatusCB
= lpwhs
->hdr
.lpfnStatusCB
;
2615 lpwhr
->hdr
.dwInternalFlags
= lpwhs
->hdr
.dwInternalFlags
& INET_CALLBACKW
;
2616 lpwhr
->dwContentLength
= ~0u;
2617 InitializeCriticalSection( &lpwhr
->read_section
);
2619 WININET_AddRef( &lpwhs
->hdr
);
2620 lpwhr
->lpHttpSession
= lpwhs
;
2621 list_add_head( &lpwhs
->hdr
.children
, &lpwhr
->hdr
.entry
);
2623 lpszHostName
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
) *
2624 (strlenW(lpwhs
->lpszHostName
) + 7 /* length of ":65535" + 1 */));
2625 if (NULL
== lpszHostName
)
2627 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
2631 handle
= WININET_AllocHandle( &lpwhr
->hdr
);
2634 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
2638 if (!NETCON_init(&lpwhr
->netConnection
, dwFlags
& INTERNET_FLAG_SECURE
))
2640 InternetCloseHandle( handle
);
2645 if (lpszObjectName
&& *lpszObjectName
) {
2649 rc
= UrlEscapeW(lpszObjectName
, NULL
, &len
, URL_ESCAPE_SPACES_ONLY
);
2650 if (rc
!= E_POINTER
)
2651 len
= strlenW(lpszObjectName
)+1;
2652 lpwhr
->lpszPath
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
2653 rc
= UrlEscapeW(lpszObjectName
, lpwhr
->lpszPath
, &len
,
2654 URL_ESCAPE_SPACES_ONLY
);
2657 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName
),rc
);
2658 strcpyW(lpwhr
->lpszPath
,lpszObjectName
);
2661 static const WCHAR slashW
[] = {'/',0};
2663 lpwhr
->lpszPath
= heap_strdupW(slashW
);
2666 if (lpszReferrer
&& *lpszReferrer
)
2667 HTTP_ProcessHeader(lpwhr
, HTTP_REFERER
, lpszReferrer
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2669 if (lpszAcceptTypes
)
2672 for (i
= 0; lpszAcceptTypes
[i
]; i
++)
2674 if (!*lpszAcceptTypes
[i
]) continue;
2675 HTTP_ProcessHeader(lpwhr
, HTTP_ACCEPT
, lpszAcceptTypes
[i
],
2676 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
|
2677 HTTP_ADDHDR_FLAG_REQ
|
2678 (i
== 0 ? HTTP_ADDHDR_FLAG_REPLACE
: 0));
2682 lpwhr
->lpszVerb
= heap_strdupW(lpszVerb
&& *lpszVerb
? lpszVerb
: szGET
);
2683 lpwhr
->lpszVersion
= heap_strdupW(lpszVersion
? lpszVersion
: g_szHttp1_1
);
2685 if (lpwhs
->nHostPort
!= INTERNET_INVALID_PORT_NUMBER
&&
2686 lpwhs
->nHostPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
2687 lpwhs
->nHostPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
2689 sprintfW(lpszHostName
, szHostForm
, lpwhs
->lpszHostName
, lpwhs
->nHostPort
);
2690 HTTP_ProcessHeader(lpwhr
, hostW
, lpszHostName
,
2691 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2694 HTTP_ProcessHeader(lpwhr
, hostW
, lpwhs
->lpszHostName
,
2695 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
2697 if (lpwhs
->nServerPort
== INTERNET_INVALID_PORT_NUMBER
)
2698 lpwhs
->nServerPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
2699 INTERNET_DEFAULT_HTTPS_PORT
:
2700 INTERNET_DEFAULT_HTTP_PORT
);
2702 if (lpwhs
->nHostPort
== INTERNET_INVALID_PORT_NUMBER
)
2703 lpwhs
->nHostPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
2704 INTERNET_DEFAULT_HTTPS_PORT
:
2705 INTERNET_DEFAULT_HTTP_PORT
);
2707 if (NULL
!= hIC
->lpszProxy
&& hIC
->lpszProxy
[0] != 0)
2708 HTTP_DealWithProxy( hIC
, lpwhs
, lpwhr
);
2710 INTERNET_SendCallback(&lpwhs
->hdr
, dwContext
,
2711 INTERNET_STATUS_HANDLE_CREATED
, &handle
,
2715 HeapFree(GetProcessHeap(), 0, lpszHostName
);
2717 WININET_Release( &lpwhr
->hdr
);
2719 TRACE("<-- %p (%p)\n", handle
, lpwhr
);
2723 /* read any content returned by the server so that the connection can be
2725 static void HTTP_DrainContent(http_request_t
*req
)
2729 if (!NETCON_connected(&req
->netConnection
)) return;
2731 if (req
->dwContentLength
== -1)
2733 NETCON_close(&req
->netConnection
);
2736 if (!strcmpW(req
->lpszVerb
, szHEAD
)) return;
2741 if (HTTPREQ_Read(req
, buffer
, sizeof(buffer
), &bytes_read
, TRUE
) != ERROR_SUCCESS
)
2743 } while (bytes_read
);
2746 static const LPCWSTR header_lookup
[] = {
2747 szMime_Version
, /* HTTP_QUERY_MIME_VERSION = 0 */
2748 szContent_Type
, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2749 szContent_Transfer_Encoding
,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2750 szContent_ID
, /* HTTP_QUERY_CONTENT_ID = 3 */
2751 NULL
, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2752 szContent_Length
, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2753 szContent_Language
, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2754 szAllow
, /* HTTP_QUERY_ALLOW = 7 */
2755 szPublic
, /* HTTP_QUERY_PUBLIC = 8 */
2756 szDate
, /* HTTP_QUERY_DATE = 9 */
2757 szExpires
, /* HTTP_QUERY_EXPIRES = 10 */
2758 szLast_Modified
, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2759 NULL
, /* HTTP_QUERY_MESSAGE_ID = 12 */
2760 szURI
, /* HTTP_QUERY_URI = 13 */
2761 szFrom
, /* HTTP_QUERY_DERIVED_FROM = 14 */
2762 NULL
, /* HTTP_QUERY_COST = 15 */
2763 NULL
, /* HTTP_QUERY_LINK = 16 */
2764 szPragma
, /* HTTP_QUERY_PRAGMA = 17 */
2765 NULL
, /* HTTP_QUERY_VERSION = 18 */
2766 szStatus
, /* HTTP_QUERY_STATUS_CODE = 19 */
2767 NULL
, /* HTTP_QUERY_STATUS_TEXT = 20 */
2768 NULL
, /* HTTP_QUERY_RAW_HEADERS = 21 */
2769 NULL
, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2770 szConnection
, /* HTTP_QUERY_CONNECTION = 23 */
2771 szAccept
, /* HTTP_QUERY_ACCEPT = 24 */
2772 szAccept_Charset
, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2773 szAccept_Encoding
, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2774 szAccept_Language
, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2775 szAuthorization
, /* HTTP_QUERY_AUTHORIZATION = 28 */
2776 szContent_Encoding
, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2777 NULL
, /* HTTP_QUERY_FORWARDED = 30 */
2778 NULL
, /* HTTP_QUERY_FROM = 31 */
2779 szIf_Modified_Since
, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2780 szLocation
, /* HTTP_QUERY_LOCATION = 33 */
2781 NULL
, /* HTTP_QUERY_ORIG_URI = 34 */
2782 szReferer
, /* HTTP_QUERY_REFERER = 35 */
2783 szRetry_After
, /* HTTP_QUERY_RETRY_AFTER = 36 */
2784 szServer
, /* HTTP_QUERY_SERVER = 37 */
2785 NULL
, /* HTTP_TITLE = 38 */
2786 szUser_Agent
, /* HTTP_QUERY_USER_AGENT = 39 */
2787 szWWW_Authenticate
, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2788 szProxy_Authenticate
, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2789 szAccept_Ranges
, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2790 szSet_Cookie
, /* HTTP_QUERY_SET_COOKIE = 43 */
2791 szCookie
, /* HTTP_QUERY_COOKIE = 44 */
2792 NULL
, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2793 NULL
, /* HTTP_QUERY_REFRESH = 46 */
2794 NULL
, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2795 szAge
, /* HTTP_QUERY_AGE = 48 */
2796 szCache_Control
, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2797 szContent_Base
, /* HTTP_QUERY_CONTENT_BASE = 50 */
2798 szContent_Location
, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2799 szContent_MD5
, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2800 szContent_Range
, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2801 szETag
, /* HTTP_QUERY_ETAG = 54 */
2802 hostW
, /* HTTP_QUERY_HOST = 55 */
2803 szIf_Match
, /* HTTP_QUERY_IF_MATCH = 56 */
2804 szIf_None_Match
, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2805 szIf_Range
, /* HTTP_QUERY_IF_RANGE = 58 */
2806 szIf_Unmodified_Since
, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2807 szMax_Forwards
, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2808 szProxy_Authorization
, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2809 szRange
, /* HTTP_QUERY_RANGE = 62 */
2810 szTransfer_Encoding
, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2811 szUpgrade
, /* HTTP_QUERY_UPGRADE = 64 */
2812 szVary
, /* HTTP_QUERY_VARY = 65 */
2813 szVia
, /* HTTP_QUERY_VIA = 66 */
2814 szWarning
, /* HTTP_QUERY_WARNING = 67 */
2815 szExpect
, /* HTTP_QUERY_EXPECT = 68 */
2816 szProxy_Connection
, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2817 szUnless_Modified_Since
, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2820 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2822 /***********************************************************************
2823 * HTTP_HttpQueryInfoW (internal)
2825 static BOOL
HTTP_HttpQueryInfoW(http_request_t
*lpwhr
, DWORD dwInfoLevel
,
2826 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
2828 LPHTTPHEADERW lphttpHdr
= NULL
;
2829 BOOL bSuccess
= FALSE
;
2830 BOOL request_only
= dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
;
2831 INT requested_index
= lpdwIndex
? *lpdwIndex
: 0;
2832 DWORD level
= (dwInfoLevel
& ~HTTP_QUERY_MODIFIER_FLAGS_MASK
);
2835 /* Find requested header structure */
2838 case HTTP_QUERY_CUSTOM
:
2839 if (!lpBuffer
) return FALSE
;
2840 index
= HTTP_GetCustomHeaderIndex(lpwhr
, lpBuffer
, requested_index
, request_only
);
2842 case HTTP_QUERY_RAW_HEADERS_CRLF
:
2849 headers
= HTTP_BuildHeaderRequestString(lpwhr
, lpwhr
->lpszVerb
, lpwhr
->lpszPath
, lpwhr
->lpszVersion
);
2851 headers
= lpwhr
->lpszRawHeaders
;
2854 len
= strlenW(headers
) * sizeof(WCHAR
);
2856 if (len
+ sizeof(WCHAR
) > *lpdwBufferLength
)
2858 len
+= sizeof(WCHAR
);
2859 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER
);
2865 memcpy(lpBuffer
, headers
, len
+ sizeof(WCHAR
));
2868 len
= strlenW(szCrLf
) * sizeof(WCHAR
);
2869 memcpy(lpBuffer
, szCrLf
, sizeof(szCrLf
));
2871 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
/ sizeof(WCHAR
)));
2874 *lpdwBufferLength
= len
;
2877 HeapFree(GetProcessHeap(), 0, headers
);
2880 case HTTP_QUERY_RAW_HEADERS
:
2882 LPWSTR
* ppszRawHeaderLines
= HTTP_Tokenize(lpwhr
->lpszRawHeaders
, szCrLf
);
2884 LPWSTR pszString
= lpBuffer
;
2886 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
2887 size
+= strlenW(ppszRawHeaderLines
[i
]) + 1;
2889 if (size
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
2891 HTTP_FreeTokens(ppszRawHeaderLines
);
2892 *lpdwBufferLength
= (size
+ 1) * sizeof(WCHAR
);
2893 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER
);
2898 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
2900 DWORD len
= strlenW(ppszRawHeaderLines
[i
]);
2901 memcpy(pszString
, ppszRawHeaderLines
[i
], (len
+1)*sizeof(WCHAR
));
2905 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, size
));
2907 *lpdwBufferLength
= size
* sizeof(WCHAR
);
2908 HTTP_FreeTokens(ppszRawHeaderLines
);
2912 case HTTP_QUERY_STATUS_TEXT
:
2913 if (lpwhr
->lpszStatusText
)
2915 DWORD len
= strlenW(lpwhr
->lpszStatusText
);
2916 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
2918 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
2919 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER
);
2924 memcpy(lpBuffer
, lpwhr
->lpszStatusText
, (len
+ 1) * sizeof(WCHAR
));
2925 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
2927 *lpdwBufferLength
= len
* sizeof(WCHAR
);
2931 case HTTP_QUERY_VERSION
:
2932 if (lpwhr
->lpszVersion
)
2934 DWORD len
= strlenW(lpwhr
->lpszVersion
);
2935 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
2937 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
2938 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER
);
2943 memcpy(lpBuffer
, lpwhr
->lpszVersion
, (len
+ 1) * sizeof(WCHAR
));
2944 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
2946 *lpdwBufferLength
= len
* sizeof(WCHAR
);
2950 case HTTP_QUERY_CONTENT_ENCODING
:
2951 index
= HTTP_GetCustomHeaderIndex(lpwhr
, header_lookup
[lpwhr
->gzip_stream
? HTTP_QUERY_CONTENT_TYPE
: level
],
2952 requested_index
,request_only
);
2955 assert (LAST_TABLE_HEADER
== (HTTP_QUERY_UNLESS_MODIFIED_SINCE
+ 1));
2957 if (level
< LAST_TABLE_HEADER
&& header_lookup
[level
])
2958 index
= HTTP_GetCustomHeaderIndex(lpwhr
, header_lookup
[level
],
2959 requested_index
,request_only
);
2963 lphttpHdr
= &lpwhr
->pCustHeaders
[index
];
2965 /* Ensure header satisfies requested attributes */
2967 ((dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
) &&
2968 (~lphttpHdr
->wFlags
& HDR_ISREQUEST
)))
2970 INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND
);
2974 if (lpdwIndex
&& level
!= HTTP_QUERY_STATUS_CODE
) (*lpdwIndex
)++;
2976 /* coalesce value to requested type */
2977 if (dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
&& lpBuffer
)
2979 *(int *)lpBuffer
= atoiW(lphttpHdr
->lpszValue
);
2980 TRACE(" returning number: %d\n", *(int *)lpBuffer
);
2983 else if (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
&& lpBuffer
)
2989 tmpTime
= ConvertTimeString(lphttpHdr
->lpszValue
);
2991 tmpTM
= *gmtime(&tmpTime
);
2992 STHook
= (SYSTEMTIME
*)lpBuffer
;
2993 STHook
->wDay
= tmpTM
.tm_mday
;
2994 STHook
->wHour
= tmpTM
.tm_hour
;
2995 STHook
->wMilliseconds
= 0;
2996 STHook
->wMinute
= tmpTM
.tm_min
;
2997 STHook
->wDayOfWeek
= tmpTM
.tm_wday
;
2998 STHook
->wMonth
= tmpTM
.tm_mon
+ 1;
2999 STHook
->wSecond
= tmpTM
.tm_sec
;
3000 STHook
->wYear
= tmpTM
.tm_year
;
3003 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3004 STHook
->wYear
, STHook
->wMonth
, STHook
->wDay
, STHook
->wDayOfWeek
,
3005 STHook
->wHour
, STHook
->wMinute
, STHook
->wSecond
, STHook
->wMilliseconds
);
3007 else if (lphttpHdr
->lpszValue
)
3009 DWORD len
= (strlenW(lphttpHdr
->lpszValue
) + 1) * sizeof(WCHAR
);
3011 if (len
> *lpdwBufferLength
)
3013 *lpdwBufferLength
= len
;
3014 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER
);
3019 memcpy(lpBuffer
, lphttpHdr
->lpszValue
, len
);
3020 TRACE(" returning string: %s\n", debugstr_w(lpBuffer
));
3022 *lpdwBufferLength
= len
- sizeof(WCHAR
);
3028 /***********************************************************************
3029 * HttpQueryInfoW (WININET.@)
3031 * Queries for information about an HTTP request
3038 BOOL WINAPI
HttpQueryInfoW(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3039 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3041 BOOL bSuccess
= FALSE
;
3042 http_request_t
*lpwhr
;
3044 if (TRACE_ON(wininet
)) {
3045 #define FE(x) { x, #x }
3046 static const wininet_flag_info query_flags
[] = {
3047 FE(HTTP_QUERY_MIME_VERSION
),
3048 FE(HTTP_QUERY_CONTENT_TYPE
),
3049 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING
),
3050 FE(HTTP_QUERY_CONTENT_ID
),
3051 FE(HTTP_QUERY_CONTENT_DESCRIPTION
),
3052 FE(HTTP_QUERY_CONTENT_LENGTH
),
3053 FE(HTTP_QUERY_CONTENT_LANGUAGE
),
3054 FE(HTTP_QUERY_ALLOW
),
3055 FE(HTTP_QUERY_PUBLIC
),
3056 FE(HTTP_QUERY_DATE
),
3057 FE(HTTP_QUERY_EXPIRES
),
3058 FE(HTTP_QUERY_LAST_MODIFIED
),
3059 FE(HTTP_QUERY_MESSAGE_ID
),
3061 FE(HTTP_QUERY_DERIVED_FROM
),
3062 FE(HTTP_QUERY_COST
),
3063 FE(HTTP_QUERY_LINK
),
3064 FE(HTTP_QUERY_PRAGMA
),
3065 FE(HTTP_QUERY_VERSION
),
3066 FE(HTTP_QUERY_STATUS_CODE
),
3067 FE(HTTP_QUERY_STATUS_TEXT
),
3068 FE(HTTP_QUERY_RAW_HEADERS
),
3069 FE(HTTP_QUERY_RAW_HEADERS_CRLF
),
3070 FE(HTTP_QUERY_CONNECTION
),
3071 FE(HTTP_QUERY_ACCEPT
),
3072 FE(HTTP_QUERY_ACCEPT_CHARSET
),
3073 FE(HTTP_QUERY_ACCEPT_ENCODING
),
3074 FE(HTTP_QUERY_ACCEPT_LANGUAGE
),
3075 FE(HTTP_QUERY_AUTHORIZATION
),
3076 FE(HTTP_QUERY_CONTENT_ENCODING
),
3077 FE(HTTP_QUERY_FORWARDED
),
3078 FE(HTTP_QUERY_FROM
),
3079 FE(HTTP_QUERY_IF_MODIFIED_SINCE
),
3080 FE(HTTP_QUERY_LOCATION
),
3081 FE(HTTP_QUERY_ORIG_URI
),
3082 FE(HTTP_QUERY_REFERER
),
3083 FE(HTTP_QUERY_RETRY_AFTER
),
3084 FE(HTTP_QUERY_SERVER
),
3085 FE(HTTP_QUERY_TITLE
),
3086 FE(HTTP_QUERY_USER_AGENT
),
3087 FE(HTTP_QUERY_WWW_AUTHENTICATE
),
3088 FE(HTTP_QUERY_PROXY_AUTHENTICATE
),
3089 FE(HTTP_QUERY_ACCEPT_RANGES
),
3090 FE(HTTP_QUERY_SET_COOKIE
),
3091 FE(HTTP_QUERY_COOKIE
),
3092 FE(HTTP_QUERY_REQUEST_METHOD
),
3093 FE(HTTP_QUERY_REFRESH
),
3094 FE(HTTP_QUERY_CONTENT_DISPOSITION
),
3096 FE(HTTP_QUERY_CACHE_CONTROL
),
3097 FE(HTTP_QUERY_CONTENT_BASE
),
3098 FE(HTTP_QUERY_CONTENT_LOCATION
),
3099 FE(HTTP_QUERY_CONTENT_MD5
),
3100 FE(HTTP_QUERY_CONTENT_RANGE
),
3101 FE(HTTP_QUERY_ETAG
),
3102 FE(HTTP_QUERY_HOST
),
3103 FE(HTTP_QUERY_IF_MATCH
),
3104 FE(HTTP_QUERY_IF_NONE_MATCH
),
3105 FE(HTTP_QUERY_IF_RANGE
),
3106 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE
),
3107 FE(HTTP_QUERY_MAX_FORWARDS
),
3108 FE(HTTP_QUERY_PROXY_AUTHORIZATION
),
3109 FE(HTTP_QUERY_RANGE
),
3110 FE(HTTP_QUERY_TRANSFER_ENCODING
),
3111 FE(HTTP_QUERY_UPGRADE
),
3112 FE(HTTP_QUERY_VARY
),
3114 FE(HTTP_QUERY_WARNING
),
3115 FE(HTTP_QUERY_CUSTOM
)
3117 static const wininet_flag_info modifier_flags
[] = {
3118 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS
),
3119 FE(HTTP_QUERY_FLAG_SYSTEMTIME
),
3120 FE(HTTP_QUERY_FLAG_NUMBER
),
3121 FE(HTTP_QUERY_FLAG_COALESCE
)
3124 DWORD info_mod
= dwInfoLevel
& HTTP_QUERY_MODIFIER_FLAGS_MASK
;
3125 DWORD info
= dwInfoLevel
& HTTP_QUERY_HEADER_MASK
;
3128 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest
, dwInfoLevel
, dwInfoLevel
);
3129 TRACE(" Attribute:");
3130 for (i
= 0; i
< (sizeof(query_flags
) / sizeof(query_flags
[0])); i
++) {
3131 if (query_flags
[i
].val
== info
) {
3132 TRACE(" %s", query_flags
[i
].name
);
3136 if (i
== (sizeof(query_flags
) / sizeof(query_flags
[0]))) {
3137 TRACE(" Unknown (%08x)", info
);
3140 TRACE(" Modifier:");
3141 for (i
= 0; i
< (sizeof(modifier_flags
) / sizeof(modifier_flags
[0])); i
++) {
3142 if (modifier_flags
[i
].val
& info_mod
) {
3143 TRACE(" %s", modifier_flags
[i
].name
);
3144 info_mod
&= ~ modifier_flags
[i
].val
;
3149 TRACE(" Unknown (%08x)", info_mod
);
3154 lpwhr
= (http_request_t
*) WININET_GetObject( hHttpRequest
);
3155 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
3157 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
3161 if (lpBuffer
== NULL
)
3162 *lpdwBufferLength
= 0;
3163 bSuccess
= HTTP_HttpQueryInfoW( lpwhr
, dwInfoLevel
,
3164 lpBuffer
, lpdwBufferLength
, lpdwIndex
);
3168 WININET_Release( &lpwhr
->hdr
);
3170 TRACE("%d <--\n", bSuccess
);
3174 /***********************************************************************
3175 * HttpQueryInfoA (WININET.@)
3177 * Queries for information about an HTTP request
3184 BOOL WINAPI
HttpQueryInfoA(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3185 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3191 if((dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
) ||
3192 (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
))
3194 return HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, lpBuffer
,
3195 lpdwBufferLength
, lpdwIndex
);
3201 len
= (*lpdwBufferLength
)*sizeof(WCHAR
);
3202 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3204 alloclen
= MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, NULL
, 0 ) * sizeof(WCHAR
);
3210 bufferW
= HeapAlloc( GetProcessHeap(), 0, alloclen
);
3211 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3212 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3213 MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, bufferW
, alloclen
/ sizeof(WCHAR
) );
3220 result
= HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, bufferW
,
3224 len
= WideCharToMultiByte( CP_ACP
,0, bufferW
, len
/ sizeof(WCHAR
) + 1,
3225 lpBuffer
, *lpdwBufferLength
, NULL
, NULL
);
3226 *lpdwBufferLength
= len
- 1;
3228 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer
));
3231 /* since the strings being returned from HttpQueryInfoW should be
3232 * only ASCII characters, it is reasonable to assume that all of
3233 * the Unicode characters can be reduced to a single byte */
3234 *lpdwBufferLength
= len
/ sizeof(WCHAR
);
3236 HeapFree(GetProcessHeap(), 0, bufferW
);
3241 /***********************************************************************
3242 * HttpSendRequestExA (WININET.@)
3244 * Sends the specified request to the HTTP server and allows chunked
3249 * Failure: FALSE, call GetLastError() for more information.
3251 BOOL WINAPI
HttpSendRequestExA(HINTERNET hRequest
,
3252 LPINTERNET_BUFFERSA lpBuffersIn
,
3253 LPINTERNET_BUFFERSA lpBuffersOut
,
3254 DWORD dwFlags
, DWORD_PTR dwContext
)
3256 INTERNET_BUFFERSW BuffersInW
;
3259 LPWSTR header
= NULL
;
3261 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
3262 lpBuffersOut
, dwFlags
, dwContext
);
3266 BuffersInW
.dwStructSize
= sizeof(LPINTERNET_BUFFERSW
);
3267 if (lpBuffersIn
->lpcszHeader
)
3269 headerlen
= MultiByteToWideChar(CP_ACP
,0,lpBuffersIn
->lpcszHeader
,
3270 lpBuffersIn
->dwHeadersLength
,0,0);
3271 header
= HeapAlloc(GetProcessHeap(),0,headerlen
*sizeof(WCHAR
));
3272 if (!(BuffersInW
.lpcszHeader
= header
))
3274 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
3277 BuffersInW
.dwHeadersLength
= MultiByteToWideChar(CP_ACP
, 0,
3278 lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
3282 BuffersInW
.lpcszHeader
= NULL
;
3283 BuffersInW
.dwHeadersTotal
= lpBuffersIn
->dwHeadersTotal
;
3284 BuffersInW
.lpvBuffer
= lpBuffersIn
->lpvBuffer
;
3285 BuffersInW
.dwBufferLength
= lpBuffersIn
->dwBufferLength
;
3286 BuffersInW
.dwBufferTotal
= lpBuffersIn
->dwBufferTotal
;
3287 BuffersInW
.Next
= NULL
;
3290 rc
= HttpSendRequestExW(hRequest
, lpBuffersIn
? &BuffersInW
: NULL
, NULL
, dwFlags
, dwContext
);
3292 HeapFree(GetProcessHeap(),0,header
);
3297 /***********************************************************************
3298 * HttpSendRequestExW (WININET.@)
3300 * Sends the specified request to the HTTP server and allows chunked
3305 * Failure: FALSE, call GetLastError() for more information.
3307 BOOL WINAPI
HttpSendRequestExW(HINTERNET hRequest
,
3308 LPINTERNET_BUFFERSW lpBuffersIn
,
3309 LPINTERNET_BUFFERSW lpBuffersOut
,
3310 DWORD dwFlags
, DWORD_PTR dwContext
)
3313 http_request_t
*lpwhr
;
3314 http_session_t
*lpwhs
;
3317 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
3318 lpBuffersOut
, dwFlags
, dwContext
);
3320 lpwhr
= (http_request_t
*) WININET_GetObject( hRequest
);
3322 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
3324 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
3328 lpwhs
= lpwhr
->lpHttpSession
;
3329 assert(lpwhs
->hdr
.htype
== WH_HHTTPSESSION
);
3330 hIC
= lpwhs
->lpAppInfo
;
3331 assert(hIC
->hdr
.htype
== WH_HINIT
);
3333 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
3335 WORKREQUEST workRequest
;
3336 struct WORKREQ_HTTPSENDREQUESTW
*req
;
3338 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
3339 workRequest
.hdr
= WININET_AddRef( &lpwhr
->hdr
);
3340 req
= &workRequest
.u
.HttpSendRequestW
;
3343 /* FIXME: this should use dwHeadersLength or may not be necessary at all */
3344 req
->lpszHeader
= heap_strdupW(lpBuffersIn
->lpcszHeader
);
3345 req
->dwHeaderLength
= lpBuffersIn
->dwHeadersLength
;
3346 req
->lpOptional
= lpBuffersIn
->lpvBuffer
;
3347 req
->dwOptionalLength
= lpBuffersIn
->dwBufferLength
;
3348 req
->dwContentLength
= lpBuffersIn
->dwBufferTotal
;
3352 req
->lpszHeader
= NULL
;
3353 req
->dwHeaderLength
= 0;
3354 req
->lpOptional
= NULL
;
3355 req
->dwOptionalLength
= 0;
3356 req
->dwContentLength
= 0;
3359 req
->bEndRequest
= FALSE
;
3361 INTERNET_AsyncCall(&workRequest
);
3363 * This is from windows.
3365 INTERNET_SetLastError(ERROR_IO_PENDING
);
3370 ret
= HTTP_HttpSendRequestW(lpwhr
, lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
3371 lpBuffersIn
->lpvBuffer
, lpBuffersIn
->dwBufferLength
,
3372 lpBuffersIn
->dwBufferTotal
, FALSE
);
3374 ret
= HTTP_HttpSendRequestW(lpwhr
, NULL
, 0, NULL
, 0, 0, FALSE
);
3379 WININET_Release( &lpwhr
->hdr
);
3385 /***********************************************************************
3386 * HttpSendRequestW (WININET.@)
3388 * Sends the specified request to the HTTP server
3395 BOOL WINAPI
HttpSendRequestW(HINTERNET hHttpRequest
, LPCWSTR lpszHeaders
,
3396 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
3398 http_request_t
*lpwhr
;
3399 http_session_t
*lpwhs
= NULL
;
3400 appinfo_t
*hIC
= NULL
;
3403 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest
,
3404 debugstr_wn(lpszHeaders
, dwHeaderLength
), dwHeaderLength
, lpOptional
, dwOptionalLength
);
3406 lpwhr
= (http_request_t
*) WININET_GetObject( hHttpRequest
);
3407 if (NULL
== lpwhr
|| lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
3409 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
3414 lpwhs
= lpwhr
->lpHttpSession
;
3415 if (NULL
== lpwhs
|| lpwhs
->hdr
.htype
!= WH_HHTTPSESSION
)
3417 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
3422 hIC
= lpwhs
->lpAppInfo
;
3423 if (NULL
== hIC
|| hIC
->hdr
.htype
!= WH_HINIT
)
3425 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
3430 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
3432 WORKREQUEST workRequest
;
3433 struct WORKREQ_HTTPSENDREQUESTW
*req
;
3435 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
3436 workRequest
.hdr
= WININET_AddRef( &lpwhr
->hdr
);
3437 req
= &workRequest
.u
.HttpSendRequestW
;
3442 if (dwHeaderLength
== ~0u) size
= (strlenW(lpszHeaders
) + 1) * sizeof(WCHAR
);
3443 else size
= dwHeaderLength
* sizeof(WCHAR
);
3445 req
->lpszHeader
= HeapAlloc(GetProcessHeap(), 0, size
);
3446 memcpy(req
->lpszHeader
, lpszHeaders
, size
);
3449 req
->lpszHeader
= 0;
3450 req
->dwHeaderLength
= dwHeaderLength
;
3451 req
->lpOptional
= lpOptional
;
3452 req
->dwOptionalLength
= dwOptionalLength
;
3453 req
->dwContentLength
= dwOptionalLength
;
3454 req
->bEndRequest
= TRUE
;
3456 INTERNET_AsyncCall(&workRequest
);
3458 * This is from windows.
3460 INTERNET_SetLastError(ERROR_IO_PENDING
);
3465 r
= HTTP_HttpSendRequestW(lpwhr
, lpszHeaders
,
3466 dwHeaderLength
, lpOptional
, dwOptionalLength
,
3467 dwOptionalLength
, TRUE
);
3471 WININET_Release( &lpwhr
->hdr
);
3475 /***********************************************************************
3476 * HttpSendRequestA (WININET.@)
3478 * Sends the specified request to the HTTP server
3485 BOOL WINAPI
HttpSendRequestA(HINTERNET hHttpRequest
, LPCSTR lpszHeaders
,
3486 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
3489 LPWSTR szHeaders
=NULL
;
3490 DWORD nLen
=dwHeaderLength
;
3491 if(lpszHeaders
!=NULL
)
3493 nLen
=MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,NULL
,0);
3494 szHeaders
=HeapAlloc(GetProcessHeap(),0,nLen
*sizeof(WCHAR
));
3495 MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,szHeaders
,nLen
);
3497 result
=HttpSendRequestW(hHttpRequest
, szHeaders
, nLen
, lpOptional
, dwOptionalLength
);
3498 HeapFree(GetProcessHeap(),0,szHeaders
);
3502 /***********************************************************************
3503 * HTTP_GetRedirectURL (internal)
3505 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*lpwhr
, LPCWSTR lpszUrl
)
3507 static WCHAR szHttp
[] = {'h','t','t','p',0};
3508 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3509 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
3510 URL_COMPONENTSW urlComponents
;
3511 DWORD url_length
= 0;
3513 LPWSTR combined_url
;
3515 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3516 urlComponents
.lpszScheme
= (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) ? szHttps
: szHttp
;
3517 urlComponents
.dwSchemeLength
= 0;
3518 urlComponents
.lpszHostName
= lpwhs
->lpszHostName
;
3519 urlComponents
.dwHostNameLength
= 0;
3520 urlComponents
.nPort
= lpwhs
->nHostPort
;
3521 urlComponents
.lpszUserName
= lpwhs
->lpszUserName
;
3522 urlComponents
.dwUserNameLength
= 0;
3523 urlComponents
.lpszPassword
= NULL
;
3524 urlComponents
.dwPasswordLength
= 0;
3525 urlComponents
.lpszUrlPath
= lpwhr
->lpszPath
;
3526 urlComponents
.dwUrlPathLength
= 0;
3527 urlComponents
.lpszExtraInfo
= NULL
;
3528 urlComponents
.dwExtraInfoLength
= 0;
3530 if (!InternetCreateUrlW(&urlComponents
, 0, NULL
, &url_length
) &&
3531 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3534 orig_url
= HeapAlloc(GetProcessHeap(), 0, url_length
);
3536 /* convert from bytes to characters */
3537 url_length
= url_length
/ sizeof(WCHAR
) - 1;
3538 if (!InternetCreateUrlW(&urlComponents
, 0, orig_url
, &url_length
))
3540 HeapFree(GetProcessHeap(), 0, orig_url
);
3545 if (!InternetCombineUrlW(orig_url
, lpszUrl
, NULL
, &url_length
, ICU_ENCODE_SPACES_ONLY
) &&
3546 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3548 HeapFree(GetProcessHeap(), 0, orig_url
);
3551 combined_url
= HeapAlloc(GetProcessHeap(), 0, url_length
* sizeof(WCHAR
));
3553 if (!InternetCombineUrlW(orig_url
, lpszUrl
, combined_url
, &url_length
, ICU_ENCODE_SPACES_ONLY
))
3555 HeapFree(GetProcessHeap(), 0, orig_url
);
3556 HeapFree(GetProcessHeap(), 0, combined_url
);
3559 HeapFree(GetProcessHeap(), 0, orig_url
);
3560 return combined_url
;
3564 /***********************************************************************
3565 * HTTP_HandleRedirect (internal)
3567 static BOOL
HTTP_HandleRedirect(http_request_t
*lpwhr
, LPCWSTR lpszUrl
)
3569 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
3570 appinfo_t
*hIC
= lpwhs
->lpAppInfo
;
3571 BOOL using_proxy
= hIC
->lpszProxy
&& hIC
->lpszProxy
[0];
3572 WCHAR path
[INTERNET_MAX_URL_LENGTH
];
3577 /* if it's an absolute path, keep the same session info */
3578 lstrcpynW(path
, lpszUrl
, INTERNET_MAX_URL_LENGTH
);
3582 URL_COMPONENTSW urlComponents
;
3583 WCHAR protocol
[32], hostName
[MAXHOSTNAME
], userName
[1024];
3584 static WCHAR szHttp
[] = {'h','t','t','p',0};
3585 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3591 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3592 urlComponents
.lpszScheme
= protocol
;
3593 urlComponents
.dwSchemeLength
= 32;
3594 urlComponents
.lpszHostName
= hostName
;
3595 urlComponents
.dwHostNameLength
= MAXHOSTNAME
;
3596 urlComponents
.lpszUserName
= userName
;
3597 urlComponents
.dwUserNameLength
= 1024;
3598 urlComponents
.lpszPassword
= NULL
;
3599 urlComponents
.dwPasswordLength
= 0;
3600 urlComponents
.lpszUrlPath
= path
;
3601 urlComponents
.dwUrlPathLength
= 2048;
3602 urlComponents
.lpszExtraInfo
= NULL
;
3603 urlComponents
.dwExtraInfoLength
= 0;
3604 if(!InternetCrackUrlW(lpszUrl
, strlenW(lpszUrl
), 0, &urlComponents
))
3607 if (!strncmpW(szHttp
, urlComponents
.lpszScheme
, strlenW(szHttp
)) &&
3608 (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
))
3610 TRACE("redirect from secure page to non-secure page\n");
3611 /* FIXME: warn about from secure redirect to non-secure page */
3612 lpwhr
->hdr
.dwFlags
&= ~INTERNET_FLAG_SECURE
;
3614 if (!strncmpW(szHttps
, urlComponents
.lpszScheme
, strlenW(szHttps
)) &&
3615 !(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
))
3617 TRACE("redirect from non-secure page to secure page\n");
3618 /* FIXME: notify about redirect to secure page */
3619 lpwhr
->hdr
.dwFlags
|= INTERNET_FLAG_SECURE
;
3622 if (urlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
3624 if (lstrlenW(protocol
)>4) /*https*/
3625 urlComponents
.nPort
= INTERNET_DEFAULT_HTTPS_PORT
;
3627 urlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
3632 * This upsets redirects to binary files on sourceforge.net
3633 * and gives an html page instead of the target file
3634 * Examination of the HTTP request sent by native wininet.dll
3635 * reveals that it doesn't send a referrer in that case.
3636 * Maybe there's a flag that enables this, or maybe a referrer
3637 * shouldn't be added in case of a redirect.
3640 /* consider the current host as the referrer */
3641 if (lpwhs
->lpszServerName
&& *lpwhs
->lpszServerName
)
3642 HTTP_ProcessHeader(lpwhr
, HTTP_REFERER
, lpwhs
->lpszServerName
,
3643 HTTP_ADDHDR_FLAG_REQ
|HTTP_ADDREQ_FLAG_REPLACE
|
3644 HTTP_ADDHDR_FLAG_ADD_IF_NEW
);
3647 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszHostName
);
3648 if (urlComponents
.nPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
3649 urlComponents
.nPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
3652 static const WCHAR fmt
[] = {'%','s',':','%','i',0};
3653 len
= lstrlenW(hostName
);
3654 len
+= 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3655 lpwhs
->lpszHostName
= HeapAlloc(GetProcessHeap(), 0, len
*sizeof(WCHAR
));
3656 sprintfW(lpwhs
->lpszHostName
, fmt
, hostName
, urlComponents
.nPort
);
3659 lpwhs
->lpszHostName
= heap_strdupW(hostName
);
3661 HTTP_ProcessHeader(lpwhr
, hostW
, lpwhs
->lpszHostName
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDREQ_FLAG_REPLACE
| HTTP_ADDHDR_FLAG_REQ
);
3663 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszUserName
);
3664 lpwhs
->lpszUserName
= NULL
;
3666 lpwhs
->lpszUserName
= heap_strdupW(userName
);
3670 if (strcmpiW(lpwhs
->lpszServerName
, hostName
) || lpwhs
->nServerPort
!= urlComponents
.nPort
)
3672 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszServerName
);
3673 lpwhs
->lpszServerName
= heap_strdupW(hostName
);
3674 lpwhs
->nServerPort
= urlComponents
.nPort
;
3676 NETCON_close(&lpwhr
->netConnection
);
3677 if (!HTTP_ResolveName(lpwhr
)) return FALSE
;
3678 if (!NETCON_init(&lpwhr
->netConnection
, lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)) return FALSE
;
3679 lpwhr
->read_pos
= lpwhr
->read_size
= 0;
3680 lpwhr
->read_chunked
= FALSE
;
3684 TRACE("Redirect through proxy\n");
3687 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszPath
);
3688 lpwhr
->lpszPath
=NULL
;
3694 rc
= UrlEscapeW(path
, NULL
, &needed
, URL_ESCAPE_SPACES_ONLY
);
3695 if (rc
!= E_POINTER
)
3696 needed
= strlenW(path
)+1;
3697 lpwhr
->lpszPath
= HeapAlloc(GetProcessHeap(), 0, needed
*sizeof(WCHAR
));
3698 rc
= UrlEscapeW(path
, lpwhr
->lpszPath
, &needed
,
3699 URL_ESCAPE_SPACES_ONLY
);
3702 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path
),rc
);
3703 strcpyW(lpwhr
->lpszPath
,path
);
3707 /* Remove custom content-type/length headers on redirects. */
3708 index
= HTTP_GetCustomHeaderIndex(lpwhr
, szContent_Type
, 0, TRUE
);
3710 HTTP_DeleteCustomHeader(lpwhr
, index
);
3711 index
= HTTP_GetCustomHeaderIndex(lpwhr
, szContent_Length
, 0, TRUE
);
3713 HTTP_DeleteCustomHeader(lpwhr
, index
);
3718 /***********************************************************************
3719 * HTTP_build_req (internal)
3721 * concatenate all the strings in the request together
3723 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
)
3728 for( t
= list
; *t
; t
++ )
3729 len
+= strlenW( *t
);
3732 str
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
3735 for( t
= list
; *t
; t
++ )
3741 static BOOL
HTTP_SecureProxyConnect(http_request_t
*lpwhr
)
3744 LPWSTR requestString
;
3750 static const WCHAR szConnect
[] = {'C','O','N','N','E','C','T',0};
3751 static const WCHAR szFormat
[] = {'%','s',':','%','d',0};
3752 http_session_t
*lpwhs
= lpwhr
->lpHttpSession
;
3756 lpszPath
= HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs
->lpszHostName
) + 13)*sizeof(WCHAR
) );
3757 sprintfW( lpszPath
, szFormat
, lpwhs
->lpszHostName
, lpwhs
->nHostPort
);
3758 requestString
= HTTP_BuildHeaderRequestString( lpwhr
, szConnect
, lpszPath
, g_szHttp1_1
);
3759 HeapFree( GetProcessHeap(), 0, lpszPath
);
3761 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3762 NULL
, 0, NULL
, NULL
);
3763 len
--; /* the nul terminator isn't needed */
3764 ascii_req
= HeapAlloc( GetProcessHeap(), 0, len
);
3765 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3766 ascii_req
, len
, NULL
, NULL
);
3767 HeapFree( GetProcessHeap(), 0, requestString
);
3769 TRACE("full request -> %s\n", debugstr_an( ascii_req
, len
) );
3771 ret
= NETCON_send( &lpwhr
->netConnection
, ascii_req
, len
, 0, &cnt
);
3772 HeapFree( GetProcessHeap(), 0, ascii_req
);
3773 if (!ret
|| cnt
< 0)
3776 responseLen
= HTTP_GetResponseHeaders( lpwhr
, TRUE
);
3783 static void HTTP_InsertCookies(http_request_t
*lpwhr
)
3785 static const WCHAR szUrlForm
[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3786 LPWSTR lpszCookies
, lpszUrl
= NULL
;
3787 DWORD nCookieSize
, size
;
3788 LPHTTPHEADERW Host
= HTTP_GetHeader(lpwhr
, hostW
);
3790 size
= (strlenW(Host
->lpszValue
) + strlenW(szUrlForm
) + strlenW(lpwhr
->lpszPath
)) * sizeof(WCHAR
);
3791 if (!(lpszUrl
= HeapAlloc(GetProcessHeap(), 0, size
))) return;
3792 sprintfW( lpszUrl
, szUrlForm
, Host
->lpszValue
, lpwhr
->lpszPath
);
3794 if (InternetGetCookieW(lpszUrl
, NULL
, NULL
, &nCookieSize
))
3797 static const WCHAR szCookie
[] = {'C','o','o','k','i','e',':',' ',0};
3799 size
= sizeof(szCookie
) + nCookieSize
* sizeof(WCHAR
) + sizeof(szCrLf
);
3800 if ((lpszCookies
= HeapAlloc(GetProcessHeap(), 0, size
)))
3802 cnt
+= sprintfW(lpszCookies
, szCookie
);
3803 InternetGetCookieW(lpszUrl
, NULL
, lpszCookies
+ cnt
, &nCookieSize
);
3804 strcatW(lpszCookies
, szCrLf
);
3806 HTTP_HttpAddRequestHeadersW(lpwhr
, lpszCookies
, strlenW(lpszCookies
), HTTP_ADDREQ_FLAG_REPLACE
);
3807 HeapFree(GetProcessHeap(), 0, lpszCookies
);
3810 HeapFree(GetProcessHeap(), 0, lpszUrl
);
3813 /***********************************************************************
3814 * HTTP_HttpSendRequestW (internal)
3816 * Sends the specified request to the HTTP server
3823 BOOL WINAPI
HTTP_HttpSendRequestW(http_request_t
*lpwhr
, LPCWSTR lpszHeaders
,
3824 DWORD dwHeaderLength
, LPVOID lpOptional
, DWORD dwOptionalLength
,
3825 DWORD dwContentLength
, BOOL bEndRequest
)
3828 BOOL bSuccess
= FALSE
, redirected
= FALSE
;
3829 LPWSTR requestString
= NULL
;
3832 INTERNET_ASYNC_RESULT iar
;
3833 static const WCHAR szPost
[] = { 'P','O','S','T',0 };
3834 static const WCHAR szContentLength
[] =
3835 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3836 WCHAR contentLengthStr
[sizeof szContentLength
/2 /* includes \r\n */ + 20 /* int */ ];
3838 TRACE("--> %p\n", lpwhr
);
3840 assert(lpwhr
->hdr
.htype
== WH_HHTTPREQ
);
3842 /* if the verb is NULL default to GET */
3843 if (!lpwhr
->lpszVerb
)
3844 lpwhr
->lpszVerb
= heap_strdupW(szGET
);
3846 if (dwContentLength
|| strcmpW(lpwhr
->lpszVerb
, szGET
))
3848 sprintfW(contentLengthStr
, szContentLength
, dwContentLength
);
3849 HTTP_HttpAddRequestHeadersW(lpwhr
, contentLengthStr
, -1L, HTTP_ADDREQ_FLAG_REPLACE
);
3850 lpwhr
->dwBytesToWrite
= dwContentLength
;
3852 if (lpwhr
->lpHttpSession
->lpAppInfo
->lpszAgent
)
3854 WCHAR
*agent_header
;
3855 static const WCHAR user_agent
[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3858 len
= strlenW(lpwhr
->lpHttpSession
->lpAppInfo
->lpszAgent
) + strlenW(user_agent
);
3859 agent_header
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
3860 sprintfW(agent_header
, user_agent
, lpwhr
->lpHttpSession
->lpAppInfo
->lpszAgent
);
3862 HTTP_HttpAddRequestHeadersW(lpwhr
, agent_header
, strlenW(agent_header
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
3863 HeapFree(GetProcessHeap(), 0, agent_header
);
3865 if (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_PRAGMA_NOCACHE
)
3867 static const WCHAR pragma_nocache
[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3868 HTTP_HttpAddRequestHeadersW(lpwhr
, pragma_nocache
, strlenW(pragma_nocache
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
3870 if ((lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_CACHE_WRITE
) && !strcmpW(lpwhr
->lpszVerb
, szPost
))
3872 static const WCHAR cache_control
[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3873 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3874 HTTP_HttpAddRequestHeadersW(lpwhr
, cache_control
, strlenW(cache_control
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
3884 /* like native, just in case the caller forgot to call InternetReadFile
3885 * for all the data */
3886 HTTP_DrainContent(lpwhr
);
3887 lpwhr
->dwContentRead
= 0;
3889 if (TRACE_ON(wininet
))
3891 LPHTTPHEADERW Host
= HTTP_GetHeader(lpwhr
, hostW
);
3892 TRACE("Going to url %s %s\n", debugstr_w(Host
->lpszValue
), debugstr_w(lpwhr
->lpszPath
));
3896 if (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_KEEP_CONNECTION
)
3898 HTTP_ProcessHeader(lpwhr
, szConnection
, szKeepAlive
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
3900 HTTP_InsertAuthorization(lpwhr
, lpwhr
->pAuthInfo
, szAuthorization
);
3901 HTTP_InsertAuthorization(lpwhr
, lpwhr
->pProxyAuthInfo
, szProxy_Authorization
);
3903 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
))
3904 HTTP_InsertCookies(lpwhr
);
3906 /* add the headers the caller supplied */
3907 if( lpszHeaders
&& dwHeaderLength
)
3909 HTTP_HttpAddRequestHeadersW(lpwhr
, lpszHeaders
, dwHeaderLength
,
3910 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REPLACE
);
3913 if (lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxy
&& lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxy
[0])
3915 WCHAR
*url
= HTTP_BuildProxyRequestUrl(lpwhr
);
3916 requestString
= HTTP_BuildHeaderRequestString(lpwhr
, lpwhr
->lpszVerb
, url
, lpwhr
->lpszVersion
);
3917 HeapFree(GetProcessHeap(), 0, url
);
3920 requestString
= HTTP_BuildHeaderRequestString(lpwhr
, lpwhr
->lpszVerb
, lpwhr
->lpszPath
, lpwhr
->lpszVersion
);
3923 TRACE("Request header -> %s\n", debugstr_w(requestString
) );
3925 /* Send the request and store the results */
3926 if (!HTTP_OpenConnection(lpwhr
))
3929 /* send the request as ASCII, tack on the optional data */
3930 if (!lpOptional
|| redirected
)
3931 dwOptionalLength
= 0;
3932 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3933 NULL
, 0, NULL
, NULL
);
3934 ascii_req
= HeapAlloc( GetProcessHeap(), 0, len
+ dwOptionalLength
);
3935 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3936 ascii_req
, len
, NULL
, NULL
);
3938 memcpy( &ascii_req
[len
-1], lpOptional
, dwOptionalLength
);
3939 len
= (len
+ dwOptionalLength
- 1);
3941 TRACE("full request -> %s\n", debugstr_a(ascii_req
) );
3943 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3944 INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
3946 NETCON_send(&lpwhr
->netConnection
, ascii_req
, len
, 0, &cnt
);
3947 HeapFree( GetProcessHeap(), 0, ascii_req
);
3949 lpwhr
->dwBytesWritten
= dwOptionalLength
;
3951 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3952 INTERNET_STATUS_REQUEST_SENT
,
3953 &len
, sizeof(DWORD
));
3960 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3961 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
3966 responseLen
= HTTP_GetResponseHeaders(lpwhr
, TRUE
);
3970 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
3971 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
,
3974 HTTP_ProcessCookies(lpwhr
);
3976 if (!set_content_length( lpwhr
)) HTTP_FinishedReading(lpwhr
);
3978 dwBufferSize
= sizeof(dwStatusCode
);
3979 if (!HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_STATUS_CODE
,
3980 &dwStatusCode
,&dwBufferSize
,NULL
))
3983 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
) && bSuccess
)
3985 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
3986 dwBufferSize
=sizeof(szNewLocation
);
3987 if ((dwStatusCode
==HTTP_STATUS_REDIRECT
|| dwStatusCode
==HTTP_STATUS_MOVED
) &&
3988 HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_LOCATION
,szNewLocation
,&dwBufferSize
,NULL
))
3990 if (strcmpW(lpwhr
->lpszVerb
, szGET
) && strcmpW(lpwhr
->lpszVerb
, szHEAD
))
3992 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszVerb
);
3993 lpwhr
->lpszVerb
= heap_strdupW(szGET
);
3995 HTTP_DrainContent(lpwhr
);
3996 if ((new_url
= HTTP_GetRedirectURL( lpwhr
, szNewLocation
)))
3998 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
3999 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
4000 bSuccess
= HTTP_HandleRedirect(lpwhr
, new_url
);
4003 HeapFree(GetProcessHeap(), 0, requestString
);
4006 HeapFree( GetProcessHeap(), 0, new_url
);
4011 if (!(lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTH
) && bSuccess
)
4013 WCHAR szAuthValue
[2048];
4015 if (dwStatusCode
== HTTP_STATUS_DENIED
)
4017 LPHTTPHEADERW Host
= HTTP_GetHeader(lpwhr
, hostW
);
4019 while (HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_WWW_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
))
4021 if (HTTP_DoAuthorization(lpwhr
, szAuthValue
,
4023 lpwhr
->lpHttpSession
->lpszUserName
,
4024 lpwhr
->lpHttpSession
->lpszPassword
,
4032 if (dwStatusCode
== HTTP_STATUS_PROXY_AUTH_REQ
)
4035 while (HTTP_HttpQueryInfoW(lpwhr
,HTTP_QUERY_PROXY_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
))
4037 if (HTTP_DoAuthorization(lpwhr
, szAuthValue
,
4038 &lpwhr
->pProxyAuthInfo
,
4039 lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxyUsername
,
4040 lpwhr
->lpHttpSession
->lpAppInfo
->lpszProxyPassword
,
4056 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
4057 WCHAR cacheFileName
[MAX_PATH
+1];
4060 b
= HTTP_GetRequestURL(lpwhr
, url
);
4062 WARN("Could not get URL\n");
4066 b
= CreateUrlCacheEntryW(url
, lpwhr
->dwContentLength
> 0 ? lpwhr
->dwContentLength
: 0, NULL
, cacheFileName
, 0);
4068 lpwhr
->lpszCacheFile
= heap_strdupW(cacheFileName
);
4069 lpwhr
->hCacheFile
= CreateFileW(lpwhr
->lpszCacheFile
, GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
4070 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
4071 if(lpwhr
->hCacheFile
== INVALID_HANDLE_VALUE
) {
4072 WARN("Could not create file: %u\n", GetLastError());
4073 lpwhr
->hCacheFile
= NULL
;
4076 WARN("Could not create cache entry: %08x\n", GetLastError());
4082 HeapFree(GetProcessHeap(), 0, requestString
);
4084 /* TODO: send notification for P3P header */
4086 if (lpwhr
->lpHttpSession
->lpAppInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4090 if (lpwhr
->dwBytesWritten
== lpwhr
->dwBytesToWrite
) HTTP_ReceiveRequestData(lpwhr
, TRUE
);
4093 iar
.dwResult
= (DWORD_PTR
)lpwhr
->hdr
.hInternet
;
4096 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
4097 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
4098 sizeof(INTERNET_ASYNC_RESULT
));
4103 iar
.dwResult
= (DWORD_PTR
)lpwhr
->hdr
.hInternet
;
4104 iar
.dwError
= INTERNET_GetLastError();
4106 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
4107 INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
4108 sizeof(INTERNET_ASYNC_RESULT
));
4113 if (bSuccess
) INTERNET_SetLastError(ERROR_SUCCESS
);
4117 /***********************************************************************
4118 * HTTPSESSION_Destroy (internal)
4120 * Deallocate session handle
4123 static void HTTPSESSION_Destroy(object_header_t
*hdr
)
4125 http_session_t
*lpwhs
= (http_session_t
*) hdr
;
4127 TRACE("%p\n", lpwhs
);
4129 WININET_Release(&lpwhs
->lpAppInfo
->hdr
);
4131 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszHostName
);
4132 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszServerName
);
4133 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszPassword
);
4134 HeapFree(GetProcessHeap(), 0, lpwhs
->lpszUserName
);
4135 HeapFree(GetProcessHeap(), 0, lpwhs
);
4138 static DWORD
HTTPSESSION_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
4141 case INTERNET_OPTION_HANDLE_TYPE
:
4142 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4144 if (*size
< sizeof(ULONG
))
4145 return ERROR_INSUFFICIENT_BUFFER
;
4147 *size
= sizeof(DWORD
);
4148 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_CONNECT_HTTP
;
4149 return ERROR_SUCCESS
;
4152 return INET_QueryOption(option
, buffer
, size
, unicode
);
4155 static DWORD
HTTPSESSION_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
4157 http_session_t
*ses
= (http_session_t
*)hdr
;
4160 case INTERNET_OPTION_USERNAME
:
4162 HeapFree(GetProcessHeap(), 0, ses
->lpszUserName
);
4163 if (!(ses
->lpszUserName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
4164 return ERROR_SUCCESS
;
4166 case INTERNET_OPTION_PASSWORD
:
4168 HeapFree(GetProcessHeap(), 0, ses
->lpszPassword
);
4169 if (!(ses
->lpszPassword
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
4170 return ERROR_SUCCESS
;
4175 return ERROR_INTERNET_INVALID_OPTION
;
4178 static const object_vtbl_t HTTPSESSIONVtbl
= {
4179 HTTPSESSION_Destroy
,
4181 HTTPSESSION_QueryOption
,
4182 HTTPSESSION_SetOption
,
4191 /***********************************************************************
4192 * HTTP_Connect (internal)
4194 * Create http session handle
4197 * HINTERNET a session handle on success
4201 HINTERNET
HTTP_Connect(appinfo_t
*hIC
, LPCWSTR lpszServerName
,
4202 INTERNET_PORT nServerPort
, LPCWSTR lpszUserName
,
4203 LPCWSTR lpszPassword
, DWORD dwFlags
, DWORD_PTR dwContext
,
4204 DWORD dwInternalFlags
)
4206 http_session_t
*lpwhs
= NULL
;
4207 HINTERNET handle
= NULL
;
4211 if (!lpszServerName
|| !lpszServerName
[0])
4213 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
4217 assert( hIC
->hdr
.htype
== WH_HINIT
);
4219 lpwhs
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(http_session_t
));
4222 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
4227 * According to my tests. The name is not resolved until a request is sent
4230 lpwhs
->hdr
.htype
= WH_HHTTPSESSION
;
4231 lpwhs
->hdr
.vtbl
= &HTTPSESSIONVtbl
;
4232 lpwhs
->hdr
.dwFlags
= dwFlags
;
4233 lpwhs
->hdr
.dwContext
= dwContext
;
4234 lpwhs
->hdr
.dwInternalFlags
= dwInternalFlags
| (hIC
->hdr
.dwInternalFlags
& INET_CALLBACKW
);
4235 lpwhs
->hdr
.refs
= 1;
4236 lpwhs
->hdr
.lpfnStatusCB
= hIC
->hdr
.lpfnStatusCB
;
4238 WININET_AddRef( &hIC
->hdr
);
4239 lpwhs
->lpAppInfo
= hIC
;
4240 list_add_head( &hIC
->hdr
.children
, &lpwhs
->hdr
.entry
);
4242 handle
= WININET_AllocHandle( &lpwhs
->hdr
);
4245 ERR("Failed to alloc handle\n");
4246 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
4250 if(hIC
->lpszProxy
&& hIC
->dwAccessType
== INTERNET_OPEN_TYPE_PROXY
) {
4251 if(strchrW(hIC
->lpszProxy
, ' '))
4252 FIXME("Several proxies not implemented.\n");
4253 if(hIC
->lpszProxyBypass
)
4254 FIXME("Proxy bypass is ignored.\n");
4256 if (lpszServerName
&& lpszServerName
[0])
4258 lpwhs
->lpszServerName
= heap_strdupW(lpszServerName
);
4259 lpwhs
->lpszHostName
= heap_strdupW(lpszServerName
);
4261 if (lpszUserName
&& lpszUserName
[0])
4262 lpwhs
->lpszUserName
= heap_strdupW(lpszUserName
);
4263 if (lpszPassword
&& lpszPassword
[0])
4264 lpwhs
->lpszPassword
= heap_strdupW(lpszPassword
);
4265 lpwhs
->nServerPort
= nServerPort
;
4266 lpwhs
->nHostPort
= nServerPort
;
4268 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4269 if (!(lpwhs
->hdr
.dwInternalFlags
& INET_OPENURL
))
4271 INTERNET_SendCallback(&hIC
->hdr
, dwContext
,
4272 INTERNET_STATUS_HANDLE_CREATED
, &handle
,
4278 WININET_Release( &lpwhs
->hdr
);
4281 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4285 TRACE("%p --> %p (%p)\n", hIC
, handle
, lpwhs
);
4290 /***********************************************************************
4291 * HTTP_OpenConnection (internal)
4293 * Connect to a web server
4300 static BOOL
HTTP_OpenConnection(http_request_t
*lpwhr
)
4302 BOOL bSuccess
= FALSE
;
4303 http_session_t
*lpwhs
;
4304 appinfo_t
*hIC
= NULL
;
4305 char szaddr
[INET6_ADDRSTRLEN
];
4311 if (lpwhr
->hdr
.htype
!= WH_HHTTPREQ
)
4313 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
4317 if (NETCON_connected(&lpwhr
->netConnection
))
4322 if (!HTTP_ResolveName(lpwhr
)) goto lend
;
4324 lpwhs
= lpwhr
->lpHttpSession
;
4326 hIC
= lpwhs
->lpAppInfo
;
4327 switch (lpwhs
->socketAddress
.ss_family
)
4330 addr
= &((struct sockaddr_in
*)&lpwhs
->socketAddress
)->sin_addr
;
4333 addr
= &((struct sockaddr_in6
*)&lpwhs
->socketAddress
)->sin6_addr
;
4336 WARN("unsupported family %d\n", lpwhs
->socketAddress
.ss_family
);
4337 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED
);
4340 inet_ntop(lpwhs
->socketAddress
.ss_family
, addr
, szaddr
, sizeof(szaddr
));
4341 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
4342 INTERNET_STATUS_CONNECTING_TO_SERVER
,
4346 if (!NETCON_create(&lpwhr
->netConnection
, lpwhs
->socketAddress
.ss_family
,
4349 WARN("Socket creation failed: %u\n", INTERNET_GetLastError());
4353 if (!NETCON_connect(&lpwhr
->netConnection
, (struct sockaddr
*)&lpwhs
->socketAddress
,
4357 if (lpwhr
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
4359 /* Note: we differ from Microsoft's WinINet here. they seem to have
4360 * a bug that causes no status callbacks to be sent when starting
4361 * a tunnel to a proxy server using the CONNECT verb. i believe our
4362 * behaviour to be more correct and to not cause any incompatibilities
4363 * because using a secure connection through a proxy server is a rare
4364 * case that would be hard for anyone to depend on */
4365 if (hIC
->lpszProxy
&& !HTTP_SecureProxyConnect(lpwhr
))
4368 if (!NETCON_secure_connect(&lpwhr
->netConnection
, lpwhs
->lpszHostName
))
4370 WARN("Couldn't connect securely to host\n");
4375 INTERNET_SendCallback(&lpwhr
->hdr
, lpwhr
->hdr
.dwContext
,
4376 INTERNET_STATUS_CONNECTED_TO_SERVER
,
4377 szaddr
, strlen(szaddr
)+1);
4382 lpwhr
->read_pos
= lpwhr
->read_size
= 0;
4383 lpwhr
->read_chunked
= FALSE
;
4385 TRACE("%d <--\n", bSuccess
);
4390 /***********************************************************************
4391 * HTTP_clear_response_headers (internal)
4393 * clear out any old response headers
4395 static void HTTP_clear_response_headers( http_request_t
*lpwhr
)
4399 for( i
=0; i
<lpwhr
->nCustHeaders
; i
++)
4401 if( !lpwhr
->pCustHeaders
[i
].lpszField
)
4403 if( !lpwhr
->pCustHeaders
[i
].lpszValue
)
4405 if ( lpwhr
->pCustHeaders
[i
].wFlags
& HDR_ISREQUEST
)
4407 HTTP_DeleteCustomHeader( lpwhr
, i
);
4412 /***********************************************************************
4413 * HTTP_GetResponseHeaders (internal)
4415 * Read server response
4422 static INT
HTTP_GetResponseHeaders(http_request_t
*lpwhr
, BOOL clear
)
4425 WCHAR buffer
[MAX_REPLY_LEN
];
4426 DWORD buflen
= MAX_REPLY_LEN
;
4427 BOOL bSuccess
= FALSE
;
4429 char bufferA
[MAX_REPLY_LEN
];
4430 LPWSTR status_code
= NULL
, status_text
= NULL
;
4431 DWORD cchMaxRawHeaders
= 1024;
4432 LPWSTR lpszRawHeaders
= HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
4434 DWORD cchRawHeaders
= 0;
4435 BOOL codeHundred
= FALSE
;
4439 /* clear old response headers (eg. from a redirect response) */
4440 if (clear
) HTTP_clear_response_headers( lpwhr
);
4442 if (!NETCON_connected(&lpwhr
->netConnection
))
4446 static const WCHAR szHundred
[] = {'1','0','0',0};
4448 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4450 buflen
= MAX_REPLY_LEN
;
4451 if (!read_line(lpwhr
, bufferA
, &buflen
))
4454 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
4455 /* check is this a status code line? */
4456 if (!strncmpW(buffer
, g_szHttp1_0
, 4))
4458 /* split the version from the status code */
4459 status_code
= strchrW( buffer
, ' ' );
4464 /* split the status code from the status text */
4465 status_text
= strchrW( status_code
, ' ' );
4470 TRACE("version [%s] status code [%s] status text [%s]\n",
4471 debugstr_w(buffer
), debugstr_w(status_code
), debugstr_w(status_text
) );
4473 codeHundred
= (!strcmpW(status_code
, szHundred
));
4475 else if (!codeHundred
)
4477 FIXME("Non status line at head of response (%s)\n",debugstr_w(buffer
));
4480 } while (codeHundred
);
4482 /* Add status code */
4483 HTTP_ProcessHeader(lpwhr
, szStatus
, status_code
,
4484 HTTP_ADDHDR_FLAG_REPLACE
);
4486 HeapFree(GetProcessHeap(),0,lpwhr
->lpszVersion
);
4487 HeapFree(GetProcessHeap(),0,lpwhr
->lpszStatusText
);
4489 lpwhr
->lpszVersion
= heap_strdupW(buffer
);
4490 lpwhr
->lpszStatusText
= heap_strdupW(status_text
);
4492 /* Restore the spaces */
4493 *(status_code
-1) = ' ';
4494 *(status_text
-1) = ' ';
4496 /* regenerate raw headers */
4497 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
4498 cchMaxRawHeaders
*= 2;
4499 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
4500 if (temp
== NULL
) goto lend
;
4501 lpszRawHeaders
= temp
;
4502 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
4503 cchRawHeaders
+= (buflen
-1);
4504 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
4505 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
4506 lpszRawHeaders
[cchRawHeaders
] = '\0';
4508 /* Parse each response line */
4511 buflen
= MAX_REPLY_LEN
;
4512 if (read_line(lpwhr
, bufferA
, &buflen
))
4514 LPWSTR
* pFieldAndValue
;
4516 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA
));
4518 if (!bufferA
[0]) break;
4519 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
4521 pFieldAndValue
= HTTP_InterpretHttpHeader(buffer
);
4524 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
4525 cchMaxRawHeaders
*= 2;
4526 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
4527 if (temp
== NULL
) goto lend
;
4528 lpszRawHeaders
= temp
;
4529 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
4530 cchRawHeaders
+= (buflen
-1);
4531 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
4532 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
4533 lpszRawHeaders
[cchRawHeaders
] = '\0';
4535 HTTP_ProcessHeader(lpwhr
, pFieldAndValue
[0], pFieldAndValue
[1],
4536 HTTP_ADDREQ_FLAG_ADD
);
4538 HTTP_FreeTokens(pFieldAndValue
);
4549 /* make sure the response header is terminated with an empty line. Some apps really
4550 truly care about that empty line being there for some reason. Just add it to the
4552 if (cchRawHeaders
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
4554 cchMaxRawHeaders
= cchRawHeaders
+ strlenW(szCrLf
);
4555 temp
= HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders
, (cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
4556 if (temp
== NULL
) goto lend
;
4557 lpszRawHeaders
= temp
;
4560 memcpy(&lpszRawHeaders
[cchRawHeaders
], szCrLf
, sizeof(szCrLf
));
4562 HeapFree(GetProcessHeap(), 0, lpwhr
->lpszRawHeaders
);
4563 lpwhr
->lpszRawHeaders
= lpszRawHeaders
;
4564 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders
));
4574 HeapFree(GetProcessHeap(), 0, lpszRawHeaders
);
4579 /***********************************************************************
4580 * HTTP_InterpretHttpHeader (internal)
4582 * Parse server response
4586 * Pointer to array of field, value, NULL on success.
4589 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
)
4591 LPWSTR
* pTokenPair
;
4595 pTokenPair
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*pTokenPair
)*3);
4597 pszColon
= strchrW(buffer
, ':');
4598 /* must have two tokens */
4601 HTTP_FreeTokens(pTokenPair
);
4603 TRACE("No ':' in line: %s\n", debugstr_w(buffer
));
4607 pTokenPair
[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon
- buffer
+ 1) * sizeof(WCHAR
));
4610 HTTP_FreeTokens(pTokenPair
);
4613 memcpy(pTokenPair
[0], buffer
, (pszColon
- buffer
) * sizeof(WCHAR
));
4614 pTokenPair
[0][pszColon
- buffer
] = '\0';
4618 len
= strlenW(pszColon
);
4619 pTokenPair
[1] = HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
));
4622 HTTP_FreeTokens(pTokenPair
);
4625 memcpy(pTokenPair
[1], pszColon
, (len
+ 1) * sizeof(WCHAR
));
4627 strip_spaces(pTokenPair
[0]);
4628 strip_spaces(pTokenPair
[1]);
4630 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair
[0]), debugstr_w(pTokenPair
[1]));
4634 /***********************************************************************
4635 * HTTP_ProcessHeader (internal)
4637 * Stuff header into header tables according to <dwModifier>
4641 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4643 static BOOL
HTTP_ProcessHeader(http_request_t
*lpwhr
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
)
4645 LPHTTPHEADERW lphttpHdr
= NULL
;
4646 BOOL bSuccess
= FALSE
;
4648 BOOL request_only
= dwModifier
& HTTP_ADDHDR_FLAG_REQ
;
4650 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field
), debugstr_w(value
), dwModifier
);
4652 /* REPLACE wins out over ADD */
4653 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
4654 dwModifier
&= ~HTTP_ADDHDR_FLAG_ADD
;
4656 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD
)
4659 index
= HTTP_GetCustomHeaderIndex(lpwhr
, field
, 0, request_only
);
4663 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD_IF_NEW
)
4667 lphttpHdr
= &lpwhr
->pCustHeaders
[index
];
4673 hdr
.lpszField
= (LPWSTR
)field
;
4674 hdr
.lpszValue
= (LPWSTR
)value
;
4675 hdr
.wFlags
= hdr
.wCount
= 0;
4677 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
4678 hdr
.wFlags
|= HDR_ISREQUEST
;
4680 return HTTP_InsertCustomHeader(lpwhr
, &hdr
);
4682 /* no value to delete */
4685 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
4686 lphttpHdr
->wFlags
|= HDR_ISREQUEST
;
4688 lphttpHdr
->wFlags
&= ~HDR_ISREQUEST
;
4690 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
4692 HTTP_DeleteCustomHeader( lpwhr
, index
);
4698 hdr
.lpszField
= (LPWSTR
)field
;
4699 hdr
.lpszValue
= (LPWSTR
)value
;
4700 hdr
.wFlags
= hdr
.wCount
= 0;
4702 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
4703 hdr
.wFlags
|= HDR_ISREQUEST
;
4705 return HTTP_InsertCustomHeader(lpwhr
, &hdr
);
4710 else if (dwModifier
& COALESCEFLAGS
)
4715 INT origlen
= strlenW(lphttpHdr
->lpszValue
);
4716 INT valuelen
= strlenW(value
);
4718 if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
)
4721 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
4723 else if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON
)
4726 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
4729 len
= origlen
+ valuelen
+ ((ch
> 0) ? 2 : 0);
4731 lpsztmp
= HeapReAlloc(GetProcessHeap(), 0, lphttpHdr
->lpszValue
, (len
+1)*sizeof(WCHAR
));
4734 lphttpHdr
->lpszValue
= lpsztmp
;
4735 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4738 lphttpHdr
->lpszValue
[origlen
] = ch
;
4740 lphttpHdr
->lpszValue
[origlen
] = ' ';
4744 memcpy(&lphttpHdr
->lpszValue
[origlen
], value
, valuelen
*sizeof(WCHAR
));
4745 lphttpHdr
->lpszValue
[len
] = '\0';
4750 WARN("HeapReAlloc (%d bytes) failed\n",len
+1);
4751 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
4754 TRACE("<-- %d\n",bSuccess
);
4759 /***********************************************************************
4760 * HTTP_FinishedReading (internal)
4762 * Called when all content from server has been read by client.
4765 static BOOL
HTTP_FinishedReading(http_request_t
*lpwhr
)
4767 BOOL keepalive
= HTTP_KeepAlive(lpwhr
);
4774 HTTPREQ_CloseConnection(&lpwhr
->hdr
);
4777 /* FIXME: store data in the URL cache here */
4783 /***********************************************************************
4784 * HTTP_GetCustomHeaderIndex (internal)
4786 * Return index of custom header from header array
4789 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*lpwhr
, LPCWSTR lpszField
,
4790 int requested_index
, BOOL request_only
)
4794 TRACE("%s\n", debugstr_w(lpszField
));
4796 for (index
= 0; index
< lpwhr
->nCustHeaders
; index
++)
4798 if (strcmpiW(lpwhr
->pCustHeaders
[index
].lpszField
, lpszField
))
4801 if (request_only
&& !(lpwhr
->pCustHeaders
[index
].wFlags
& HDR_ISREQUEST
))
4804 if (!request_only
&& (lpwhr
->pCustHeaders
[index
].wFlags
& HDR_ISREQUEST
))
4807 if (requested_index
== 0)
4812 if (index
>= lpwhr
->nCustHeaders
)
4815 TRACE("Return: %d\n", index
);
4820 /***********************************************************************
4821 * HTTP_InsertCustomHeader (internal)
4823 * Insert header into array
4826 static BOOL
HTTP_InsertCustomHeader(http_request_t
*lpwhr
, LPHTTPHEADERW lpHdr
)
4829 LPHTTPHEADERW lph
= NULL
;
4832 TRACE("--> %s: %s\n", debugstr_w(lpHdr
->lpszField
), debugstr_w(lpHdr
->lpszValue
));
4833 count
= lpwhr
->nCustHeaders
+ 1;
4835 lph
= HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, lpwhr
->pCustHeaders
, sizeof(HTTPHEADERW
) * count
);
4837 lph
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(HTTPHEADERW
) * count
);
4841 lpwhr
->pCustHeaders
= lph
;
4842 lpwhr
->pCustHeaders
[count
-1].lpszField
= heap_strdupW(lpHdr
->lpszField
);
4843 lpwhr
->pCustHeaders
[count
-1].lpszValue
= heap_strdupW(lpHdr
->lpszValue
);
4844 lpwhr
->pCustHeaders
[count
-1].wFlags
= lpHdr
->wFlags
;
4845 lpwhr
->pCustHeaders
[count
-1].wCount
= lpHdr
->wCount
;
4846 lpwhr
->nCustHeaders
++;
4851 INTERNET_SetLastError(ERROR_OUTOFMEMORY
);
4858 /***********************************************************************
4859 * HTTP_DeleteCustomHeader (internal)
4861 * Delete header from array
4862 * If this function is called, the indexs may change.
4864 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*lpwhr
, DWORD index
)
4866 if( lpwhr
->nCustHeaders
<= 0 )
4868 if( index
>= lpwhr
->nCustHeaders
)
4870 lpwhr
->nCustHeaders
--;
4872 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[index
].lpszField
);
4873 HeapFree(GetProcessHeap(), 0, lpwhr
->pCustHeaders
[index
].lpszValue
);
4875 memmove( &lpwhr
->pCustHeaders
[index
], &lpwhr
->pCustHeaders
[index
+1],
4876 (lpwhr
->nCustHeaders
- index
)* sizeof(HTTPHEADERW
) );
4877 memset( &lpwhr
->pCustHeaders
[lpwhr
->nCustHeaders
], 0, sizeof(HTTPHEADERW
) );
4883 /***********************************************************************
4884 * HTTP_VerifyValidHeader (internal)
4886 * Verify the given header is not invalid for the given http request
4889 static BOOL
HTTP_VerifyValidHeader(http_request_t
*lpwhr
, LPCWSTR field
)
4891 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4892 if (!strcmpW(lpwhr
->lpszVersion
, g_szHttp1_0
) && !strcmpiW(field
, szAccept_Encoding
))
4898 /***********************************************************************
4899 * IsHostInProxyBypassList (@)
4904 BOOL WINAPI
IsHostInProxyBypassList(DWORD flags
, LPCSTR szHost
, DWORD length
)
4906 FIXME("STUB: flags=%d host=%s length=%d\n",flags
,szHost
,length
);