push c89211be2889db25484c69db361f8abc539b86e2
[wine/hacks.git] / dlls / wininet / http.c
blob693a44fa297c8f0c73242bebf1262dbfe30135da
1 /*
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
11 * Ulrich Czekalla
12 * David Hammerton
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
29 #include "config.h"
30 #include "wine/port.h"
32 #if defined(__MINGW32__) || defined (_MSC_VER)
33 #include <ws2tcpip.h>
34 #endif
36 #include <sys/types.h>
37 #ifdef HAVE_SYS_SOCKET_H
38 # include <sys/socket.h>
39 #endif
40 #ifdef HAVE_ARPA_INET_H
41 # include <arpa/inet.h>
42 #endif
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #ifdef HAVE_UNISTD_H
47 # include <unistd.h>
48 #endif
49 #include <time.h>
50 #include <assert.h>
51 #ifdef HAVE_ZLIB
52 # include <zlib.h>
53 #endif
55 #include "windef.h"
56 #include "winbase.h"
57 #include "wininet.h"
58 #include "winerror.h"
59 #define NO_SHLWAPI_STREAM
60 #define NO_SHLWAPI_REG
61 #define NO_SHLWAPI_STRFCNS
62 #define NO_SHLWAPI_GDI
63 #include "shlwapi.h"
64 #include "sspi.h"
65 #include "wincrypt.h"
67 #include "internet.h"
68 #include "wine/debug.h"
69 #include "wine/exception.h"
70 #include "wine/unicode.h"
72 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
74 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
75 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
76 static const WCHAR hostW[] = { 'H','o','s','t',0 };
77 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
78 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
79 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
80 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
81 static const WCHAR szGET[] = { 'G','E','T', 0 };
82 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
83 static const WCHAR szCrLf[] = {'\r','\n', 0};
85 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
86 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
87 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
88 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
89 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
90 static const WCHAR szAge[] = { 'A','g','e',0 };
91 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
92 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
93 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
94 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
95 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
96 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
97 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
98 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
99 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
100 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
101 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
102 static const WCHAR szContent_Transfer_Encoding[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
103 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
104 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
105 static const WCHAR szDate[] = { 'D','a','t','e',0 };
106 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
107 static const WCHAR szETag[] = { 'E','T','a','g',0 };
108 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
109 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
110 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
111 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
112 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
113 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
114 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
115 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
116 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
117 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
118 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
119 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
120 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
121 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
122 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
123 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
124 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
125 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
126 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
127 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
128 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
129 static const WCHAR szUnless_Modified_Since[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
130 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
131 static const WCHAR szURI[] = { 'U','R','I',0 };
132 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
133 static const WCHAR szVary[] = { 'V','a','r','y',0 };
134 static const WCHAR szVia[] = { 'V','i','a',0 };
135 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
136 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
138 #define MAXHOSTNAME 100
139 #define MAX_FIELD_VALUE_LEN 256
140 #define MAX_FIELD_LEN 256
142 #define HTTP_REFERER szReferer
143 #define HTTP_ACCEPT szAccept
144 #define HTTP_USERAGENT szUser_Agent
146 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
147 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
148 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
149 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
150 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
151 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
152 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
154 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
156 struct HttpAuthInfo
158 LPWSTR scheme;
159 CredHandle cred;
160 CtxtHandle ctx;
161 TimeStamp exp;
162 ULONG attr;
163 ULONG max_token;
164 void *auth_data;
165 unsigned int auth_data_len;
166 BOOL finished; /* finished authenticating */
170 struct gzip_stream_t {
171 #ifdef HAVE_ZLIB
172 z_stream zstream;
173 #endif
174 BYTE buf[8192];
175 DWORD buf_size;
176 DWORD buf_pos;
177 BOOL end_of_data;
180 static BOOL HTTP_OpenConnection(http_request_t *req);
181 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
182 static BOOL HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
183 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
184 static BOOL HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
185 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
186 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
187 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
188 static BOOL HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
189 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
190 static BOOL HTTP_HandleRedirect(http_request_t *req, LPCWSTR lpszUrl);
191 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
192 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
193 static void HTTP_DrainContent(http_request_t *req);
194 static BOOL HTTP_FinishedReading(http_request_t *req);
196 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
198 int HeaderIndex = 0;
199 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
200 if (HeaderIndex == -1)
201 return NULL;
202 else
203 return &req->pCustHeaders[HeaderIndex];
206 #ifdef HAVE_ZLIB
208 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
210 return HeapAlloc(GetProcessHeap(), 0, items*size);
213 static void wininet_zfree(voidpf opaque, voidpf address)
215 HeapFree(GetProcessHeap(), 0, address);
218 static void init_gzip_stream(http_request_t *req)
220 gzip_stream_t *gzip_stream;
221 int zres;
223 gzip_stream = HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t));
224 gzip_stream->zstream.zalloc = wininet_zalloc;
225 gzip_stream->zstream.zfree = wininet_zfree;
226 gzip_stream->zstream.opaque = NULL;
227 gzip_stream->zstream.next_in = NULL;
228 gzip_stream->zstream.avail_in = 0;
229 gzip_stream->zstream.next_out = NULL;
230 gzip_stream->zstream.avail_out = 0;
231 gzip_stream->buf_pos = 0;
232 gzip_stream->buf_size = 0;
233 gzip_stream->end_of_data = FALSE;
235 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
236 if(zres != Z_OK) {
237 ERR("inflateInit failed: %d\n", zres);
238 HeapFree(GetProcessHeap(), 0, gzip_stream);
239 return;
242 req->gzip_stream = gzip_stream;
245 #else
247 static void init_gzip_stream(http_request_t *req)
249 ERR("gzip stream not supported, missing zlib.\n");
252 #endif
254 /* set the request content length based on the headers */
255 static DWORD set_content_length( http_request_t *lpwhr )
257 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
258 WCHAR encoding[20];
259 DWORD size;
261 size = sizeof(lpwhr->dwContentLength);
262 if (!HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
263 &lpwhr->dwContentLength, &size, NULL))
264 lpwhr->dwContentLength = ~0u;
266 size = sizeof(encoding);
267 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) &&
268 !strcmpiW(encoding, szChunked))
270 lpwhr->dwContentLength = ~0u;
271 lpwhr->read_chunked = TRUE;
274 if(lpwhr->decoding) {
275 int encoding_idx;
277 static const WCHAR gzipW[] = {'g','z','i','p',0};
279 encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
280 if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
281 init_gzip_stream(lpwhr);
284 return lpwhr->dwContentLength;
287 /***********************************************************************
288 * HTTP_Tokenize (internal)
290 * Tokenize a string, allocating memory for the tokens.
292 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
294 LPWSTR * token_array;
295 int tokens = 0;
296 int i;
297 LPCWSTR next_token;
299 if (string)
301 /* empty string has no tokens */
302 if (*string)
303 tokens++;
304 /* count tokens */
305 for (i = 0; string[i]; i++)
307 if (!strncmpW(string+i, token_string, strlenW(token_string)))
309 DWORD j;
310 tokens++;
311 /* we want to skip over separators, but not the null terminator */
312 for (j = 0; j < strlenW(token_string) - 1; j++)
313 if (!string[i+j])
314 break;
315 i += j;
320 /* add 1 for terminating NULL */
321 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
322 token_array[tokens] = NULL;
323 if (!tokens)
324 return token_array;
325 for (i = 0; i < tokens; i++)
327 int len;
328 next_token = strstrW(string, token_string);
329 if (!next_token) next_token = string+strlenW(string);
330 len = next_token - string;
331 token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
332 memcpy(token_array[i], string, len*sizeof(WCHAR));
333 token_array[i][len] = '\0';
334 string = next_token+strlenW(token_string);
336 return token_array;
339 /***********************************************************************
340 * HTTP_FreeTokens (internal)
342 * Frees memory returned from HTTP_Tokenize.
344 static void HTTP_FreeTokens(LPWSTR * token_array)
346 int i;
347 for (i = 0; token_array[i]; i++)
348 HeapFree(GetProcessHeap(), 0, token_array[i]);
349 HeapFree(GetProcessHeap(), 0, token_array);
352 /* **********************************************************************
354 * Helper functions for the HttpSendRequest(Ex) functions
357 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
359 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
360 http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
362 TRACE("%p\n", lpwhr);
364 HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
365 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
366 req->dwContentLength, req->bEndRequest);
368 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
371 static void HTTP_FixURL(http_request_t *lpwhr)
373 static const WCHAR szSlash[] = { '/',0 };
374 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
376 /* If we don't have a path we set it to root */
377 if (NULL == lpwhr->lpszPath)
378 lpwhr->lpszPath = heap_strdupW(szSlash);
379 else /* remove \r and \n*/
381 int nLen = strlenW(lpwhr->lpszPath);
382 while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
384 nLen--;
385 lpwhr->lpszPath[nLen]='\0';
387 /* Replace '\' with '/' */
388 while (nLen>0) {
389 nLen--;
390 if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
394 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
395 lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
396 && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
398 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
399 (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
400 *fixurl = '/';
401 strcpyW(fixurl + 1, lpwhr->lpszPath);
402 HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
403 lpwhr->lpszPath = fixurl;
407 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
409 LPWSTR requestString;
410 DWORD len, n;
411 LPCWSTR *req;
412 UINT i;
413 LPWSTR p;
415 static const WCHAR szSpace[] = { ' ',0 };
416 static const WCHAR szColon[] = { ':',' ',0 };
417 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
419 /* allocate space for an array of all the string pointers to be added */
420 len = (lpwhr->nCustHeaders)*4 + 10;
421 req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
423 /* add the verb, path and HTTP version string */
424 n = 0;
425 req[n++] = verb;
426 req[n++] = szSpace;
427 req[n++] = path;
428 req[n++] = szSpace;
429 req[n++] = version;
431 /* Append custom request headers */
432 for (i = 0; i < lpwhr->nCustHeaders; i++)
434 if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
436 req[n++] = szCrLf;
437 req[n++] = lpwhr->pCustHeaders[i].lpszField;
438 req[n++] = szColon;
439 req[n++] = lpwhr->pCustHeaders[i].lpszValue;
441 TRACE("Adding custom header %s (%s)\n",
442 debugstr_w(lpwhr->pCustHeaders[i].lpszField),
443 debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
447 if( n >= len )
448 ERR("oops. buffer overrun\n");
450 req[n] = NULL;
451 requestString = HTTP_build_req( req, 4 );
452 HeapFree( GetProcessHeap(), 0, req );
455 * Set (header) termination string for request
456 * Make sure there's exactly two new lines at the end of the request
458 p = &requestString[strlenW(requestString)-1];
459 while ( (*p == '\n') || (*p == '\r') )
460 p--;
461 strcpyW( p+1, sztwocrlf );
463 return requestString;
466 static void HTTP_ProcessCookies( http_request_t *lpwhr )
468 int HeaderIndex;
469 int numCookies = 0;
470 LPHTTPHEADERW setCookieHeader;
472 while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
474 setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
476 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
478 int len;
479 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
480 LPWSTR buf_url;
481 LPHTTPHEADERW Host;
483 Host = HTTP_GetHeader(lpwhr, hostW);
484 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
485 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
486 sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
487 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
489 HeapFree(GetProcessHeap(), 0, buf_url);
491 numCookies++;
495 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue )
497 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
498 return !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
499 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
502 static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
503 struct HttpAuthInfo **ppAuthInfo,
504 LPWSTR domain_and_username, LPWSTR password )
506 SECURITY_STATUS sec_status;
507 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
508 BOOL first = FALSE;
510 TRACE("%s\n", debugstr_w(pszAuthValue));
512 if (!pAuthInfo)
514 TimeStamp exp;
516 first = TRUE;
517 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
518 if (!pAuthInfo)
519 return FALSE;
521 SecInvalidateHandle(&pAuthInfo->cred);
522 SecInvalidateHandle(&pAuthInfo->ctx);
523 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
524 pAuthInfo->attr = 0;
525 pAuthInfo->auth_data = NULL;
526 pAuthInfo->auth_data_len = 0;
527 pAuthInfo->finished = FALSE;
529 if (is_basic_auth_value(pszAuthValue))
531 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
532 pAuthInfo->scheme = heap_strdupW(szBasic);
533 if (!pAuthInfo->scheme)
535 HeapFree(GetProcessHeap(), 0, pAuthInfo);
536 return FALSE;
539 else
541 PVOID pAuthData;
542 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
544 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
545 if (!pAuthInfo->scheme)
547 HeapFree(GetProcessHeap(), 0, pAuthInfo);
548 return FALSE;
551 if (domain_and_username)
553 WCHAR *user = strchrW(domain_and_username, '\\');
554 WCHAR *domain = domain_and_username;
556 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
558 pAuthData = &nt_auth_identity;
560 if (user) user++;
561 else
563 user = domain_and_username;
564 domain = NULL;
567 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
568 nt_auth_identity.User = user;
569 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
570 nt_auth_identity.Domain = domain;
571 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
572 nt_auth_identity.Password = password;
573 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
575 else
576 /* use default credentials */
577 pAuthData = NULL;
579 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
580 SECPKG_CRED_OUTBOUND, NULL,
581 pAuthData, NULL,
582 NULL, &pAuthInfo->cred,
583 &exp);
584 if (sec_status == SEC_E_OK)
586 PSecPkgInfoW sec_pkg_info;
587 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
588 if (sec_status == SEC_E_OK)
590 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
591 FreeContextBuffer(sec_pkg_info);
594 if (sec_status != SEC_E_OK)
596 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
597 debugstr_w(pAuthInfo->scheme), sec_status);
598 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
599 HeapFree(GetProcessHeap(), 0, pAuthInfo);
600 return FALSE;
603 *ppAuthInfo = pAuthInfo;
605 else if (pAuthInfo->finished)
606 return FALSE;
608 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
609 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
611 ERR("authentication scheme changed from %s to %s\n",
612 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
613 return FALSE;
616 if (is_basic_auth_value(pszAuthValue))
618 int userlen;
619 int passlen;
620 char *auth_data;
622 TRACE("basic authentication\n");
624 /* we don't cache credentials for basic authentication, so we can't
625 * retrieve them if the application didn't pass us any credentials */
626 if (!domain_and_username) return FALSE;
628 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
629 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
631 /* length includes a nul terminator, which will be re-used for the ':' */
632 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
633 if (!auth_data)
634 return FALSE;
636 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
637 auth_data[userlen] = ':';
638 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
640 pAuthInfo->auth_data = auth_data;
641 pAuthInfo->auth_data_len = userlen + 1 + passlen;
642 pAuthInfo->finished = TRUE;
644 return TRUE;
646 else
648 LPCWSTR pszAuthData;
649 SecBufferDesc out_desc, in_desc;
650 SecBuffer out, in;
651 unsigned char *buffer;
652 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
653 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
655 in.BufferType = SECBUFFER_TOKEN;
656 in.cbBuffer = 0;
657 in.pvBuffer = NULL;
659 in_desc.ulVersion = 0;
660 in_desc.cBuffers = 1;
661 in_desc.pBuffers = &in;
663 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
664 if (*pszAuthData == ' ')
666 pszAuthData++;
667 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
668 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
669 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
672 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
674 out.BufferType = SECBUFFER_TOKEN;
675 out.cbBuffer = pAuthInfo->max_token;
676 out.pvBuffer = buffer;
678 out_desc.ulVersion = 0;
679 out_desc.cBuffers = 1;
680 out_desc.pBuffers = &out;
682 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
683 first ? NULL : &pAuthInfo->ctx,
684 first ? lpwhr->lpHttpSession->lpszServerName : NULL,
685 context_req, 0, SECURITY_NETWORK_DREP,
686 in.pvBuffer ? &in_desc : NULL,
687 0, &pAuthInfo->ctx, &out_desc,
688 &pAuthInfo->attr, &pAuthInfo->exp);
689 if (sec_status == SEC_E_OK)
691 pAuthInfo->finished = TRUE;
692 pAuthInfo->auth_data = out.pvBuffer;
693 pAuthInfo->auth_data_len = out.cbBuffer;
694 TRACE("sending last auth packet\n");
696 else if (sec_status == SEC_I_CONTINUE_NEEDED)
698 pAuthInfo->auth_data = out.pvBuffer;
699 pAuthInfo->auth_data_len = out.cbBuffer;
700 TRACE("sending next auth packet\n");
702 else
704 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
705 pAuthInfo->finished = TRUE;
706 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
707 return FALSE;
711 return TRUE;
714 /***********************************************************************
715 * HTTP_HttpAddRequestHeadersW (internal)
717 static BOOL HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
718 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
720 LPWSTR lpszStart;
721 LPWSTR lpszEnd;
722 LPWSTR buffer;
723 BOOL bSuccess = FALSE;
724 DWORD len;
726 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
728 if( dwHeaderLength == ~0U )
729 len = strlenW(lpszHeader);
730 else
731 len = dwHeaderLength;
732 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
733 lstrcpynW( buffer, lpszHeader, len + 1);
735 lpszStart = buffer;
739 LPWSTR * pFieldAndValue;
741 lpszEnd = lpszStart;
743 while (*lpszEnd != '\0')
745 if (*lpszEnd == '\r' || *lpszEnd == '\n')
746 break;
747 lpszEnd++;
750 if (*lpszStart == '\0')
751 break;
753 if (*lpszEnd == '\r' || *lpszEnd == '\n')
755 *lpszEnd = '\0';
756 lpszEnd++; /* Jump over newline */
758 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
759 if (*lpszStart == '\0')
761 /* Skip 0-length headers */
762 lpszStart = lpszEnd;
763 bSuccess = TRUE;
764 continue;
766 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
767 if (pFieldAndValue)
769 bSuccess = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
770 if (bSuccess)
771 bSuccess = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
772 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
773 HTTP_FreeTokens(pFieldAndValue);
776 lpszStart = lpszEnd;
777 } while (bSuccess);
779 HeapFree(GetProcessHeap(), 0, buffer);
781 return bSuccess;
784 /***********************************************************************
785 * HttpAddRequestHeadersW (WININET.@)
787 * Adds one or more HTTP header to the request handler
789 * NOTE
790 * On Windows if dwHeaderLength includes the trailing '\0', then
791 * HttpAddRequestHeadersW() adds it too. However this results in an
792 * invalid Http header which is rejected by some servers so we probably
793 * don't need to match Windows on that point.
795 * RETURNS
796 * TRUE on success
797 * FALSE on failure
800 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
801 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
803 BOOL bSuccess = FALSE;
804 http_request_t *lpwhr;
806 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
808 if (!lpszHeader)
809 return TRUE;
811 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
812 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
814 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
815 goto lend;
817 bSuccess = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
818 lend:
819 if( lpwhr )
820 WININET_Release( &lpwhr->hdr );
822 return bSuccess;
825 /***********************************************************************
826 * HttpAddRequestHeadersA (WININET.@)
828 * Adds one or more HTTP header to the request handler
830 * RETURNS
831 * TRUE on success
832 * FALSE on failure
835 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
836 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
838 DWORD len;
839 LPWSTR hdr;
840 BOOL r;
842 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
844 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
845 hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
846 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
847 if( dwHeaderLength != ~0U )
848 dwHeaderLength = len;
850 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
852 HeapFree( GetProcessHeap(), 0, hdr );
854 return r;
857 /***********************************************************************
858 * HttpEndRequestA (WININET.@)
860 * Ends an HTTP request that was started by HttpSendRequestEx
862 * RETURNS
863 * TRUE if successful
864 * FALSE on failure
867 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
868 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
870 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
872 if (lpBuffersOut)
874 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
875 return FALSE;
878 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
881 static BOOL HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
883 BOOL rc = FALSE;
884 INT responseLen;
885 DWORD dwBufferSize;
886 INTERNET_ASYNC_RESULT iar;
888 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
889 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
891 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
892 if (responseLen)
893 rc = TRUE;
895 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
896 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
898 /* process cookies here. Is this right? */
899 HTTP_ProcessCookies(lpwhr);
901 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
903 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
905 DWORD dwCode,dwCodeLength = sizeof(DWORD);
906 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) &&
907 (dwCode == 302 || dwCode == 301 || dwCode == 303))
909 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
910 dwBufferSize=sizeof(szNewLocation);
911 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL))
913 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
915 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
916 lpwhr->lpszVerb = heap_strdupW(szGET);
918 HTTP_DrainContent(lpwhr);
919 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
921 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
922 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
923 rc = HTTP_HandleRedirect(lpwhr, new_url);
924 if (rc)
925 rc = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
926 HeapFree( GetProcessHeap(), 0, new_url );
932 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
933 iar.dwError = rc ? 0 : INTERNET_GetLastError();
935 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
936 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
937 sizeof(INTERNET_ASYNC_RESULT));
938 return rc;
941 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
943 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
944 http_request_t *lpwhr = (http_request_t*)work->hdr;
946 TRACE("%p\n", lpwhr);
948 HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
951 /***********************************************************************
952 * HttpEndRequestW (WININET.@)
954 * Ends an HTTP request that was started by HttpSendRequestEx
956 * RETURNS
957 * TRUE if successful
958 * FALSE on failure
961 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
962 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
964 BOOL rc = FALSE;
965 http_request_t *lpwhr;
967 TRACE("-->\n");
969 if (lpBuffersOut)
971 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
972 return FALSE;
975 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
977 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
979 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
980 if (lpwhr)
981 WININET_Release( &lpwhr->hdr );
982 return FALSE;
984 lpwhr->hdr.dwFlags |= dwFlags;
986 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
988 WORKREQUEST work;
989 struct WORKREQ_HTTPENDREQUESTW *request;
991 work.asyncproc = AsyncHttpEndRequestProc;
992 work.hdr = WININET_AddRef( &lpwhr->hdr );
994 request = &work.u.HttpEndRequestW;
995 request->dwFlags = dwFlags;
996 request->dwContext = dwContext;
998 INTERNET_AsyncCall(&work);
999 INTERNET_SetLastError(ERROR_IO_PENDING);
1001 else
1002 rc = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
1004 WININET_Release( &lpwhr->hdr );
1005 TRACE("%i <--\n",rc);
1006 return rc;
1009 /***********************************************************************
1010 * HttpOpenRequestW (WININET.@)
1012 * Open a HTTP request handle
1014 * RETURNS
1015 * HINTERNET a HTTP request handle on success
1016 * NULL on failure
1019 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
1020 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
1021 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
1022 DWORD dwFlags, DWORD_PTR dwContext)
1024 http_session_t *lpwhs;
1025 HINTERNET handle = NULL;
1027 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1028 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
1029 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
1030 dwFlags, dwContext);
1031 if(lpszAcceptTypes!=NULL)
1033 int i;
1034 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
1035 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
1038 lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
1039 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
1041 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1042 goto lend;
1046 * My tests seem to show that the windows version does not
1047 * become asynchronous until after this point. And anyhow
1048 * if this call was asynchronous then how would you get the
1049 * necessary HINTERNET pointer returned by this function.
1052 handle = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
1053 lpszVersion, lpszReferrer, lpszAcceptTypes,
1054 dwFlags, dwContext);
1055 lend:
1056 if( lpwhs )
1057 WININET_Release( &lpwhs->hdr );
1058 TRACE("returning %p\n", handle);
1059 return handle;
1063 /***********************************************************************
1064 * HttpOpenRequestA (WININET.@)
1066 * Open a HTTP request handle
1068 * RETURNS
1069 * HINTERNET a HTTP request handle on success
1070 * NULL on failure
1073 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1074 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1075 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1076 DWORD dwFlags, DWORD_PTR dwContext)
1078 LPWSTR szVerb = NULL, szObjectName = NULL;
1079 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1080 INT acceptTypesCount;
1081 HINTERNET rc = FALSE;
1082 LPCSTR *types;
1084 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1085 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1086 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1087 dwFlags, dwContext);
1089 if (lpszVerb)
1091 szVerb = heap_strdupAtoW(lpszVerb);
1092 if ( !szVerb )
1093 goto end;
1096 if (lpszObjectName)
1098 szObjectName = heap_strdupAtoW(lpszObjectName);
1099 if ( !szObjectName )
1100 goto end;
1103 if (lpszVersion)
1105 szVersion = heap_strdupAtoW(lpszVersion);
1106 if ( !szVersion )
1107 goto end;
1110 if (lpszReferrer)
1112 szReferrer = heap_strdupAtoW(lpszReferrer);
1113 if ( !szReferrer )
1114 goto end;
1117 if (lpszAcceptTypes)
1119 acceptTypesCount = 0;
1120 types = lpszAcceptTypes;
1121 while (*types)
1123 __TRY
1125 /* find out how many there are */
1126 if (*types && **types)
1128 TRACE("accept type: %s\n", debugstr_a(*types));
1129 acceptTypesCount++;
1132 __EXCEPT_PAGE_FAULT
1134 WARN("invalid accept type pointer\n");
1136 __ENDTRY;
1137 types++;
1139 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1140 if (!szAcceptTypes) goto end;
1142 acceptTypesCount = 0;
1143 types = lpszAcceptTypes;
1144 while (*types)
1146 __TRY
1148 if (*types && **types)
1149 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1151 __EXCEPT_PAGE_FAULT
1153 /* ignore invalid pointer */
1155 __ENDTRY;
1156 types++;
1158 szAcceptTypes[acceptTypesCount] = NULL;
1161 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1162 szVersion, szReferrer,
1163 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1165 end:
1166 if (szAcceptTypes)
1168 acceptTypesCount = 0;
1169 while (szAcceptTypes[acceptTypesCount])
1171 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1172 acceptTypesCount++;
1174 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1176 HeapFree(GetProcessHeap(), 0, szReferrer);
1177 HeapFree(GetProcessHeap(), 0, szVersion);
1178 HeapFree(GetProcessHeap(), 0, szObjectName);
1179 HeapFree(GetProcessHeap(), 0, szVerb);
1181 return rc;
1184 /***********************************************************************
1185 * HTTP_EncodeBase64
1187 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1189 UINT n = 0, x;
1190 static const CHAR HTTP_Base64Enc[] =
1191 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1193 while( len > 0 )
1195 /* first 6 bits, all from bin[0] */
1196 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1197 x = (bin[0] & 3) << 4;
1199 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1200 if( len == 1 )
1202 base64[n++] = HTTP_Base64Enc[x];
1203 base64[n++] = '=';
1204 base64[n++] = '=';
1205 break;
1207 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1208 x = ( bin[1] & 0x0f ) << 2;
1210 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1211 if( len == 2 )
1213 base64[n++] = HTTP_Base64Enc[x];
1214 base64[n++] = '=';
1215 break;
1217 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1219 /* last 6 bits, all from bin [2] */
1220 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1221 bin += 3;
1222 len -= 3;
1224 base64[n] = 0;
1225 return n;
1228 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1229 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1230 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1231 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1232 static const signed char HTTP_Base64Dec[256] =
1234 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1235 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1236 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1237 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1238 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1239 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1240 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1241 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1242 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1243 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1244 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1245 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1246 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1247 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1248 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1249 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1250 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1251 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1252 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1253 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1254 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1255 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1256 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1257 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1258 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1259 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1261 #undef CH
1263 /***********************************************************************
1264 * HTTP_DecodeBase64
1266 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1268 unsigned int n = 0;
1270 while(*base64)
1272 signed char in[4];
1274 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1275 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1276 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1277 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1279 WARN("invalid base64: %s\n", debugstr_w(base64));
1280 return 0;
1282 if (bin)
1283 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1284 n++;
1286 if ((base64[2] == '=') && (base64[3] == '='))
1287 break;
1288 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1289 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1291 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1292 return 0;
1294 if (bin)
1295 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1296 n++;
1298 if (base64[3] == '=')
1299 break;
1300 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1301 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1303 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1304 return 0;
1306 if (bin)
1307 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1308 n++;
1310 base64 += 4;
1313 return n;
1316 /***********************************************************************
1317 * HTTP_InsertAuthorization
1319 * Insert or delete the authorization field in the request header.
1321 static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1323 if (pAuthInfo)
1325 static const WCHAR wszSpace[] = {' ',0};
1326 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1327 unsigned int len;
1328 WCHAR *authorization = NULL;
1330 if (pAuthInfo->auth_data_len)
1332 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1333 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1334 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1335 if (!authorization)
1336 return FALSE;
1338 strcpyW(authorization, pAuthInfo->scheme);
1339 strcatW(authorization, wszSpace);
1340 HTTP_EncodeBase64(pAuthInfo->auth_data,
1341 pAuthInfo->auth_data_len,
1342 authorization+strlenW(authorization));
1344 /* clear the data as it isn't valid now that it has been sent to the
1345 * server, unless it's Basic authentication which doesn't do
1346 * connection tracking */
1347 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1349 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1350 pAuthInfo->auth_data = NULL;
1351 pAuthInfo->auth_data_len = 0;
1355 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1357 HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1359 HeapFree(GetProcessHeap(), 0, authorization);
1361 return TRUE;
1364 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1366 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1367 DWORD size;
1369 size = sizeof(new_location);
1370 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL))
1372 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1373 strcpyW( url, new_location );
1375 else
1377 static const WCHAR slash[] = { '/',0 };
1378 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1379 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1380 http_session_t *session = req->lpHttpSession;
1382 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1383 size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
1385 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1387 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1388 sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
1389 else
1390 sprintfW( url, format, session->lpszHostName, session->nHostPort );
1391 if (req->lpszPath[0] != '/') strcatW( url, slash );
1392 strcatW( url, req->lpszPath );
1394 TRACE("url=%s\n", debugstr_w(url));
1395 return url;
1398 /***********************************************************************
1399 * HTTP_DealWithProxy
1401 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
1403 WCHAR buf[MAXHOSTNAME];
1404 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1405 static WCHAR szNul[] = { 0 };
1406 URL_COMPONENTSW UrlComponents;
1407 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1408 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1410 memset( &UrlComponents, 0, sizeof UrlComponents );
1411 UrlComponents.dwStructSize = sizeof UrlComponents;
1412 UrlComponents.lpszHostName = buf;
1413 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1415 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1416 hIC->lpszProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1417 sprintfW(proxy, szFormat, hIC->lpszProxy);
1418 else
1419 strcpyW(proxy, hIC->lpszProxy);
1420 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1421 return FALSE;
1422 if( UrlComponents.dwHostNameLength == 0 )
1423 return FALSE;
1425 if( !lpwhr->lpszPath )
1426 lpwhr->lpszPath = szNul;
1428 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1429 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1431 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1432 lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1433 lpwhs->nServerPort = UrlComponents.nPort;
1435 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1436 return TRUE;
1439 #ifndef INET6_ADDRSTRLEN
1440 #define INET6_ADDRSTRLEN 46
1441 #endif
1443 static BOOL HTTP_ResolveName(http_request_t *lpwhr)
1445 char szaddr[INET6_ADDRSTRLEN];
1446 http_session_t *lpwhs = lpwhr->lpHttpSession;
1447 const void *addr;
1449 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1450 INTERNET_STATUS_RESOLVING_NAME,
1451 lpwhs->lpszServerName,
1452 strlenW(lpwhs->lpszServerName)+1);
1454 lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1455 if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1456 (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1458 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1459 return FALSE;
1462 switch (lpwhs->socketAddress.ss_family)
1464 case AF_INET:
1465 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1466 break;
1467 case AF_INET6:
1468 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1469 break;
1470 default:
1471 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1472 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1473 return FALSE;
1475 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1476 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1477 INTERNET_STATUS_NAME_RESOLVED,
1478 szaddr, strlen(szaddr)+1);
1480 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1481 return TRUE;
1485 /***********************************************************************
1486 * HTTPREQ_Destroy (internal)
1488 * Deallocate request handle
1491 static void HTTPREQ_Destroy(object_header_t *hdr)
1493 http_request_t *lpwhr = (http_request_t*) hdr;
1494 DWORD i;
1496 TRACE("\n");
1498 if(lpwhr->hCacheFile)
1499 CloseHandle(lpwhr->hCacheFile);
1501 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1503 DeleteCriticalSection( &lpwhr->read_section );
1504 WININET_Release(&lpwhr->lpHttpSession->hdr);
1506 if (lpwhr->pAuthInfo)
1508 if (SecIsValidHandle(&lpwhr->pAuthInfo->ctx))
1509 DeleteSecurityContext(&lpwhr->pAuthInfo->ctx);
1510 if (SecIsValidHandle(&lpwhr->pAuthInfo->cred))
1511 FreeCredentialsHandle(&lpwhr->pAuthInfo->cred);
1513 HeapFree(GetProcessHeap(), 0, lpwhr->pAuthInfo->auth_data);
1514 HeapFree(GetProcessHeap(), 0, lpwhr->pAuthInfo->scheme);
1515 HeapFree(GetProcessHeap(), 0, lpwhr->pAuthInfo);
1516 lpwhr->pAuthInfo = NULL;
1519 if (lpwhr->pProxyAuthInfo)
1521 if (SecIsValidHandle(&lpwhr->pProxyAuthInfo->ctx))
1522 DeleteSecurityContext(&lpwhr->pProxyAuthInfo->ctx);
1523 if (SecIsValidHandle(&lpwhr->pProxyAuthInfo->cred))
1524 FreeCredentialsHandle(&lpwhr->pProxyAuthInfo->cred);
1526 HeapFree(GetProcessHeap(), 0, lpwhr->pProxyAuthInfo->auth_data);
1527 HeapFree(GetProcessHeap(), 0, lpwhr->pProxyAuthInfo->scheme);
1528 HeapFree(GetProcessHeap(), 0, lpwhr->pProxyAuthInfo);
1529 lpwhr->pProxyAuthInfo = NULL;
1532 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1533 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1534 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1535 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1536 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1538 for (i = 0; i < lpwhr->nCustHeaders; i++)
1540 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1541 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1544 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1545 HeapFree(GetProcessHeap(), 0, lpwhr);
1548 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1550 http_request_t *lpwhr = (http_request_t*) hdr;
1552 TRACE("%p\n",lpwhr);
1554 #ifdef HAVE_ZLIB
1555 if(lpwhr->gzip_stream) {
1556 inflateEnd(&lpwhr->gzip_stream->zstream);
1557 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1558 lpwhr->gzip_stream = NULL;
1560 #endif
1562 if (!NETCON_connected(&lpwhr->netConnection))
1563 return;
1565 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1566 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1568 NETCON_close(&lpwhr->netConnection);
1570 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1571 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1574 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1576 LPHTTPHEADERW host_header;
1578 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1580 host_header = HTTP_GetHeader(req, hostW);
1581 if(!host_header)
1582 return FALSE;
1584 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1585 return TRUE;
1588 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1590 http_request_t *req = (http_request_t*)hdr;
1592 switch(option) {
1593 case INTERNET_OPTION_SECURITY_FLAGS:
1595 http_session_t *lpwhs;
1596 lpwhs = req->lpHttpSession;
1598 if (*size < sizeof(ULONG))
1599 return ERROR_INSUFFICIENT_BUFFER;
1601 *size = sizeof(DWORD);
1602 if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1603 *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1604 else
1605 *(DWORD*)buffer = 0;
1606 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1607 return ERROR_SUCCESS;
1610 case INTERNET_OPTION_HANDLE_TYPE:
1611 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1613 if (*size < sizeof(ULONG))
1614 return ERROR_INSUFFICIENT_BUFFER;
1616 *size = sizeof(DWORD);
1617 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1618 return ERROR_SUCCESS;
1620 case INTERNET_OPTION_URL: {
1621 WCHAR url[INTERNET_MAX_URL_LENGTH];
1622 HTTPHEADERW *host;
1623 DWORD len;
1624 WCHAR *pch;
1626 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1628 TRACE("INTERNET_OPTION_URL\n");
1630 host = HTTP_GetHeader(req, hostW);
1631 strcpyW(url, httpW);
1632 strcatW(url, host->lpszValue);
1633 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1634 *pch = 0;
1635 strcatW(url, req->lpszPath);
1637 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1639 if(unicode) {
1640 len = (strlenW(url)+1) * sizeof(WCHAR);
1641 if(*size < len)
1642 return ERROR_INSUFFICIENT_BUFFER;
1644 *size = len;
1645 strcpyW(buffer, url);
1646 return ERROR_SUCCESS;
1647 }else {
1648 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1649 if(len > *size)
1650 return ERROR_INSUFFICIENT_BUFFER;
1652 *size = len;
1653 return ERROR_SUCCESS;
1657 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1658 INTERNET_CACHE_ENTRY_INFOW *info;
1659 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1660 WCHAR url[INTERNET_MAX_URL_LENGTH];
1661 DWORD nbytes, error;
1662 BOOL ret;
1664 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1666 if (*size < sizeof(*ts))
1668 *size = sizeof(*ts);
1669 return ERROR_INSUFFICIENT_BUFFER;
1671 nbytes = 0;
1672 HTTP_GetRequestURL(req, url);
1673 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1674 error = GetLastError();
1675 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1677 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1678 return ERROR_OUTOFMEMORY;
1680 GetUrlCacheEntryInfoW(url, info, &nbytes);
1682 ts->ftExpires = info->ExpireTime;
1683 ts->ftLastModified = info->LastModifiedTime;
1685 HeapFree(GetProcessHeap(), 0, info);
1686 *size = sizeof(*ts);
1687 return ERROR_SUCCESS;
1689 return error;
1692 case INTERNET_OPTION_DATAFILE_NAME: {
1693 DWORD req_size;
1695 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1697 if(!req->lpszCacheFile) {
1698 *size = 0;
1699 return ERROR_INTERNET_ITEM_NOT_FOUND;
1702 if(unicode) {
1703 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1704 if(*size < req_size)
1705 return ERROR_INSUFFICIENT_BUFFER;
1707 *size = req_size;
1708 memcpy(buffer, req->lpszCacheFile, *size);
1709 return ERROR_SUCCESS;
1710 }else {
1711 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1712 if (req_size > *size)
1713 return ERROR_INSUFFICIENT_BUFFER;
1715 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1716 -1, buffer, *size, NULL, NULL);
1717 return ERROR_SUCCESS;
1721 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1722 PCCERT_CONTEXT context;
1724 if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1725 *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1726 return ERROR_INSUFFICIENT_BUFFER;
1729 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1730 if(context) {
1731 INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1732 DWORD len;
1734 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1735 info->ftExpiry = context->pCertInfo->NotAfter;
1736 info->ftStart = context->pCertInfo->NotBefore;
1737 if(unicode) {
1738 len = CertNameToStrW(context->dwCertEncodingType,
1739 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1740 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1741 if(info->lpszSubjectInfo)
1742 CertNameToStrW(context->dwCertEncodingType,
1743 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1744 info->lpszSubjectInfo, len);
1745 len = CertNameToStrW(context->dwCertEncodingType,
1746 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1747 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1748 if (info->lpszIssuerInfo)
1749 CertNameToStrW(context->dwCertEncodingType,
1750 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1751 info->lpszIssuerInfo, len);
1752 }else {
1753 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1755 len = CertNameToStrA(context->dwCertEncodingType,
1756 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1757 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1758 if(infoA->lpszSubjectInfo)
1759 CertNameToStrA(context->dwCertEncodingType,
1760 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1761 infoA->lpszSubjectInfo, len);
1762 len = CertNameToStrA(context->dwCertEncodingType,
1763 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1764 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1765 if(infoA->lpszIssuerInfo)
1766 CertNameToStrA(context->dwCertEncodingType,
1767 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1768 infoA->lpszIssuerInfo, len);
1772 * Contrary to MSDN, these do not appear to be set.
1773 * lpszProtocolName
1774 * lpszSignatureAlgName
1775 * lpszEncryptionAlgName
1776 * dwKeySize
1778 CertFreeCertificateContext(context);
1779 return ERROR_SUCCESS;
1784 return INET_QueryOption(option, buffer, size, unicode);
1787 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1789 http_request_t *req = (http_request_t*)hdr;
1791 switch(option) {
1792 case INTERNET_OPTION_SEND_TIMEOUT:
1793 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1794 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1796 if (size != sizeof(DWORD))
1797 return ERROR_INVALID_PARAMETER;
1799 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1800 *(DWORD*)buffer);
1802 case INTERNET_OPTION_USERNAME:
1803 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1804 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1805 return ERROR_SUCCESS;
1807 case INTERNET_OPTION_PASSWORD:
1808 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1809 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1810 return ERROR_SUCCESS;
1811 case INTERNET_OPTION_HTTP_DECODING:
1812 if(size != sizeof(BOOL))
1813 return ERROR_INVALID_PARAMETER;
1814 req->decoding = *(BOOL*)buffer;
1815 return ERROR_SUCCESS;
1818 return ERROR_INTERNET_INVALID_OPTION;
1821 /* read some more data into the read buffer (the read section must be held) */
1822 static BOOL read_more_data( http_request_t *req, int maxlen )
1824 int len;
1826 if (req->read_pos)
1828 /* move existing data to the start of the buffer */
1829 if(req->read_size)
1830 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1831 req->read_pos = 0;
1834 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1836 if(!NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1837 maxlen - req->read_size, 0, &len ))
1838 return FALSE;
1840 req->read_size += len;
1841 return TRUE;
1844 /* remove some amount of data from the read buffer (the read section must be held) */
1845 static void remove_data( http_request_t *req, int count )
1847 if (!(req->read_size -= count)) req->read_pos = 0;
1848 else req->read_pos += count;
1851 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1853 int count, bytes_read, pos = 0;
1855 EnterCriticalSection( &req->read_section );
1856 for (;;)
1858 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1860 if (eol)
1862 count = eol - (req->read_buf + req->read_pos);
1863 bytes_read = count + 1;
1865 else count = bytes_read = req->read_size;
1867 count = min( count, *len - pos );
1868 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1869 pos += count;
1870 remove_data( req, bytes_read );
1871 if (eol) break;
1873 if (!read_more_data( req, -1 ) || !req->read_size)
1875 *len = 0;
1876 TRACE( "returning empty string\n" );
1877 LeaveCriticalSection( &req->read_section );
1878 return FALSE;
1881 LeaveCriticalSection( &req->read_section );
1883 if (pos < *len)
1885 if (pos && buffer[pos - 1] == '\r') pos--;
1886 *len = pos + 1;
1888 buffer[*len - 1] = 0;
1889 TRACE( "returning %s\n", debugstr_a(buffer));
1890 return TRUE;
1893 /* discard data contents until we reach end of line (the read section must be held) */
1894 static BOOL discard_eol( http_request_t *req )
1898 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1899 if (eol)
1901 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
1902 break;
1904 req->read_pos = req->read_size = 0; /* discard everything */
1905 if (!read_more_data( req, -1 )) return FALSE;
1906 } while (req->read_size);
1907 return TRUE;
1910 /* read the size of the next chunk (the read section must be held) */
1911 static BOOL start_next_chunk( http_request_t *req )
1913 DWORD chunk_size = 0;
1915 if (!req->dwContentLength) return TRUE;
1916 if (req->dwContentLength == req->dwContentRead)
1918 /* read terminator for the previous chunk */
1919 if (!discard_eol( req )) return FALSE;
1920 req->dwContentLength = ~0u;
1921 req->dwContentRead = 0;
1923 for (;;)
1925 while (req->read_size)
1927 char ch = req->read_buf[req->read_pos];
1928 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
1929 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
1930 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
1931 else if (ch == ';' || ch == '\r' || ch == '\n')
1933 TRACE( "reading %u byte chunk\n", chunk_size );
1934 req->dwContentLength = chunk_size;
1935 req->dwContentRead = 0;
1936 if (!discard_eol( req )) return FALSE;
1937 return TRUE;
1939 remove_data( req, 1 );
1941 if (!read_more_data( req, -1 )) return FALSE;
1942 if (!req->read_size)
1944 req->dwContentLength = req->dwContentRead = 0;
1945 return TRUE;
1950 /* check if we have reached the end of the data to read (the read section must be held) */
1951 static BOOL end_of_read_data( http_request_t *req )
1953 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
1954 if (req->read_chunked) return (req->dwContentLength == 0);
1955 if (req->dwContentLength == ~0u) return FALSE;
1956 return (req->dwContentLength == req->dwContentRead);
1959 /* fetch some more data into the read buffer (the read section must be held) */
1960 static BOOL refill_buffer( http_request_t *req )
1962 int len = sizeof(req->read_buf);
1964 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
1966 if (!start_next_chunk( req )) return FALSE;
1969 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
1970 if (len <= req->read_size) return TRUE;
1972 if (!read_more_data( req, len )) return FALSE;
1973 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
1974 return TRUE;
1977 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
1979 DWORD ret = ERROR_SUCCESS;
1980 int read = 0;
1982 #ifdef HAVE_ZLIB
1983 z_stream *zstream = &req->gzip_stream->zstream;
1984 int zres;
1986 while(read < size && !req->gzip_stream->end_of_data) {
1987 if(!req->read_size) {
1988 if(!sync || !refill_buffer(req))
1989 break;
1992 zstream->next_in = req->read_buf+req->read_pos;
1993 zstream->avail_in = req->read_size;
1994 zstream->next_out = buf+read;
1995 zstream->avail_out = size-read;
1996 zres = inflate(zstream, Z_FULL_FLUSH);
1997 read = size - zstream->avail_out;
1998 remove_data(req, req->read_size-zstream->avail_in);
1999 if(zres == Z_STREAM_END) {
2000 TRACE("end of data\n");
2001 req->gzip_stream->end_of_data = TRUE;
2002 }else if(zres != Z_OK) {
2003 WARN("inflate failed %d\n", zres);
2004 if(!read)
2005 ret = ERROR_INTERNET_DECODING_FAILED;
2006 break;
2009 #endif
2011 *read_ret = read;
2012 return ret;
2015 static void refill_gzip_buffer(http_request_t *req)
2017 DWORD res;
2018 int len;
2020 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2021 return;
2023 if(req->gzip_stream->buf_pos) {
2024 if(req->gzip_stream->buf_size)
2025 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2026 req->gzip_stream->buf_pos = 0;
2029 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2030 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2031 if(res == ERROR_SUCCESS)
2032 req->gzip_stream->buf_size += len;
2035 /* return the size of data available to be read immediately (the read section must be held) */
2036 static DWORD get_avail_data( http_request_t *req )
2038 if (req->gzip_stream) {
2039 refill_gzip_buffer(req);
2040 return req->gzip_stream->buf_size;
2042 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2043 return 0;
2044 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2047 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2049 INTERNET_ASYNC_RESULT iar;
2051 TRACE("%p\n", req);
2053 EnterCriticalSection( &req->read_section );
2054 if (refill_buffer( req )) {
2055 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2056 iar.dwError = first_notif ? 0 : get_avail_data(req);
2057 }else {
2058 iar.dwResult = 0;
2059 iar.dwError = INTERNET_GetLastError();
2061 LeaveCriticalSection( &req->read_section );
2063 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2064 sizeof(INTERNET_ASYNC_RESULT));
2067 /* read data from the http connection (the read section must be held) */
2068 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2070 BOOL finished_reading = FALSE;
2071 int len, bytes_read = 0;
2072 DWORD ret = ERROR_SUCCESS;
2074 EnterCriticalSection( &req->read_section );
2076 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2078 if (!start_next_chunk( req )) goto done;
2081 if(req->gzip_stream) {
2082 if(req->gzip_stream->buf_size) {
2083 bytes_read = min(req->gzip_stream->buf_size, size);
2084 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2085 req->gzip_stream->buf_pos += bytes_read;
2086 req->gzip_stream->buf_size -= bytes_read;
2087 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2088 refill_buffer(req);
2091 if(size > bytes_read) {
2092 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2093 if(ret == ERROR_SUCCESS)
2094 bytes_read += len;
2097 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2098 }else {
2099 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2101 if (req->read_size) {
2102 bytes_read = min( req->read_size, size );
2103 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2104 remove_data( req, bytes_read );
2107 if (size > bytes_read && (!bytes_read || sync)) {
2108 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2109 sync ? MSG_WAITALL : 0, &len))
2110 bytes_read += len;
2111 /* always return success, even if the network layer returns an error */
2114 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2116 done:
2117 req->dwContentRead += bytes_read;
2118 *read = bytes_read;
2120 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2121 LeaveCriticalSection( &req->read_section );
2123 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2124 BOOL res;
2125 DWORD dwBytesWritten;
2127 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2128 if(!res)
2129 WARN("WriteFile failed: %u\n", GetLastError());
2132 if(finished_reading)
2133 HTTP_FinishedReading(req);
2135 return ret;
2139 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2141 http_request_t *req = (http_request_t*)hdr;
2142 return HTTPREQ_Read(req, buffer, size, read, TRUE);
2145 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2147 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2148 http_request_t *req = (http_request_t*)workRequest->hdr;
2149 INTERNET_ASYNC_RESULT iar;
2150 DWORD res;
2152 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2154 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2155 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2157 iar.dwResult = res == ERROR_SUCCESS;
2158 iar.dwError = res;
2160 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2161 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2162 sizeof(INTERNET_ASYNC_RESULT));
2165 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2166 DWORD flags, DWORD_PTR context)
2168 http_request_t *req = (http_request_t*)hdr;
2169 DWORD res;
2171 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2172 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2174 if (buffers->dwStructSize != sizeof(*buffers))
2175 return ERROR_INVALID_PARAMETER;
2177 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2179 if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
2181 WORKREQUEST workRequest;
2183 if (TryEnterCriticalSection( &req->read_section ))
2185 if (get_avail_data(req))
2187 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2188 &buffers->dwBufferLength, FALSE);
2189 LeaveCriticalSection( &req->read_section );
2190 goto done;
2192 LeaveCriticalSection( &req->read_section );
2195 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2196 workRequest.hdr = WININET_AddRef(&req->hdr);
2197 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2199 INTERNET_AsyncCall(&workRequest);
2201 return ERROR_IO_PENDING;
2204 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2205 !(flags & IRF_NO_WAIT));
2207 done:
2208 if (res == ERROR_SUCCESS) {
2209 DWORD size = buffers->dwBufferLength;
2210 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2211 &size, sizeof(size));
2214 return res;
2217 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2219 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2220 http_request_t *req = (http_request_t*)workRequest->hdr;
2221 INTERNET_ASYNC_RESULT iar;
2222 DWORD res;
2224 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2226 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2227 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2229 iar.dwResult = res == ERROR_SUCCESS;
2230 iar.dwError = res;
2232 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2233 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2234 sizeof(INTERNET_ASYNC_RESULT));
2237 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2238 DWORD flags, DWORD_PTR context)
2241 http_request_t *req = (http_request_t*)hdr;
2242 DWORD res;
2244 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2245 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2247 if (buffers->dwStructSize != sizeof(*buffers))
2248 return ERROR_INVALID_PARAMETER;
2250 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2252 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2254 WORKREQUEST workRequest;
2256 if (TryEnterCriticalSection( &req->read_section ))
2258 if (get_avail_data(req))
2260 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2261 &buffers->dwBufferLength, FALSE);
2262 LeaveCriticalSection( &req->read_section );
2263 goto done;
2265 LeaveCriticalSection( &req->read_section );
2268 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2269 workRequest.hdr = WININET_AddRef(&req->hdr);
2270 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2272 INTERNET_AsyncCall(&workRequest);
2274 return ERROR_IO_PENDING;
2277 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2278 !(flags & IRF_NO_WAIT));
2280 done:
2281 if (res == ERROR_SUCCESS) {
2282 DWORD size = buffers->dwBufferLength;
2283 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2284 &size, sizeof(size));
2287 return res;
2290 static BOOL HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2292 BOOL ret;
2293 http_request_t *lpwhr = (http_request_t*)hdr;
2295 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2297 *written = 0;
2298 if ((ret = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written)))
2299 lpwhr->dwBytesWritten += *written;
2301 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2302 return ret;
2305 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2307 http_request_t *req = (http_request_t*)workRequest->hdr;
2309 HTTP_ReceiveRequestData(req, FALSE);
2312 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2314 http_request_t *req = (http_request_t*)hdr;
2316 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2318 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2320 WORKREQUEST workRequest;
2322 /* never wait, if we can't enter the section we queue an async request right away */
2323 if (TryEnterCriticalSection( &req->read_section ))
2325 if ((*available = get_avail_data( req ))) goto done;
2326 if (end_of_read_data( req )) goto done;
2327 LeaveCriticalSection( &req->read_section );
2330 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2331 workRequest.hdr = WININET_AddRef( &req->hdr );
2333 INTERNET_AsyncCall(&workRequest);
2335 return ERROR_IO_PENDING;
2338 EnterCriticalSection( &req->read_section );
2340 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2342 refill_buffer( req );
2343 *available = get_avail_data( req );
2346 done:
2347 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2349 DWORD extra;
2350 if (NETCON_query_data_available(&req->netConnection, &extra))
2351 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2353 LeaveCriticalSection( &req->read_section );
2355 TRACE( "returning %u\n", *available );
2356 return ERROR_SUCCESS;
2359 static const object_vtbl_t HTTPREQVtbl = {
2360 HTTPREQ_Destroy,
2361 HTTPREQ_CloseConnection,
2362 HTTPREQ_QueryOption,
2363 HTTPREQ_SetOption,
2364 HTTPREQ_ReadFile,
2365 HTTPREQ_ReadFileExA,
2366 HTTPREQ_ReadFileExW,
2367 HTTPREQ_WriteFile,
2368 HTTPREQ_QueryDataAvailable,
2369 NULL
2372 /***********************************************************************
2373 * HTTP_HttpOpenRequestW (internal)
2375 * Open a HTTP request handle
2377 * RETURNS
2378 * HINTERNET a HTTP request handle on success
2379 * NULL on failure
2382 HINTERNET WINAPI HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2383 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2384 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2385 DWORD dwFlags, DWORD_PTR dwContext)
2387 appinfo_t *hIC = NULL;
2388 http_request_t *lpwhr;
2389 LPWSTR lpszHostName = NULL;
2390 HINTERNET handle = NULL;
2391 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2392 DWORD len;
2394 TRACE("-->\n");
2396 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2397 hIC = lpwhs->lpAppInfo;
2399 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2400 if (NULL == lpwhr)
2402 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2403 goto lend;
2405 lpwhr->hdr.htype = WH_HHTTPREQ;
2406 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2407 lpwhr->hdr.dwFlags = dwFlags;
2408 lpwhr->hdr.dwContext = dwContext;
2409 lpwhr->hdr.refs = 1;
2410 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2411 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2412 lpwhr->dwContentLength = ~0u;
2413 InitializeCriticalSection( &lpwhr->read_section );
2415 WININET_AddRef( &lpwhs->hdr );
2416 lpwhr->lpHttpSession = lpwhs;
2417 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2419 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2420 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2421 if (NULL == lpszHostName)
2423 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2424 goto lend;
2427 handle = WININET_AllocHandle( &lpwhr->hdr );
2428 if (NULL == handle)
2430 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2431 goto lend;
2434 if (!NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE))
2436 InternetCloseHandle( handle );
2437 handle = NULL;
2438 goto lend;
2441 if (lpszObjectName && *lpszObjectName) {
2442 HRESULT rc;
2444 len = 0;
2445 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2446 if (rc != E_POINTER)
2447 len = strlenW(lpszObjectName)+1;
2448 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2449 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2450 URL_ESCAPE_SPACES_ONLY);
2451 if (rc != S_OK)
2453 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2454 strcpyW(lpwhr->lpszPath,lpszObjectName);
2456 }else {
2457 static const WCHAR slashW[] = {'/',0};
2459 lpwhr->lpszPath = heap_strdupW(slashW);
2462 if (lpszReferrer && *lpszReferrer)
2463 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2465 if (lpszAcceptTypes)
2467 int i;
2468 for (i = 0; lpszAcceptTypes[i]; i++)
2470 if (!*lpszAcceptTypes[i]) continue;
2471 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2472 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2473 HTTP_ADDHDR_FLAG_REQ |
2474 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2478 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2479 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2481 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2482 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2483 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2485 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2486 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2487 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2489 else
2490 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2491 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2493 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2494 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2495 INTERNET_DEFAULT_HTTPS_PORT :
2496 INTERNET_DEFAULT_HTTP_PORT);
2498 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2499 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2500 INTERNET_DEFAULT_HTTPS_PORT :
2501 INTERNET_DEFAULT_HTTP_PORT);
2503 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2504 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2506 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2507 INTERNET_STATUS_HANDLE_CREATED, &handle,
2508 sizeof(handle));
2510 lend:
2511 HeapFree(GetProcessHeap(), 0, lpszHostName);
2512 if( lpwhr )
2513 WININET_Release( &lpwhr->hdr );
2515 TRACE("<-- %p (%p)\n", handle, lpwhr);
2516 return handle;
2519 /* read any content returned by the server so that the connection can be
2520 * reused */
2521 static void HTTP_DrainContent(http_request_t *req)
2523 DWORD bytes_read;
2525 if (!NETCON_connected(&req->netConnection)) return;
2527 if (req->dwContentLength == -1)
2529 NETCON_close(&req->netConnection);
2530 return;
2532 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2536 char buffer[2048];
2537 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2538 return;
2539 } while (bytes_read);
2542 static const LPCWSTR header_lookup[] = {
2543 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2544 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2545 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2546 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2547 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2548 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2549 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2550 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2551 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2552 szDate, /* HTTP_QUERY_DATE = 9 */
2553 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2554 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2555 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2556 szURI, /* HTTP_QUERY_URI = 13 */
2557 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2558 NULL, /* HTTP_QUERY_COST = 15 */
2559 NULL, /* HTTP_QUERY_LINK = 16 */
2560 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2561 NULL, /* HTTP_QUERY_VERSION = 18 */
2562 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2563 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2564 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2565 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2566 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2567 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2568 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2569 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2570 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2571 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2572 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2573 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2574 NULL, /* HTTP_QUERY_FROM = 31 */
2575 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2576 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2577 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2578 szReferer, /* HTTP_QUERY_REFERER = 35 */
2579 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2580 szServer, /* HTTP_QUERY_SERVER = 37 */
2581 NULL, /* HTTP_TITLE = 38 */
2582 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2583 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2584 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2585 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2586 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2587 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2588 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2589 NULL, /* HTTP_QUERY_REFRESH = 46 */
2590 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2591 szAge, /* HTTP_QUERY_AGE = 48 */
2592 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2593 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2594 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2595 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2596 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2597 szETag, /* HTTP_QUERY_ETAG = 54 */
2598 hostW, /* HTTP_QUERY_HOST = 55 */
2599 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2600 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2601 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2602 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2603 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2604 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2605 szRange, /* HTTP_QUERY_RANGE = 62 */
2606 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2607 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2608 szVary, /* HTTP_QUERY_VARY = 65 */
2609 szVia, /* HTTP_QUERY_VIA = 66 */
2610 szWarning, /* HTTP_QUERY_WARNING = 67 */
2611 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2612 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2613 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2616 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2618 /***********************************************************************
2619 * HTTP_HttpQueryInfoW (internal)
2621 static BOOL HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2622 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2624 LPHTTPHEADERW lphttpHdr = NULL;
2625 BOOL bSuccess = FALSE;
2626 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2627 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2628 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2629 INT index = -1;
2631 /* Find requested header structure */
2632 switch (level)
2634 case HTTP_QUERY_CUSTOM:
2635 if (!lpBuffer) return FALSE;
2636 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2637 break;
2639 case HTTP_QUERY_CONTENT_LENGTH:
2640 if(lpwhr->gzip_stream) {
2641 INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
2642 return FALSE;
2645 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2646 requested_index,request_only);
2647 break;
2649 case HTTP_QUERY_RAW_HEADERS_CRLF:
2651 LPWSTR headers;
2652 DWORD len = 0;
2653 BOOL ret = FALSE;
2655 if (request_only)
2656 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2657 else
2658 headers = lpwhr->lpszRawHeaders;
2660 if (headers)
2661 len = strlenW(headers) * sizeof(WCHAR);
2663 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2665 len += sizeof(WCHAR);
2666 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2667 ret = FALSE;
2669 else if (lpBuffer)
2671 if (headers)
2672 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2673 else
2675 len = strlenW(szCrLf) * sizeof(WCHAR);
2676 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2678 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2679 ret = TRUE;
2681 *lpdwBufferLength = len;
2683 if (request_only)
2684 HeapFree(GetProcessHeap(), 0, headers);
2685 return ret;
2687 case HTTP_QUERY_RAW_HEADERS:
2689 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2690 DWORD i, size = 0;
2691 LPWSTR pszString = lpBuffer;
2693 for (i = 0; ppszRawHeaderLines[i]; i++)
2694 size += strlenW(ppszRawHeaderLines[i]) + 1;
2696 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2698 HTTP_FreeTokens(ppszRawHeaderLines);
2699 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2700 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2701 return FALSE;
2703 if (pszString)
2705 for (i = 0; ppszRawHeaderLines[i]; i++)
2707 DWORD len = strlenW(ppszRawHeaderLines[i]);
2708 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2709 pszString += len+1;
2711 *pszString = '\0';
2712 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2714 *lpdwBufferLength = size * sizeof(WCHAR);
2715 HTTP_FreeTokens(ppszRawHeaderLines);
2717 return TRUE;
2719 case HTTP_QUERY_STATUS_TEXT:
2720 if (lpwhr->lpszStatusText)
2722 DWORD len = strlenW(lpwhr->lpszStatusText);
2723 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2725 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2726 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2727 return FALSE;
2729 if (lpBuffer)
2731 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2732 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2734 *lpdwBufferLength = len * sizeof(WCHAR);
2735 return TRUE;
2737 break;
2738 case HTTP_QUERY_VERSION:
2739 if (lpwhr->lpszVersion)
2741 DWORD len = strlenW(lpwhr->lpszVersion);
2742 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2744 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2745 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2746 return FALSE;
2748 if (lpBuffer)
2750 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2751 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2753 *lpdwBufferLength = len * sizeof(WCHAR);
2754 return TRUE;
2756 break;
2757 case HTTP_QUERY_CONTENT_ENCODING:
2758 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2759 requested_index,request_only);
2760 break;
2761 default:
2762 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2764 if (level < LAST_TABLE_HEADER && header_lookup[level])
2765 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2766 requested_index,request_only);
2769 if (index >= 0)
2770 lphttpHdr = &lpwhr->pCustHeaders[index];
2772 /* Ensure header satisfies requested attributes */
2773 if (!lphttpHdr ||
2774 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2775 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2777 INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
2778 return bSuccess;
2781 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2783 /* coalesce value to requested type */
2784 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2786 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2787 TRACE(" returning number: %d\n", *(int *)lpBuffer);
2788 bSuccess = TRUE;
2790 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2792 time_t tmpTime;
2793 struct tm tmpTM;
2794 SYSTEMTIME *STHook;
2796 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2798 tmpTM = *gmtime(&tmpTime);
2799 STHook = (SYSTEMTIME *)lpBuffer;
2800 STHook->wDay = tmpTM.tm_mday;
2801 STHook->wHour = tmpTM.tm_hour;
2802 STHook->wMilliseconds = 0;
2803 STHook->wMinute = tmpTM.tm_min;
2804 STHook->wDayOfWeek = tmpTM.tm_wday;
2805 STHook->wMonth = tmpTM.tm_mon + 1;
2806 STHook->wSecond = tmpTM.tm_sec;
2807 STHook->wYear = tmpTM.tm_year;
2808 bSuccess = TRUE;
2810 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
2811 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
2812 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
2814 else if (lphttpHdr->lpszValue)
2816 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
2818 if (len > *lpdwBufferLength)
2820 *lpdwBufferLength = len;
2821 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2822 return bSuccess;
2824 if (lpBuffer)
2826 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
2827 TRACE(" returning string: %s\n", debugstr_w(lpBuffer));
2829 *lpdwBufferLength = len - sizeof(WCHAR);
2830 bSuccess = TRUE;
2832 return bSuccess;
2835 /***********************************************************************
2836 * HttpQueryInfoW (WININET.@)
2838 * Queries for information about an HTTP request
2840 * RETURNS
2841 * TRUE on success
2842 * FALSE on failure
2845 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
2846 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2848 BOOL bSuccess = FALSE;
2849 http_request_t *lpwhr;
2851 if (TRACE_ON(wininet)) {
2852 #define FE(x) { x, #x }
2853 static const wininet_flag_info query_flags[] = {
2854 FE(HTTP_QUERY_MIME_VERSION),
2855 FE(HTTP_QUERY_CONTENT_TYPE),
2856 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
2857 FE(HTTP_QUERY_CONTENT_ID),
2858 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
2859 FE(HTTP_QUERY_CONTENT_LENGTH),
2860 FE(HTTP_QUERY_CONTENT_LANGUAGE),
2861 FE(HTTP_QUERY_ALLOW),
2862 FE(HTTP_QUERY_PUBLIC),
2863 FE(HTTP_QUERY_DATE),
2864 FE(HTTP_QUERY_EXPIRES),
2865 FE(HTTP_QUERY_LAST_MODIFIED),
2866 FE(HTTP_QUERY_MESSAGE_ID),
2867 FE(HTTP_QUERY_URI),
2868 FE(HTTP_QUERY_DERIVED_FROM),
2869 FE(HTTP_QUERY_COST),
2870 FE(HTTP_QUERY_LINK),
2871 FE(HTTP_QUERY_PRAGMA),
2872 FE(HTTP_QUERY_VERSION),
2873 FE(HTTP_QUERY_STATUS_CODE),
2874 FE(HTTP_QUERY_STATUS_TEXT),
2875 FE(HTTP_QUERY_RAW_HEADERS),
2876 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
2877 FE(HTTP_QUERY_CONNECTION),
2878 FE(HTTP_QUERY_ACCEPT),
2879 FE(HTTP_QUERY_ACCEPT_CHARSET),
2880 FE(HTTP_QUERY_ACCEPT_ENCODING),
2881 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
2882 FE(HTTP_QUERY_AUTHORIZATION),
2883 FE(HTTP_QUERY_CONTENT_ENCODING),
2884 FE(HTTP_QUERY_FORWARDED),
2885 FE(HTTP_QUERY_FROM),
2886 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
2887 FE(HTTP_QUERY_LOCATION),
2888 FE(HTTP_QUERY_ORIG_URI),
2889 FE(HTTP_QUERY_REFERER),
2890 FE(HTTP_QUERY_RETRY_AFTER),
2891 FE(HTTP_QUERY_SERVER),
2892 FE(HTTP_QUERY_TITLE),
2893 FE(HTTP_QUERY_USER_AGENT),
2894 FE(HTTP_QUERY_WWW_AUTHENTICATE),
2895 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
2896 FE(HTTP_QUERY_ACCEPT_RANGES),
2897 FE(HTTP_QUERY_SET_COOKIE),
2898 FE(HTTP_QUERY_COOKIE),
2899 FE(HTTP_QUERY_REQUEST_METHOD),
2900 FE(HTTP_QUERY_REFRESH),
2901 FE(HTTP_QUERY_CONTENT_DISPOSITION),
2902 FE(HTTP_QUERY_AGE),
2903 FE(HTTP_QUERY_CACHE_CONTROL),
2904 FE(HTTP_QUERY_CONTENT_BASE),
2905 FE(HTTP_QUERY_CONTENT_LOCATION),
2906 FE(HTTP_QUERY_CONTENT_MD5),
2907 FE(HTTP_QUERY_CONTENT_RANGE),
2908 FE(HTTP_QUERY_ETAG),
2909 FE(HTTP_QUERY_HOST),
2910 FE(HTTP_QUERY_IF_MATCH),
2911 FE(HTTP_QUERY_IF_NONE_MATCH),
2912 FE(HTTP_QUERY_IF_RANGE),
2913 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
2914 FE(HTTP_QUERY_MAX_FORWARDS),
2915 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
2916 FE(HTTP_QUERY_RANGE),
2917 FE(HTTP_QUERY_TRANSFER_ENCODING),
2918 FE(HTTP_QUERY_UPGRADE),
2919 FE(HTTP_QUERY_VARY),
2920 FE(HTTP_QUERY_VIA),
2921 FE(HTTP_QUERY_WARNING),
2922 FE(HTTP_QUERY_CUSTOM)
2924 static const wininet_flag_info modifier_flags[] = {
2925 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
2926 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
2927 FE(HTTP_QUERY_FLAG_NUMBER),
2928 FE(HTTP_QUERY_FLAG_COALESCE)
2930 #undef FE
2931 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
2932 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
2933 DWORD i;
2935 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, dwInfoLevel);
2936 TRACE(" Attribute:");
2937 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
2938 if (query_flags[i].val == info) {
2939 TRACE(" %s", query_flags[i].name);
2940 break;
2943 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
2944 TRACE(" Unknown (%08x)", info);
2947 TRACE(" Modifier:");
2948 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
2949 if (modifier_flags[i].val & info_mod) {
2950 TRACE(" %s", modifier_flags[i].name);
2951 info_mod &= ~ modifier_flags[i].val;
2955 if (info_mod) {
2956 TRACE(" Unknown (%08x)", info_mod);
2958 TRACE("\n");
2961 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
2962 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
2964 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2965 goto lend;
2968 if (lpBuffer == NULL)
2969 *lpdwBufferLength = 0;
2970 bSuccess = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
2971 lpBuffer, lpdwBufferLength, lpdwIndex);
2973 lend:
2974 if( lpwhr )
2975 WININET_Release( &lpwhr->hdr );
2977 TRACE("%d <--\n", bSuccess);
2978 return bSuccess;
2981 /***********************************************************************
2982 * HttpQueryInfoA (WININET.@)
2984 * Queries for information about an HTTP request
2986 * RETURNS
2987 * TRUE on success
2988 * FALSE on failure
2991 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
2992 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2994 BOOL result;
2995 DWORD len;
2996 WCHAR* bufferW;
2998 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
2999 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3001 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3002 lpdwBufferLength, lpdwIndex );
3005 if (lpBuffer)
3007 DWORD alloclen;
3008 len = (*lpdwBufferLength)*sizeof(WCHAR);
3009 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3011 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3012 if (alloclen < len)
3013 alloclen = len;
3015 else
3016 alloclen = len;
3017 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3018 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3019 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3020 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3021 } else
3023 bufferW = NULL;
3024 len = 0;
3027 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3028 &len, lpdwIndex );
3029 if( result )
3031 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3032 lpBuffer, *lpdwBufferLength, NULL, NULL );
3033 *lpdwBufferLength = len - 1;
3035 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3037 else
3038 /* since the strings being returned from HttpQueryInfoW should be
3039 * only ASCII characters, it is reasonable to assume that all of
3040 * the Unicode characters can be reduced to a single byte */
3041 *lpdwBufferLength = len / sizeof(WCHAR);
3043 HeapFree(GetProcessHeap(), 0, bufferW );
3045 return result;
3048 /***********************************************************************
3049 * HttpSendRequestExA (WININET.@)
3051 * Sends the specified request to the HTTP server and allows chunked
3052 * transfers.
3054 * RETURNS
3055 * Success: TRUE
3056 * Failure: FALSE, call GetLastError() for more information.
3058 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3059 LPINTERNET_BUFFERSA lpBuffersIn,
3060 LPINTERNET_BUFFERSA lpBuffersOut,
3061 DWORD dwFlags, DWORD_PTR dwContext)
3063 INTERNET_BUFFERSW BuffersInW;
3064 BOOL rc = FALSE;
3065 DWORD headerlen;
3066 LPWSTR header = NULL;
3068 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3069 lpBuffersOut, dwFlags, dwContext);
3071 if (lpBuffersIn)
3073 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3074 if (lpBuffersIn->lpcszHeader)
3076 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3077 lpBuffersIn->dwHeadersLength,0,0);
3078 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3079 if (!(BuffersInW.lpcszHeader = header))
3081 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3082 return FALSE;
3084 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3085 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3086 header, headerlen);
3088 else
3089 BuffersInW.lpcszHeader = NULL;
3090 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3091 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3092 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3093 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3094 BuffersInW.Next = NULL;
3097 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3099 HeapFree(GetProcessHeap(),0,header);
3101 return rc;
3104 /***********************************************************************
3105 * HttpSendRequestExW (WININET.@)
3107 * Sends the specified request to the HTTP server and allows chunked
3108 * transfers
3110 * RETURNS
3111 * Success: TRUE
3112 * Failure: FALSE, call GetLastError() for more information.
3114 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3115 LPINTERNET_BUFFERSW lpBuffersIn,
3116 LPINTERNET_BUFFERSW lpBuffersOut,
3117 DWORD dwFlags, DWORD_PTR dwContext)
3119 BOOL ret = FALSE;
3120 http_request_t *lpwhr;
3121 http_session_t *lpwhs;
3122 appinfo_t *hIC;
3124 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3125 lpBuffersOut, dwFlags, dwContext);
3127 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3129 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3131 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3132 goto lend;
3135 lpwhs = lpwhr->lpHttpSession;
3136 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3137 hIC = lpwhs->lpAppInfo;
3138 assert(hIC->hdr.htype == WH_HINIT);
3140 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3142 WORKREQUEST workRequest;
3143 struct WORKREQ_HTTPSENDREQUESTW *req;
3145 workRequest.asyncproc = AsyncHttpSendRequestProc;
3146 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3147 req = &workRequest.u.HttpSendRequestW;
3148 if (lpBuffersIn)
3150 /* FIXME: this should use dwHeadersLength or may not be necessary at all */
3151 req->lpszHeader = heap_strdupW(lpBuffersIn->lpcszHeader);
3152 req->dwHeaderLength = lpBuffersIn->dwHeadersLength;
3153 req->lpOptional = lpBuffersIn->lpvBuffer;
3154 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3155 req->dwContentLength = lpBuffersIn->dwBufferTotal;
3157 else
3159 req->lpszHeader = NULL;
3160 req->dwHeaderLength = 0;
3161 req->lpOptional = NULL;
3162 req->dwOptionalLength = 0;
3163 req->dwContentLength = 0;
3166 req->bEndRequest = FALSE;
3168 INTERNET_AsyncCall(&workRequest);
3170 * This is from windows.
3172 INTERNET_SetLastError(ERROR_IO_PENDING);
3174 else
3176 if (lpBuffersIn)
3177 ret = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3178 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
3179 lpBuffersIn->dwBufferTotal, FALSE);
3180 else
3181 ret = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
3184 lend:
3185 if ( lpwhr )
3186 WININET_Release( &lpwhr->hdr );
3188 TRACE("<---\n");
3189 return ret;
3192 /***********************************************************************
3193 * HttpSendRequestW (WININET.@)
3195 * Sends the specified request to the HTTP server
3197 * RETURNS
3198 * TRUE on success
3199 * FALSE on failure
3202 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
3203 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3205 http_request_t *lpwhr;
3206 http_session_t *lpwhs = NULL;
3207 appinfo_t *hIC = NULL;
3208 BOOL r;
3210 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
3211 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
3213 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3214 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3216 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3217 r = FALSE;
3218 goto lend;
3221 lpwhs = lpwhr->lpHttpSession;
3222 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
3224 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3225 r = FALSE;
3226 goto lend;
3229 hIC = lpwhs->lpAppInfo;
3230 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
3232 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3233 r = FALSE;
3234 goto lend;
3237 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3239 WORKREQUEST workRequest;
3240 struct WORKREQ_HTTPSENDREQUESTW *req;
3242 workRequest.asyncproc = AsyncHttpSendRequestProc;
3243 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3244 req = &workRequest.u.HttpSendRequestW;
3245 if (lpszHeaders)
3247 DWORD size;
3249 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
3250 else size = dwHeaderLength * sizeof(WCHAR);
3252 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
3253 memcpy(req->lpszHeader, lpszHeaders, size);
3255 else
3256 req->lpszHeader = 0;
3257 req->dwHeaderLength = dwHeaderLength;
3258 req->lpOptional = lpOptional;
3259 req->dwOptionalLength = dwOptionalLength;
3260 req->dwContentLength = dwOptionalLength;
3261 req->bEndRequest = TRUE;
3263 INTERNET_AsyncCall(&workRequest);
3265 * This is from windows.
3267 INTERNET_SetLastError(ERROR_IO_PENDING);
3268 r = FALSE;
3270 else
3272 r = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
3273 dwHeaderLength, lpOptional, dwOptionalLength,
3274 dwOptionalLength, TRUE);
3276 lend:
3277 if( lpwhr )
3278 WININET_Release( &lpwhr->hdr );
3279 return r;
3282 /***********************************************************************
3283 * HttpSendRequestA (WININET.@)
3285 * Sends the specified request to the HTTP server
3287 * RETURNS
3288 * TRUE on success
3289 * FALSE on failure
3292 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
3293 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3295 BOOL result;
3296 LPWSTR szHeaders=NULL;
3297 DWORD nLen=dwHeaderLength;
3298 if(lpszHeaders!=NULL)
3300 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
3301 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
3302 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
3304 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
3305 HeapFree(GetProcessHeap(),0,szHeaders);
3306 return result;
3309 /***********************************************************************
3310 * HTTP_GetRedirectURL (internal)
3312 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3314 static WCHAR szHttp[] = {'h','t','t','p',0};
3315 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3316 http_session_t *lpwhs = lpwhr->lpHttpSession;
3317 URL_COMPONENTSW urlComponents;
3318 DWORD url_length = 0;
3319 LPWSTR orig_url;
3320 LPWSTR combined_url;
3322 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3323 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3324 urlComponents.dwSchemeLength = 0;
3325 urlComponents.lpszHostName = lpwhs->lpszHostName;
3326 urlComponents.dwHostNameLength = 0;
3327 urlComponents.nPort = lpwhs->nHostPort;
3328 urlComponents.lpszUserName = lpwhs->lpszUserName;
3329 urlComponents.dwUserNameLength = 0;
3330 urlComponents.lpszPassword = NULL;
3331 urlComponents.dwPasswordLength = 0;
3332 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3333 urlComponents.dwUrlPathLength = 0;
3334 urlComponents.lpszExtraInfo = NULL;
3335 urlComponents.dwExtraInfoLength = 0;
3337 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3338 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3339 return NULL;
3341 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3343 /* convert from bytes to characters */
3344 url_length = url_length / sizeof(WCHAR) - 1;
3345 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3347 HeapFree(GetProcessHeap(), 0, orig_url);
3348 return NULL;
3351 url_length = 0;
3352 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3353 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3355 HeapFree(GetProcessHeap(), 0, orig_url);
3356 return NULL;
3358 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3360 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3362 HeapFree(GetProcessHeap(), 0, orig_url);
3363 HeapFree(GetProcessHeap(), 0, combined_url);
3364 return NULL;
3366 HeapFree(GetProcessHeap(), 0, orig_url);
3367 return combined_url;
3371 /***********************************************************************
3372 * HTTP_HandleRedirect (internal)
3374 static BOOL HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3376 http_session_t *lpwhs = lpwhr->lpHttpSession;
3377 appinfo_t *hIC = lpwhs->lpAppInfo;
3378 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3379 WCHAR path[INTERNET_MAX_URL_LENGTH];
3380 int index;
3382 if(lpszUrl[0]=='/')
3384 /* if it's an absolute path, keep the same session info */
3385 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3387 else
3389 URL_COMPONENTSW urlComponents;
3390 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3391 static WCHAR szHttp[] = {'h','t','t','p',0};
3392 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3394 userName[0] = 0;
3395 hostName[0] = 0;
3396 protocol[0] = 0;
3398 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3399 urlComponents.lpszScheme = protocol;
3400 urlComponents.dwSchemeLength = 32;
3401 urlComponents.lpszHostName = hostName;
3402 urlComponents.dwHostNameLength = MAXHOSTNAME;
3403 urlComponents.lpszUserName = userName;
3404 urlComponents.dwUserNameLength = 1024;
3405 urlComponents.lpszPassword = NULL;
3406 urlComponents.dwPasswordLength = 0;
3407 urlComponents.lpszUrlPath = path;
3408 urlComponents.dwUrlPathLength = 2048;
3409 urlComponents.lpszExtraInfo = NULL;
3410 urlComponents.dwExtraInfoLength = 0;
3411 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3412 return FALSE;
3414 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3415 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3417 TRACE("redirect from secure page to non-secure page\n");
3418 /* FIXME: warn about from secure redirect to non-secure page */
3419 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3421 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3422 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3424 TRACE("redirect from non-secure page to secure page\n");
3425 /* FIXME: notify about redirect to secure page */
3426 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3429 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3431 if (lstrlenW(protocol)>4) /*https*/
3432 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3433 else /*http*/
3434 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3437 #if 0
3439 * This upsets redirects to binary files on sourceforge.net
3440 * and gives an html page instead of the target file
3441 * Examination of the HTTP request sent by native wininet.dll
3442 * reveals that it doesn't send a referrer in that case.
3443 * Maybe there's a flag that enables this, or maybe a referrer
3444 * shouldn't be added in case of a redirect.
3447 /* consider the current host as the referrer */
3448 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3449 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3450 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3451 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3452 #endif
3454 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3455 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3456 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3458 int len;
3459 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3460 len = lstrlenW(hostName);
3461 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3462 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3463 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3465 else
3466 lpwhs->lpszHostName = heap_strdupW(hostName);
3468 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3470 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3471 lpwhs->lpszUserName = NULL;
3472 if (userName[0])
3473 lpwhs->lpszUserName = heap_strdupW(userName);
3475 if (!using_proxy)
3477 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3479 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3480 lpwhs->lpszServerName = heap_strdupW(hostName);
3481 lpwhs->nServerPort = urlComponents.nPort;
3483 NETCON_close(&lpwhr->netConnection);
3484 if (!HTTP_ResolveName(lpwhr)) return FALSE;
3485 if (!NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)) return FALSE;
3486 lpwhr->read_pos = lpwhr->read_size = 0;
3487 lpwhr->read_chunked = FALSE;
3490 else
3491 TRACE("Redirect through proxy\n");
3494 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3495 lpwhr->lpszPath=NULL;
3496 if (*path)
3498 DWORD needed = 0;
3499 HRESULT rc;
3501 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3502 if (rc != E_POINTER)
3503 needed = strlenW(path)+1;
3504 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3505 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3506 URL_ESCAPE_SPACES_ONLY);
3507 if (rc != S_OK)
3509 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3510 strcpyW(lpwhr->lpszPath,path);
3514 /* Remove custom content-type/length headers on redirects. */
3515 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3516 if (0 <= index)
3517 HTTP_DeleteCustomHeader(lpwhr, index);
3518 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3519 if (0 <= index)
3520 HTTP_DeleteCustomHeader(lpwhr, index);
3522 return TRUE;
3525 /***********************************************************************
3526 * HTTP_build_req (internal)
3528 * concatenate all the strings in the request together
3530 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3532 LPCWSTR *t;
3533 LPWSTR str;
3535 for( t = list; *t ; t++ )
3536 len += strlenW( *t );
3537 len++;
3539 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3540 *str = 0;
3542 for( t = list; *t ; t++ )
3543 strcatW( str, *t );
3545 return str;
3548 static BOOL HTTP_SecureProxyConnect(http_request_t *lpwhr)
3550 LPWSTR lpszPath;
3551 LPWSTR requestString;
3552 INT len;
3553 INT cnt;
3554 INT responseLen;
3555 char *ascii_req;
3556 BOOL ret;
3557 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3558 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3559 http_session_t *lpwhs = lpwhr->lpHttpSession;
3561 TRACE("\n");
3563 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3564 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3565 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3566 HeapFree( GetProcessHeap(), 0, lpszPath );
3568 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3569 NULL, 0, NULL, NULL );
3570 len--; /* the nul terminator isn't needed */
3571 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3572 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3573 ascii_req, len, NULL, NULL );
3574 HeapFree( GetProcessHeap(), 0, requestString );
3576 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3578 ret = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3579 HeapFree( GetProcessHeap(), 0, ascii_req );
3580 if (!ret || cnt < 0)
3581 return FALSE;
3583 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3584 if (!responseLen)
3585 return FALSE;
3587 return TRUE;
3590 static void HTTP_InsertCookies(http_request_t *lpwhr)
3592 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3593 LPWSTR lpszCookies, lpszUrl = NULL;
3594 DWORD nCookieSize, size;
3595 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3597 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3598 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3599 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3601 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3603 int cnt = 0;
3604 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3606 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3607 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3609 cnt += sprintfW(lpszCookies, szCookie);
3610 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3611 strcatW(lpszCookies, szCrLf);
3613 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3614 HeapFree(GetProcessHeap(), 0, lpszCookies);
3617 HeapFree(GetProcessHeap(), 0, lpszUrl);
3620 /***********************************************************************
3621 * HTTP_HttpSendRequestW (internal)
3623 * Sends the specified request to the HTTP server
3625 * RETURNS
3626 * TRUE on success
3627 * FALSE on failure
3630 BOOL WINAPI HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3631 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3632 DWORD dwContentLength, BOOL bEndRequest)
3634 INT cnt;
3635 BOOL bSuccess = FALSE, redirected = FALSE;
3636 LPWSTR requestString = NULL;
3637 INT responseLen;
3638 BOOL loop_next;
3639 INTERNET_ASYNC_RESULT iar;
3640 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3641 static const WCHAR szContentLength[] =
3642 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3643 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3645 TRACE("--> %p\n", lpwhr);
3647 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3649 /* if the verb is NULL default to GET */
3650 if (!lpwhr->lpszVerb)
3651 lpwhr->lpszVerb = heap_strdupW(szGET);
3653 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3655 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3656 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3657 lpwhr->dwBytesToWrite = dwContentLength;
3659 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3661 WCHAR *agent_header;
3662 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3663 int len;
3665 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3666 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3667 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3669 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3670 HeapFree(GetProcessHeap(), 0, agent_header);
3672 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3674 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3675 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3677 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3679 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3680 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3681 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3686 DWORD len;
3687 char *ascii_req;
3689 loop_next = FALSE;
3691 /* like native, just in case the caller forgot to call InternetReadFile
3692 * for all the data */
3693 HTTP_DrainContent(lpwhr);
3694 lpwhr->dwContentRead = 0;
3696 if (TRACE_ON(wininet))
3698 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3699 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3702 HTTP_FixURL(lpwhr);
3703 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3705 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3707 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3708 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3710 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3711 HTTP_InsertCookies(lpwhr);
3713 /* add the headers the caller supplied */
3714 if( lpszHeaders && dwHeaderLength )
3716 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3717 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3720 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3722 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3723 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3724 HeapFree(GetProcessHeap(), 0, url);
3726 else
3727 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3730 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3732 /* Send the request and store the results */
3733 if (!HTTP_OpenConnection(lpwhr))
3734 goto lend;
3736 /* send the request as ASCII, tack on the optional data */
3737 if (!lpOptional || redirected)
3738 dwOptionalLength = 0;
3739 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3740 NULL, 0, NULL, NULL );
3741 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3742 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3743 ascii_req, len, NULL, NULL );
3744 if( lpOptional )
3745 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3746 len = (len + dwOptionalLength - 1);
3747 ascii_req[len] = 0;
3748 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3750 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3751 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3753 NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3754 HeapFree( GetProcessHeap(), 0, ascii_req );
3756 lpwhr->dwBytesWritten = dwOptionalLength;
3758 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3759 INTERNET_STATUS_REQUEST_SENT,
3760 &len, sizeof(DWORD));
3762 if (bEndRequest)
3764 DWORD dwBufferSize;
3765 DWORD dwStatusCode;
3767 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3768 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3770 if (cnt < 0)
3771 goto lend;
3773 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3774 if (responseLen)
3775 bSuccess = TRUE;
3777 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3778 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3779 sizeof(DWORD));
3781 HTTP_ProcessCookies(lpwhr);
3783 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3785 dwBufferSize = sizeof(dwStatusCode);
3786 if (!HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3787 &dwStatusCode,&dwBufferSize,NULL))
3788 dwStatusCode = 0;
3790 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && bSuccess)
3792 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3793 dwBufferSize=sizeof(szNewLocation);
3794 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
3795 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL))
3797 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3799 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3800 lpwhr->lpszVerb = heap_strdupW(szGET);
3802 HTTP_DrainContent(lpwhr);
3803 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3805 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3806 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3807 bSuccess = HTTP_HandleRedirect(lpwhr, new_url);
3808 if (bSuccess)
3810 HeapFree(GetProcessHeap(), 0, requestString);
3811 loop_next = TRUE;
3813 HeapFree( GetProcessHeap(), 0, new_url );
3815 redirected = TRUE;
3818 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && bSuccess)
3820 WCHAR szAuthValue[2048];
3821 dwBufferSize=2048;
3822 if (dwStatusCode == HTTP_STATUS_DENIED)
3824 DWORD dwIndex = 0;
3825 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
3827 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3828 &lpwhr->pAuthInfo,
3829 lpwhr->lpHttpSession->lpszUserName,
3830 lpwhr->lpHttpSession->lpszPassword))
3832 loop_next = TRUE;
3833 break;
3837 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3839 DWORD dwIndex = 0;
3840 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
3842 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3843 &lpwhr->pProxyAuthInfo,
3844 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3845 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword))
3847 loop_next = TRUE;
3848 break;
3854 else
3855 bSuccess = TRUE;
3857 while (loop_next);
3859 if(bSuccess) {
3860 WCHAR url[INTERNET_MAX_URL_LENGTH];
3861 WCHAR cacheFileName[MAX_PATH+1];
3862 BOOL b;
3864 b = HTTP_GetRequestURL(lpwhr, url);
3865 if(!b) {
3866 WARN("Could not get URL\n");
3867 goto lend;
3870 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3871 if(b) {
3872 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3873 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3874 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3875 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3876 WARN("Could not create file: %u\n", GetLastError());
3877 lpwhr->hCacheFile = NULL;
3879 }else {
3880 WARN("Could not create cache entry: %08x\n", GetLastError());
3884 lend:
3886 HeapFree(GetProcessHeap(), 0, requestString);
3888 /* TODO: send notification for P3P header */
3890 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3892 if (bSuccess)
3894 if (lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite) HTTP_ReceiveRequestData(lpwhr, TRUE);
3895 else
3897 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3898 iar.dwError = 0;
3900 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3901 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3902 sizeof(INTERNET_ASYNC_RESULT));
3905 else
3907 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3908 iar.dwError = INTERNET_GetLastError();
3910 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3911 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3912 sizeof(INTERNET_ASYNC_RESULT));
3916 TRACE("<--\n");
3917 if (bSuccess) INTERNET_SetLastError(ERROR_SUCCESS);
3918 return bSuccess;
3921 /***********************************************************************
3922 * HTTPSESSION_Destroy (internal)
3924 * Deallocate session handle
3927 static void HTTPSESSION_Destroy(object_header_t *hdr)
3929 http_session_t *lpwhs = (http_session_t*) hdr;
3931 TRACE("%p\n", lpwhs);
3933 WININET_Release(&lpwhs->lpAppInfo->hdr);
3935 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3936 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3937 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
3938 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3939 HeapFree(GetProcessHeap(), 0, lpwhs);
3942 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3944 switch(option) {
3945 case INTERNET_OPTION_HANDLE_TYPE:
3946 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3948 if (*size < sizeof(ULONG))
3949 return ERROR_INSUFFICIENT_BUFFER;
3951 *size = sizeof(DWORD);
3952 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
3953 return ERROR_SUCCESS;
3956 return INET_QueryOption(option, buffer, size, unicode);
3959 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
3961 http_session_t *ses = (http_session_t*)hdr;
3963 switch(option) {
3964 case INTERNET_OPTION_USERNAME:
3966 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
3967 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
3968 return ERROR_SUCCESS;
3970 case INTERNET_OPTION_PASSWORD:
3972 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
3973 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
3974 return ERROR_SUCCESS;
3976 default: break;
3979 return ERROR_INTERNET_INVALID_OPTION;
3982 static const object_vtbl_t HTTPSESSIONVtbl = {
3983 HTTPSESSION_Destroy,
3984 NULL,
3985 HTTPSESSION_QueryOption,
3986 HTTPSESSION_SetOption,
3987 NULL,
3988 NULL,
3989 NULL,
3990 NULL,
3991 NULL
3995 /***********************************************************************
3996 * HTTP_Connect (internal)
3998 * Create http session handle
4000 * RETURNS
4001 * HINTERNET a session handle on success
4002 * NULL on failure
4005 HINTERNET HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4006 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4007 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4008 DWORD dwInternalFlags)
4010 http_session_t *lpwhs = NULL;
4011 HINTERNET handle = NULL;
4013 TRACE("-->\n");
4015 if (!lpszServerName || !lpszServerName[0])
4017 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4018 goto lerror;
4021 assert( hIC->hdr.htype == WH_HINIT );
4023 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4024 if (NULL == lpwhs)
4026 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4027 goto lerror;
4031 * According to my tests. The name is not resolved until a request is sent
4034 lpwhs->hdr.htype = WH_HHTTPSESSION;
4035 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4036 lpwhs->hdr.dwFlags = dwFlags;
4037 lpwhs->hdr.dwContext = dwContext;
4038 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4039 lpwhs->hdr.refs = 1;
4040 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4042 WININET_AddRef( &hIC->hdr );
4043 lpwhs->lpAppInfo = hIC;
4044 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4046 handle = WININET_AllocHandle( &lpwhs->hdr );
4047 if (NULL == handle)
4049 ERR("Failed to alloc handle\n");
4050 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4051 goto lerror;
4054 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4055 if(strchrW(hIC->lpszProxy, ' '))
4056 FIXME("Several proxies not implemented.\n");
4057 if(hIC->lpszProxyBypass)
4058 FIXME("Proxy bypass is ignored.\n");
4060 if (lpszServerName && lpszServerName[0])
4062 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4063 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4065 if (lpszUserName && lpszUserName[0])
4066 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4067 if (lpszPassword && lpszPassword[0])
4068 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4069 lpwhs->nServerPort = nServerPort;
4070 lpwhs->nHostPort = nServerPort;
4072 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4073 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4075 INTERNET_SendCallback(&hIC->hdr, dwContext,
4076 INTERNET_STATUS_HANDLE_CREATED, &handle,
4077 sizeof(handle));
4080 lerror:
4081 if( lpwhs )
4082 WININET_Release( &lpwhs->hdr );
4085 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4086 * windows
4089 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4090 return handle;
4094 /***********************************************************************
4095 * HTTP_OpenConnection (internal)
4097 * Connect to a web server
4099 * RETURNS
4101 * TRUE on success
4102 * FALSE on failure
4104 static BOOL HTTP_OpenConnection(http_request_t *lpwhr)
4106 BOOL bSuccess = FALSE;
4107 http_session_t *lpwhs;
4108 appinfo_t *hIC = NULL;
4109 char szaddr[INET6_ADDRSTRLEN];
4110 const void *addr;
4112 TRACE("-->\n");
4115 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4117 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4118 goto lend;
4121 if (NETCON_connected(&lpwhr->netConnection))
4123 bSuccess = TRUE;
4124 goto lend;
4126 if (!HTTP_ResolveName(lpwhr)) goto lend;
4128 lpwhs = lpwhr->lpHttpSession;
4130 hIC = lpwhs->lpAppInfo;
4131 switch (lpwhs->socketAddress.ss_family)
4133 case AF_INET:
4134 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4135 break;
4136 case AF_INET6:
4137 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4138 break;
4139 default:
4140 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4141 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
4142 return FALSE;
4144 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4145 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4146 INTERNET_STATUS_CONNECTING_TO_SERVER,
4147 szaddr,
4148 strlen(szaddr)+1);
4150 if (!NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family,
4151 SOCK_STREAM, 0))
4153 WARN("Socket creation failed: %u\n", INTERNET_GetLastError());
4154 goto lend;
4157 if (!NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4158 lpwhs->sa_len))
4159 goto lend;
4161 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4163 /* Note: we differ from Microsoft's WinINet here. they seem to have
4164 * a bug that causes no status callbacks to be sent when starting
4165 * a tunnel to a proxy server using the CONNECT verb. i believe our
4166 * behaviour to be more correct and to not cause any incompatibilities
4167 * because using a secure connection through a proxy server is a rare
4168 * case that would be hard for anyone to depend on */
4169 if (hIC->lpszProxy && !HTTP_SecureProxyConnect(lpwhr))
4170 goto lend;
4172 if (!NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName))
4174 WARN("Couldn't connect securely to host\n");
4175 goto lend;
4179 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4180 INTERNET_STATUS_CONNECTED_TO_SERVER,
4181 szaddr, strlen(szaddr)+1);
4183 bSuccess = TRUE;
4185 lend:
4186 lpwhr->read_pos = lpwhr->read_size = 0;
4187 lpwhr->read_chunked = FALSE;
4189 TRACE("%d <--\n", bSuccess);
4190 return bSuccess;
4194 /***********************************************************************
4195 * HTTP_clear_response_headers (internal)
4197 * clear out any old response headers
4199 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4201 DWORD i;
4203 for( i=0; i<lpwhr->nCustHeaders; i++)
4205 if( !lpwhr->pCustHeaders[i].lpszField )
4206 continue;
4207 if( !lpwhr->pCustHeaders[i].lpszValue )
4208 continue;
4209 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4210 continue;
4211 HTTP_DeleteCustomHeader( lpwhr, i );
4212 i--;
4216 /***********************************************************************
4217 * HTTP_GetResponseHeaders (internal)
4219 * Read server response
4221 * RETURNS
4223 * TRUE on success
4224 * FALSE on error
4226 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4228 INT cbreaks = 0;
4229 WCHAR buffer[MAX_REPLY_LEN];
4230 DWORD buflen = MAX_REPLY_LEN;
4231 BOOL bSuccess = FALSE;
4232 INT rc = 0;
4233 char bufferA[MAX_REPLY_LEN];
4234 LPWSTR status_code = NULL, status_text = NULL;
4235 DWORD cchMaxRawHeaders = 1024;
4236 LPWSTR lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4237 LPWSTR temp;
4238 DWORD cchRawHeaders = 0;
4239 BOOL codeHundred = FALSE;
4241 TRACE("-->\n");
4243 /* clear old response headers (eg. from a redirect response) */
4244 if (clear) HTTP_clear_response_headers( lpwhr );
4246 if (!NETCON_connected(&lpwhr->netConnection))
4247 goto lend;
4249 do {
4250 static const WCHAR szHundred[] = {'1','0','0',0};
4252 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4254 buflen = MAX_REPLY_LEN;
4255 if (!read_line(lpwhr, bufferA, &buflen))
4256 goto lend;
4257 rc += buflen;
4258 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4259 /* check is this a status code line? */
4260 if (!strncmpW(buffer, g_szHttp1_0, 4))
4262 /* split the version from the status code */
4263 status_code = strchrW( buffer, ' ' );
4264 if( !status_code )
4265 goto lend;
4266 *status_code++=0;
4268 /* split the status code from the status text */
4269 status_text = strchrW( status_code, ' ' );
4270 if( !status_text )
4271 goto lend;
4272 *status_text++=0;
4274 TRACE("version [%s] status code [%s] status text [%s]\n",
4275 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4277 codeHundred = (!strcmpW(status_code, szHundred));
4279 else if (!codeHundred)
4281 FIXME("Non status line at head of response (%s)\n",debugstr_w(buffer));
4282 continue;
4284 } while (codeHundred);
4286 /* Add status code */
4287 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4288 HTTP_ADDHDR_FLAG_REPLACE);
4290 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4291 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4293 lpwhr->lpszVersion = heap_strdupW(buffer);
4294 lpwhr->lpszStatusText = heap_strdupW(status_text);
4296 /* Restore the spaces */
4297 *(status_code-1) = ' ';
4298 *(status_text-1) = ' ';
4300 /* regenerate raw headers */
4301 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4302 cchMaxRawHeaders *= 2;
4303 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4304 if (temp == NULL) goto lend;
4305 lpszRawHeaders = temp;
4306 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4307 cchRawHeaders += (buflen-1);
4308 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4309 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4310 lpszRawHeaders[cchRawHeaders] = '\0';
4312 /* Parse each response line */
4315 buflen = MAX_REPLY_LEN;
4316 if (read_line(lpwhr, bufferA, &buflen))
4318 LPWSTR * pFieldAndValue;
4320 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4322 if (!bufferA[0]) break;
4323 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4325 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4326 if (pFieldAndValue)
4328 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4329 cchMaxRawHeaders *= 2;
4330 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4331 if (temp == NULL) goto lend;
4332 lpszRawHeaders = temp;
4333 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4334 cchRawHeaders += (buflen-1);
4335 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4336 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4337 lpszRawHeaders[cchRawHeaders] = '\0';
4339 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4340 HTTP_ADDREQ_FLAG_ADD );
4342 HTTP_FreeTokens(pFieldAndValue);
4345 else
4347 cbreaks++;
4348 if (cbreaks >= 2)
4349 break;
4351 }while(1);
4353 /* make sure the response header is terminated with an empty line. Some apps really
4354 truly care about that empty line being there for some reason. Just add it to the
4355 header. */
4356 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4358 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4359 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4360 if (temp == NULL) goto lend;
4361 lpszRawHeaders = temp;
4364 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4366 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4367 lpwhr->lpszRawHeaders = lpszRawHeaders;
4368 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4369 bSuccess = TRUE;
4371 lend:
4373 TRACE("<--\n");
4374 if (bSuccess)
4375 return rc;
4376 else
4378 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4379 return 0;
4384 static void strip_spaces(LPWSTR start)
4386 LPWSTR str = start;
4387 LPWSTR end;
4389 while (*str == ' ' && *str != '\0')
4390 str++;
4392 if (str != start)
4393 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
4395 end = start + strlenW(start) - 1;
4396 while (end >= start && *end == ' ')
4398 *end = '\0';
4399 end--;
4404 /***********************************************************************
4405 * HTTP_InterpretHttpHeader (internal)
4407 * Parse server response
4409 * RETURNS
4411 * Pointer to array of field, value, NULL on success.
4412 * NULL on error.
4414 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4416 LPWSTR * pTokenPair;
4417 LPWSTR pszColon;
4418 INT len;
4420 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4422 pszColon = strchrW(buffer, ':');
4423 /* must have two tokens */
4424 if (!pszColon)
4426 HTTP_FreeTokens(pTokenPair);
4427 if (buffer[0])
4428 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4429 return NULL;
4432 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4433 if (!pTokenPair[0])
4435 HTTP_FreeTokens(pTokenPair);
4436 return NULL;
4438 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4439 pTokenPair[0][pszColon - buffer] = '\0';
4441 /* skip colon */
4442 pszColon++;
4443 len = strlenW(pszColon);
4444 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4445 if (!pTokenPair[1])
4447 HTTP_FreeTokens(pTokenPair);
4448 return NULL;
4450 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4452 strip_spaces(pTokenPair[0]);
4453 strip_spaces(pTokenPair[1]);
4455 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4456 return pTokenPair;
4459 /***********************************************************************
4460 * HTTP_ProcessHeader (internal)
4462 * Stuff header into header tables according to <dwModifier>
4466 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4468 static BOOL HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4470 LPHTTPHEADERW lphttpHdr = NULL;
4471 BOOL bSuccess = FALSE;
4472 INT index = -1;
4473 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4475 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4477 /* REPLACE wins out over ADD */
4478 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4479 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4481 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4482 index = -1;
4483 else
4484 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4486 if (index >= 0)
4488 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4490 return FALSE;
4492 lphttpHdr = &lpwhr->pCustHeaders[index];
4494 else if (value)
4496 HTTPHEADERW hdr;
4498 hdr.lpszField = (LPWSTR)field;
4499 hdr.lpszValue = (LPWSTR)value;
4500 hdr.wFlags = hdr.wCount = 0;
4502 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4503 hdr.wFlags |= HDR_ISREQUEST;
4505 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4507 /* no value to delete */
4508 else return TRUE;
4510 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4511 lphttpHdr->wFlags |= HDR_ISREQUEST;
4512 else
4513 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4515 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4517 HTTP_DeleteCustomHeader( lpwhr, index );
4519 if (value)
4521 HTTPHEADERW hdr;
4523 hdr.lpszField = (LPWSTR)field;
4524 hdr.lpszValue = (LPWSTR)value;
4525 hdr.wFlags = hdr.wCount = 0;
4527 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4528 hdr.wFlags |= HDR_ISREQUEST;
4530 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4533 return TRUE;
4535 else if (dwModifier & COALESCEFLAGS)
4537 LPWSTR lpsztmp;
4538 WCHAR ch = 0;
4539 INT len = 0;
4540 INT origlen = strlenW(lphttpHdr->lpszValue);
4541 INT valuelen = strlenW(value);
4543 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4545 ch = ',';
4546 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4548 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4550 ch = ';';
4551 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4554 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4556 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4557 if (lpsztmp)
4559 lphttpHdr->lpszValue = lpsztmp;
4560 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4561 if (ch > 0)
4563 lphttpHdr->lpszValue[origlen] = ch;
4564 origlen++;
4565 lphttpHdr->lpszValue[origlen] = ' ';
4566 origlen++;
4569 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4570 lphttpHdr->lpszValue[len] = '\0';
4571 bSuccess = TRUE;
4573 else
4575 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4576 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4579 TRACE("<-- %d\n",bSuccess);
4580 return bSuccess;
4584 /***********************************************************************
4585 * HTTP_FinishedReading (internal)
4587 * Called when all content from server has been read by client.
4590 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4592 WCHAR szVersion[10];
4593 WCHAR szConnectionResponse[20];
4594 DWORD dwBufferSize = sizeof(szVersion);
4595 BOOL keepalive = FALSE;
4597 TRACE("\n");
4599 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
4600 * the connection is keep-alive by default */
4601 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion,
4602 &dwBufferSize, NULL) &&
4603 !strcmpiW(szVersion, g_szHttp1_1))
4605 keepalive = TRUE;
4608 dwBufferSize = sizeof(szConnectionResponse);
4609 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) ||
4610 HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL))
4612 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
4615 if (!keepalive)
4617 HTTPREQ_CloseConnection(&lpwhr->hdr);
4620 /* FIXME: store data in the URL cache here */
4622 return TRUE;
4626 /***********************************************************************
4627 * HTTP_GetCustomHeaderIndex (internal)
4629 * Return index of custom header from header array
4632 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
4633 int requested_index, BOOL request_only)
4635 DWORD index;
4637 TRACE("%s\n", debugstr_w(lpszField));
4639 for (index = 0; index < lpwhr->nCustHeaders; index++)
4641 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
4642 continue;
4644 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4645 continue;
4647 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4648 continue;
4650 if (requested_index == 0)
4651 break;
4652 requested_index --;
4655 if (index >= lpwhr->nCustHeaders)
4656 index = -1;
4658 TRACE("Return: %d\n", index);
4659 return index;
4663 /***********************************************************************
4664 * HTTP_InsertCustomHeader (internal)
4666 * Insert header into array
4669 static BOOL HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
4671 INT count;
4672 LPHTTPHEADERW lph = NULL;
4673 BOOL r = FALSE;
4675 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
4676 count = lpwhr->nCustHeaders + 1;
4677 if (count > 1)
4678 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
4679 else
4680 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
4682 if (NULL != lph)
4684 lpwhr->pCustHeaders = lph;
4685 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
4686 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
4687 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
4688 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
4689 lpwhr->nCustHeaders++;
4690 r = TRUE;
4692 else
4694 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4697 return r;
4701 /***********************************************************************
4702 * HTTP_DeleteCustomHeader (internal)
4704 * Delete header from array
4705 * If this function is called, the indexs may change.
4707 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
4709 if( lpwhr->nCustHeaders <= 0 )
4710 return FALSE;
4711 if( index >= lpwhr->nCustHeaders )
4712 return FALSE;
4713 lpwhr->nCustHeaders--;
4715 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
4716 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
4718 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
4719 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
4720 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
4722 return TRUE;
4726 /***********************************************************************
4727 * HTTP_VerifyValidHeader (internal)
4729 * Verify the given header is not invalid for the given http request
4732 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
4734 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4735 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
4736 return FALSE;
4738 return TRUE;
4741 /***********************************************************************
4742 * IsHostInProxyBypassList (@)
4744 * Undocumented
4747 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
4749 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
4750 return FALSE;