push 5aff8350ceade24f8243f07a9cf7ecb816236fb1
[wine/hacks.git] / dlls / wininet / http.c
blob117d30272f19b5c89e63b0958d23348d10a86b15
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 index, 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;
244 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
245 if(index != -1)
246 HTTP_DeleteCustomHeader(req, index);
249 #else
251 static void init_gzip_stream(http_request_t *req)
253 ERR("gzip stream not supported, missing zlib.\n");
256 #endif
258 /* set the request content length based on the headers */
259 static DWORD set_content_length( http_request_t *lpwhr )
261 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
262 WCHAR encoding[20];
263 DWORD size;
265 size = sizeof(lpwhr->dwContentLength);
266 if (!HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
267 &lpwhr->dwContentLength, &size, NULL))
268 lpwhr->dwContentLength = ~0u;
270 size = sizeof(encoding);
271 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) &&
272 !strcmpiW(encoding, szChunked))
274 lpwhr->dwContentLength = ~0u;
275 lpwhr->read_chunked = TRUE;
278 if(lpwhr->decoding) {
279 int encoding_idx;
281 static const WCHAR gzipW[] = {'g','z','i','p',0};
283 encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
284 if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
285 init_gzip_stream(lpwhr);
288 return lpwhr->dwContentLength;
291 /***********************************************************************
292 * HTTP_Tokenize (internal)
294 * Tokenize a string, allocating memory for the tokens.
296 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
298 LPWSTR * token_array;
299 int tokens = 0;
300 int i;
301 LPCWSTR next_token;
303 if (string)
305 /* empty string has no tokens */
306 if (*string)
307 tokens++;
308 /* count tokens */
309 for (i = 0; string[i]; i++)
311 if (!strncmpW(string+i, token_string, strlenW(token_string)))
313 DWORD j;
314 tokens++;
315 /* we want to skip over separators, but not the null terminator */
316 for (j = 0; j < strlenW(token_string) - 1; j++)
317 if (!string[i+j])
318 break;
319 i += j;
324 /* add 1 for terminating NULL */
325 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
326 token_array[tokens] = NULL;
327 if (!tokens)
328 return token_array;
329 for (i = 0; i < tokens; i++)
331 int len;
332 next_token = strstrW(string, token_string);
333 if (!next_token) next_token = string+strlenW(string);
334 len = next_token - string;
335 token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
336 memcpy(token_array[i], string, len*sizeof(WCHAR));
337 token_array[i][len] = '\0';
338 string = next_token+strlenW(token_string);
340 return token_array;
343 /***********************************************************************
344 * HTTP_FreeTokens (internal)
346 * Frees memory returned from HTTP_Tokenize.
348 static void HTTP_FreeTokens(LPWSTR * token_array)
350 int i;
351 for (i = 0; token_array[i]; i++)
352 HeapFree(GetProcessHeap(), 0, token_array[i]);
353 HeapFree(GetProcessHeap(), 0, token_array);
356 /* **********************************************************************
358 * Helper functions for the HttpSendRequest(Ex) functions
361 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
363 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
364 http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
366 TRACE("%p\n", lpwhr);
368 HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
369 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
370 req->dwContentLength, req->bEndRequest);
372 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
375 static void HTTP_FixURL(http_request_t *lpwhr)
377 static const WCHAR szSlash[] = { '/',0 };
378 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
380 /* If we don't have a path we set it to root */
381 if (NULL == lpwhr->lpszPath)
382 lpwhr->lpszPath = heap_strdupW(szSlash);
383 else /* remove \r and \n*/
385 int nLen = strlenW(lpwhr->lpszPath);
386 while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
388 nLen--;
389 lpwhr->lpszPath[nLen]='\0';
391 /* Replace '\' with '/' */
392 while (nLen>0) {
393 nLen--;
394 if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
398 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
399 lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
400 && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
402 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
403 (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
404 *fixurl = '/';
405 strcpyW(fixurl + 1, lpwhr->lpszPath);
406 HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
407 lpwhr->lpszPath = fixurl;
411 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
413 LPWSTR requestString;
414 DWORD len, n;
415 LPCWSTR *req;
416 UINT i;
417 LPWSTR p;
419 static const WCHAR szSpace[] = { ' ',0 };
420 static const WCHAR szColon[] = { ':',' ',0 };
421 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
423 /* allocate space for an array of all the string pointers to be added */
424 len = (lpwhr->nCustHeaders)*4 + 10;
425 req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
427 /* add the verb, path and HTTP version string */
428 n = 0;
429 req[n++] = verb;
430 req[n++] = szSpace;
431 req[n++] = path;
432 req[n++] = szSpace;
433 req[n++] = version;
435 /* Append custom request headers */
436 for (i = 0; i < lpwhr->nCustHeaders; i++)
438 if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
440 req[n++] = szCrLf;
441 req[n++] = lpwhr->pCustHeaders[i].lpszField;
442 req[n++] = szColon;
443 req[n++] = lpwhr->pCustHeaders[i].lpszValue;
445 TRACE("Adding custom header %s (%s)\n",
446 debugstr_w(lpwhr->pCustHeaders[i].lpszField),
447 debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
451 if( n >= len )
452 ERR("oops. buffer overrun\n");
454 req[n] = NULL;
455 requestString = HTTP_build_req( req, 4 );
456 HeapFree( GetProcessHeap(), 0, req );
459 * Set (header) termination string for request
460 * Make sure there's exactly two new lines at the end of the request
462 p = &requestString[strlenW(requestString)-1];
463 while ( (*p == '\n') || (*p == '\r') )
464 p--;
465 strcpyW( p+1, sztwocrlf );
467 return requestString;
470 static void HTTP_ProcessCookies( http_request_t *lpwhr )
472 int HeaderIndex;
473 int numCookies = 0;
474 LPHTTPHEADERW setCookieHeader;
476 while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
478 setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
480 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
482 int len;
483 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
484 LPWSTR buf_url;
485 LPHTTPHEADERW Host;
487 Host = HTTP_GetHeader(lpwhr, hostW);
488 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
489 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
490 sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
491 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
493 HeapFree(GetProcessHeap(), 0, buf_url);
495 numCookies++;
499 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue )
501 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
502 return !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
503 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
506 static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
507 struct HttpAuthInfo **ppAuthInfo,
508 LPWSTR domain_and_username, LPWSTR password )
510 SECURITY_STATUS sec_status;
511 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
512 BOOL first = FALSE;
514 TRACE("%s\n", debugstr_w(pszAuthValue));
516 if (!pAuthInfo)
518 TimeStamp exp;
520 first = TRUE;
521 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
522 if (!pAuthInfo)
523 return FALSE;
525 SecInvalidateHandle(&pAuthInfo->cred);
526 SecInvalidateHandle(&pAuthInfo->ctx);
527 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
528 pAuthInfo->attr = 0;
529 pAuthInfo->auth_data = NULL;
530 pAuthInfo->auth_data_len = 0;
531 pAuthInfo->finished = FALSE;
533 if (is_basic_auth_value(pszAuthValue))
535 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
536 pAuthInfo->scheme = heap_strdupW(szBasic);
537 if (!pAuthInfo->scheme)
539 HeapFree(GetProcessHeap(), 0, pAuthInfo);
540 return FALSE;
543 else
545 PVOID pAuthData;
546 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
548 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
549 if (!pAuthInfo->scheme)
551 HeapFree(GetProcessHeap(), 0, pAuthInfo);
552 return FALSE;
555 if (domain_and_username)
557 WCHAR *user = strchrW(domain_and_username, '\\');
558 WCHAR *domain = domain_and_username;
560 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
562 pAuthData = &nt_auth_identity;
564 if (user) user++;
565 else
567 user = domain_and_username;
568 domain = NULL;
571 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
572 nt_auth_identity.User = user;
573 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
574 nt_auth_identity.Domain = domain;
575 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
576 nt_auth_identity.Password = password;
577 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
579 else
580 /* use default credentials */
581 pAuthData = NULL;
583 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
584 SECPKG_CRED_OUTBOUND, NULL,
585 pAuthData, NULL,
586 NULL, &pAuthInfo->cred,
587 &exp);
588 if (sec_status == SEC_E_OK)
590 PSecPkgInfoW sec_pkg_info;
591 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
592 if (sec_status == SEC_E_OK)
594 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
595 FreeContextBuffer(sec_pkg_info);
598 if (sec_status != SEC_E_OK)
600 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
601 debugstr_w(pAuthInfo->scheme), sec_status);
602 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
603 HeapFree(GetProcessHeap(), 0, pAuthInfo);
604 return FALSE;
607 *ppAuthInfo = pAuthInfo;
609 else if (pAuthInfo->finished)
610 return FALSE;
612 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
613 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
615 ERR("authentication scheme changed from %s to %s\n",
616 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
617 return FALSE;
620 if (is_basic_auth_value(pszAuthValue))
622 int userlen;
623 int passlen;
624 char *auth_data;
626 TRACE("basic authentication\n");
628 /* we don't cache credentials for basic authentication, so we can't
629 * retrieve them if the application didn't pass us any credentials */
630 if (!domain_and_username) return FALSE;
632 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
633 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
635 /* length includes a nul terminator, which will be re-used for the ':' */
636 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
637 if (!auth_data)
638 return FALSE;
640 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
641 auth_data[userlen] = ':';
642 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
644 pAuthInfo->auth_data = auth_data;
645 pAuthInfo->auth_data_len = userlen + 1 + passlen;
646 pAuthInfo->finished = TRUE;
648 return TRUE;
650 else
652 LPCWSTR pszAuthData;
653 SecBufferDesc out_desc, in_desc;
654 SecBuffer out, in;
655 unsigned char *buffer;
656 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
657 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
659 in.BufferType = SECBUFFER_TOKEN;
660 in.cbBuffer = 0;
661 in.pvBuffer = NULL;
663 in_desc.ulVersion = 0;
664 in_desc.cBuffers = 1;
665 in_desc.pBuffers = &in;
667 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
668 if (*pszAuthData == ' ')
670 pszAuthData++;
671 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
672 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
673 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
676 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
678 out.BufferType = SECBUFFER_TOKEN;
679 out.cbBuffer = pAuthInfo->max_token;
680 out.pvBuffer = buffer;
682 out_desc.ulVersion = 0;
683 out_desc.cBuffers = 1;
684 out_desc.pBuffers = &out;
686 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
687 first ? NULL : &pAuthInfo->ctx,
688 first ? lpwhr->lpHttpSession->lpszServerName : NULL,
689 context_req, 0, SECURITY_NETWORK_DREP,
690 in.pvBuffer ? &in_desc : NULL,
691 0, &pAuthInfo->ctx, &out_desc,
692 &pAuthInfo->attr, &pAuthInfo->exp);
693 if (sec_status == SEC_E_OK)
695 pAuthInfo->finished = TRUE;
696 pAuthInfo->auth_data = out.pvBuffer;
697 pAuthInfo->auth_data_len = out.cbBuffer;
698 TRACE("sending last auth packet\n");
700 else if (sec_status == SEC_I_CONTINUE_NEEDED)
702 pAuthInfo->auth_data = out.pvBuffer;
703 pAuthInfo->auth_data_len = out.cbBuffer;
704 TRACE("sending next auth packet\n");
706 else
708 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
709 pAuthInfo->finished = TRUE;
710 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
711 return FALSE;
715 return TRUE;
718 /***********************************************************************
719 * HTTP_HttpAddRequestHeadersW (internal)
721 static BOOL HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
722 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
724 LPWSTR lpszStart;
725 LPWSTR lpszEnd;
726 LPWSTR buffer;
727 BOOL bSuccess = FALSE;
728 DWORD len;
730 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
732 if( dwHeaderLength == ~0U )
733 len = strlenW(lpszHeader);
734 else
735 len = dwHeaderLength;
736 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
737 lstrcpynW( buffer, lpszHeader, len + 1);
739 lpszStart = buffer;
743 LPWSTR * pFieldAndValue;
745 lpszEnd = lpszStart;
747 while (*lpszEnd != '\0')
749 if (*lpszEnd == '\r' || *lpszEnd == '\n')
750 break;
751 lpszEnd++;
754 if (*lpszStart == '\0')
755 break;
757 if (*lpszEnd == '\r' || *lpszEnd == '\n')
759 *lpszEnd = '\0';
760 lpszEnd++; /* Jump over newline */
762 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
763 if (*lpszStart == '\0')
765 /* Skip 0-length headers */
766 lpszStart = lpszEnd;
767 bSuccess = TRUE;
768 continue;
770 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
771 if (pFieldAndValue)
773 bSuccess = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
774 if (bSuccess)
775 bSuccess = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
776 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
777 HTTP_FreeTokens(pFieldAndValue);
780 lpszStart = lpszEnd;
781 } while (bSuccess);
783 HeapFree(GetProcessHeap(), 0, buffer);
785 return bSuccess;
788 /***********************************************************************
789 * HttpAddRequestHeadersW (WININET.@)
791 * Adds one or more HTTP header to the request handler
793 * NOTE
794 * On Windows if dwHeaderLength includes the trailing '\0', then
795 * HttpAddRequestHeadersW() adds it too. However this results in an
796 * invalid Http header which is rejected by some servers so we probably
797 * don't need to match Windows on that point.
799 * RETURNS
800 * TRUE on success
801 * FALSE on failure
804 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
805 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
807 BOOL bSuccess = FALSE;
808 http_request_t *lpwhr;
810 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
812 if (!lpszHeader)
813 return TRUE;
815 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
816 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
818 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
819 goto lend;
821 bSuccess = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
822 lend:
823 if( lpwhr )
824 WININET_Release( &lpwhr->hdr );
826 return bSuccess;
829 /***********************************************************************
830 * HttpAddRequestHeadersA (WININET.@)
832 * Adds one or more HTTP header to the request handler
834 * RETURNS
835 * TRUE on success
836 * FALSE on failure
839 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
840 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
842 DWORD len;
843 LPWSTR hdr;
844 BOOL r;
846 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
848 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
849 hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
850 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
851 if( dwHeaderLength != ~0U )
852 dwHeaderLength = len;
854 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
856 HeapFree( GetProcessHeap(), 0, hdr );
858 return r;
861 /***********************************************************************
862 * HttpEndRequestA (WININET.@)
864 * Ends an HTTP request that was started by HttpSendRequestEx
866 * RETURNS
867 * TRUE if successful
868 * FALSE on failure
871 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
872 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
874 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
876 if (lpBuffersOut)
878 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
879 return FALSE;
882 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
885 static BOOL HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
887 BOOL rc = FALSE;
888 INT responseLen;
889 DWORD dwBufferSize;
890 INTERNET_ASYNC_RESULT iar;
892 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
893 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
895 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
896 if (responseLen)
897 rc = TRUE;
899 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
900 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
902 /* process cookies here. Is this right? */
903 HTTP_ProcessCookies(lpwhr);
905 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
907 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
909 DWORD dwCode,dwCodeLength = sizeof(DWORD);
910 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) &&
911 (dwCode == 302 || dwCode == 301 || dwCode == 303))
913 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
914 dwBufferSize=sizeof(szNewLocation);
915 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL))
917 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
919 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
920 lpwhr->lpszVerb = heap_strdupW(szGET);
922 HTTP_DrainContent(lpwhr);
923 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
925 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
926 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
927 rc = HTTP_HandleRedirect(lpwhr, new_url);
928 if (rc)
929 rc = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
930 HeapFree( GetProcessHeap(), 0, new_url );
936 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
937 iar.dwError = rc ? 0 : INTERNET_GetLastError();
939 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
940 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
941 sizeof(INTERNET_ASYNC_RESULT));
942 return rc;
945 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
947 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
948 http_request_t *lpwhr = (http_request_t*)work->hdr;
950 TRACE("%p\n", lpwhr);
952 HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
955 /***********************************************************************
956 * HttpEndRequestW (WININET.@)
958 * Ends an HTTP request that was started by HttpSendRequestEx
960 * RETURNS
961 * TRUE if successful
962 * FALSE on failure
965 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
966 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
968 BOOL rc = FALSE;
969 http_request_t *lpwhr;
971 TRACE("-->\n");
973 if (lpBuffersOut)
975 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
976 return FALSE;
979 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
981 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
983 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
984 if (lpwhr)
985 WININET_Release( &lpwhr->hdr );
986 return FALSE;
988 lpwhr->hdr.dwFlags |= dwFlags;
990 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
992 WORKREQUEST work;
993 struct WORKREQ_HTTPENDREQUESTW *request;
995 work.asyncproc = AsyncHttpEndRequestProc;
996 work.hdr = WININET_AddRef( &lpwhr->hdr );
998 request = &work.u.HttpEndRequestW;
999 request->dwFlags = dwFlags;
1000 request->dwContext = dwContext;
1002 INTERNET_AsyncCall(&work);
1003 INTERNET_SetLastError(ERROR_IO_PENDING);
1005 else
1006 rc = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
1008 WININET_Release( &lpwhr->hdr );
1009 TRACE("%i <--\n",rc);
1010 return rc;
1013 /***********************************************************************
1014 * HttpOpenRequestW (WININET.@)
1016 * Open a HTTP request handle
1018 * RETURNS
1019 * HINTERNET a HTTP request handle on success
1020 * NULL on failure
1023 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
1024 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
1025 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
1026 DWORD dwFlags, DWORD_PTR dwContext)
1028 http_session_t *lpwhs;
1029 HINTERNET handle = NULL;
1031 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1032 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
1033 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
1034 dwFlags, dwContext);
1035 if(lpszAcceptTypes!=NULL)
1037 int i;
1038 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
1039 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
1042 lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
1043 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
1045 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1046 goto lend;
1050 * My tests seem to show that the windows version does not
1051 * become asynchronous until after this point. And anyhow
1052 * if this call was asynchronous then how would you get the
1053 * necessary HINTERNET pointer returned by this function.
1056 handle = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
1057 lpszVersion, lpszReferrer, lpszAcceptTypes,
1058 dwFlags, dwContext);
1059 lend:
1060 if( lpwhs )
1061 WININET_Release( &lpwhs->hdr );
1062 TRACE("returning %p\n", handle);
1063 return handle;
1067 /***********************************************************************
1068 * HttpOpenRequestA (WININET.@)
1070 * Open a HTTP request handle
1072 * RETURNS
1073 * HINTERNET a HTTP request handle on success
1074 * NULL on failure
1077 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1078 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1079 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1080 DWORD dwFlags, DWORD_PTR dwContext)
1082 LPWSTR szVerb = NULL, szObjectName = NULL;
1083 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1084 INT acceptTypesCount;
1085 HINTERNET rc = FALSE;
1086 LPCSTR *types;
1088 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1089 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1090 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1091 dwFlags, dwContext);
1093 if (lpszVerb)
1095 szVerb = heap_strdupAtoW(lpszVerb);
1096 if ( !szVerb )
1097 goto end;
1100 if (lpszObjectName)
1102 szObjectName = heap_strdupAtoW(lpszObjectName);
1103 if ( !szObjectName )
1104 goto end;
1107 if (lpszVersion)
1109 szVersion = heap_strdupAtoW(lpszVersion);
1110 if ( !szVersion )
1111 goto end;
1114 if (lpszReferrer)
1116 szReferrer = heap_strdupAtoW(lpszReferrer);
1117 if ( !szReferrer )
1118 goto end;
1121 if (lpszAcceptTypes)
1123 acceptTypesCount = 0;
1124 types = lpszAcceptTypes;
1125 while (*types)
1127 __TRY
1129 /* find out how many there are */
1130 if (*types && **types)
1132 TRACE("accept type: %s\n", debugstr_a(*types));
1133 acceptTypesCount++;
1136 __EXCEPT_PAGE_FAULT
1138 WARN("invalid accept type pointer\n");
1140 __ENDTRY;
1141 types++;
1143 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1144 if (!szAcceptTypes) goto end;
1146 acceptTypesCount = 0;
1147 types = lpszAcceptTypes;
1148 while (*types)
1150 __TRY
1152 if (*types && **types)
1153 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1155 __EXCEPT_PAGE_FAULT
1157 /* ignore invalid pointer */
1159 __ENDTRY;
1160 types++;
1162 szAcceptTypes[acceptTypesCount] = NULL;
1165 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1166 szVersion, szReferrer,
1167 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1169 end:
1170 if (szAcceptTypes)
1172 acceptTypesCount = 0;
1173 while (szAcceptTypes[acceptTypesCount])
1175 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1176 acceptTypesCount++;
1178 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1180 HeapFree(GetProcessHeap(), 0, szReferrer);
1181 HeapFree(GetProcessHeap(), 0, szVersion);
1182 HeapFree(GetProcessHeap(), 0, szObjectName);
1183 HeapFree(GetProcessHeap(), 0, szVerb);
1185 return rc;
1188 /***********************************************************************
1189 * HTTP_EncodeBase64
1191 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1193 UINT n = 0, x;
1194 static const CHAR HTTP_Base64Enc[] =
1195 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1197 while( len > 0 )
1199 /* first 6 bits, all from bin[0] */
1200 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1201 x = (bin[0] & 3) << 4;
1203 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1204 if( len == 1 )
1206 base64[n++] = HTTP_Base64Enc[x];
1207 base64[n++] = '=';
1208 base64[n++] = '=';
1209 break;
1211 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1212 x = ( bin[1] & 0x0f ) << 2;
1214 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1215 if( len == 2 )
1217 base64[n++] = HTTP_Base64Enc[x];
1218 base64[n++] = '=';
1219 break;
1221 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1223 /* last 6 bits, all from bin [2] */
1224 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1225 bin += 3;
1226 len -= 3;
1228 base64[n] = 0;
1229 return n;
1232 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1233 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1234 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1235 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1236 static const signed char HTTP_Base64Dec[256] =
1238 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1239 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1240 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1241 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1242 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1243 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1244 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1245 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1246 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1247 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1248 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1249 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1250 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1251 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1252 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1253 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1254 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1255 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1256 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1257 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1258 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1259 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1260 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1261 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1262 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1263 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1265 #undef CH
1267 /***********************************************************************
1268 * HTTP_DecodeBase64
1270 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1272 unsigned int n = 0;
1274 while(*base64)
1276 signed char in[4];
1278 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1279 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1280 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1281 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1283 WARN("invalid base64: %s\n", debugstr_w(base64));
1284 return 0;
1286 if (bin)
1287 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1288 n++;
1290 if ((base64[2] == '=') && (base64[3] == '='))
1291 break;
1292 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1293 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1295 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1296 return 0;
1298 if (bin)
1299 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1300 n++;
1302 if (base64[3] == '=')
1303 break;
1304 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1305 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1307 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1308 return 0;
1310 if (bin)
1311 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1312 n++;
1314 base64 += 4;
1317 return n;
1320 /***********************************************************************
1321 * HTTP_InsertAuthorization
1323 * Insert or delete the authorization field in the request header.
1325 static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1327 if (pAuthInfo)
1329 static const WCHAR wszSpace[] = {' ',0};
1330 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1331 unsigned int len;
1332 WCHAR *authorization = NULL;
1334 if (pAuthInfo->auth_data_len)
1336 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1337 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1338 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1339 if (!authorization)
1340 return FALSE;
1342 strcpyW(authorization, pAuthInfo->scheme);
1343 strcatW(authorization, wszSpace);
1344 HTTP_EncodeBase64(pAuthInfo->auth_data,
1345 pAuthInfo->auth_data_len,
1346 authorization+strlenW(authorization));
1348 /* clear the data as it isn't valid now that it has been sent to the
1349 * server, unless it's Basic authentication which doesn't do
1350 * connection tracking */
1351 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1353 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1354 pAuthInfo->auth_data = NULL;
1355 pAuthInfo->auth_data_len = 0;
1359 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1361 HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1363 HeapFree(GetProcessHeap(), 0, authorization);
1365 return TRUE;
1368 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1370 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1371 DWORD size;
1373 size = sizeof(new_location);
1374 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL))
1376 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1377 strcpyW( url, new_location );
1379 else
1381 static const WCHAR slash[] = { '/',0 };
1382 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1383 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1384 http_session_t *session = req->lpHttpSession;
1386 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1387 size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
1389 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1391 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1392 sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
1393 else
1394 sprintfW( url, format, session->lpszHostName, session->nHostPort );
1395 if (req->lpszPath[0] != '/') strcatW( url, slash );
1396 strcatW( url, req->lpszPath );
1398 TRACE("url=%s\n", debugstr_w(url));
1399 return url;
1402 /***********************************************************************
1403 * HTTP_DealWithProxy
1405 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
1407 WCHAR buf[MAXHOSTNAME];
1408 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1409 static WCHAR szNul[] = { 0 };
1410 URL_COMPONENTSW UrlComponents;
1411 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1412 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1414 memset( &UrlComponents, 0, sizeof UrlComponents );
1415 UrlComponents.dwStructSize = sizeof UrlComponents;
1416 UrlComponents.lpszHostName = buf;
1417 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1419 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1420 hIC->lpszProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1421 sprintfW(proxy, szFormat, hIC->lpszProxy);
1422 else
1423 strcpyW(proxy, hIC->lpszProxy);
1424 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1425 return FALSE;
1426 if( UrlComponents.dwHostNameLength == 0 )
1427 return FALSE;
1429 if( !lpwhr->lpszPath )
1430 lpwhr->lpszPath = szNul;
1432 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1433 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1435 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1436 lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1437 lpwhs->nServerPort = UrlComponents.nPort;
1439 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1440 return TRUE;
1443 #ifndef INET6_ADDRSTRLEN
1444 #define INET6_ADDRSTRLEN 46
1445 #endif
1447 static BOOL HTTP_ResolveName(http_request_t *lpwhr)
1449 char szaddr[INET6_ADDRSTRLEN];
1450 http_session_t *lpwhs = lpwhr->lpHttpSession;
1451 const void *addr;
1453 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1454 INTERNET_STATUS_RESOLVING_NAME,
1455 lpwhs->lpszServerName,
1456 strlenW(lpwhs->lpszServerName)+1);
1458 lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1459 if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1460 (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1462 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1463 return FALSE;
1466 switch (lpwhs->socketAddress.ss_family)
1468 case AF_INET:
1469 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1470 break;
1471 case AF_INET6:
1472 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1473 break;
1474 default:
1475 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1476 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1477 return FALSE;
1479 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1480 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1481 INTERNET_STATUS_NAME_RESOLVED,
1482 szaddr, strlen(szaddr)+1);
1484 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1485 return TRUE;
1489 /***********************************************************************
1490 * HTTPREQ_Destroy (internal)
1492 * Deallocate request handle
1495 static void HTTPREQ_Destroy(object_header_t *hdr)
1497 http_request_t *lpwhr = (http_request_t*) hdr;
1498 DWORD i;
1500 TRACE("\n");
1502 if(lpwhr->hCacheFile)
1503 CloseHandle(lpwhr->hCacheFile);
1505 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1507 DeleteCriticalSection( &lpwhr->read_section );
1508 WININET_Release(&lpwhr->lpHttpSession->hdr);
1510 if (lpwhr->pAuthInfo)
1512 if (SecIsValidHandle(&lpwhr->pAuthInfo->ctx))
1513 DeleteSecurityContext(&lpwhr->pAuthInfo->ctx);
1514 if (SecIsValidHandle(&lpwhr->pAuthInfo->cred))
1515 FreeCredentialsHandle(&lpwhr->pAuthInfo->cred);
1517 HeapFree(GetProcessHeap(), 0, lpwhr->pAuthInfo->auth_data);
1518 HeapFree(GetProcessHeap(), 0, lpwhr->pAuthInfo->scheme);
1519 HeapFree(GetProcessHeap(), 0, lpwhr->pAuthInfo);
1520 lpwhr->pAuthInfo = NULL;
1523 if (lpwhr->pProxyAuthInfo)
1525 if (SecIsValidHandle(&lpwhr->pProxyAuthInfo->ctx))
1526 DeleteSecurityContext(&lpwhr->pProxyAuthInfo->ctx);
1527 if (SecIsValidHandle(&lpwhr->pProxyAuthInfo->cred))
1528 FreeCredentialsHandle(&lpwhr->pProxyAuthInfo->cred);
1530 HeapFree(GetProcessHeap(), 0, lpwhr->pProxyAuthInfo->auth_data);
1531 HeapFree(GetProcessHeap(), 0, lpwhr->pProxyAuthInfo->scheme);
1532 HeapFree(GetProcessHeap(), 0, lpwhr->pProxyAuthInfo);
1533 lpwhr->pProxyAuthInfo = NULL;
1536 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1537 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1538 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1539 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1540 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1542 for (i = 0; i < lpwhr->nCustHeaders; i++)
1544 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1545 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1548 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1549 HeapFree(GetProcessHeap(), 0, lpwhr);
1552 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1554 http_request_t *lpwhr = (http_request_t*) hdr;
1556 TRACE("%p\n",lpwhr);
1558 #ifdef HAVE_ZLIB
1559 if(lpwhr->gzip_stream) {
1560 inflateEnd(&lpwhr->gzip_stream->zstream);
1561 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1562 lpwhr->gzip_stream = NULL;
1564 #endif
1566 if (!NETCON_connected(&lpwhr->netConnection))
1567 return;
1569 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1570 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1572 NETCON_close(&lpwhr->netConnection);
1574 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1575 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1578 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1580 LPHTTPHEADERW host_header;
1582 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1584 host_header = HTTP_GetHeader(req, hostW);
1585 if(!host_header)
1586 return FALSE;
1588 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1589 return TRUE;
1592 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1594 http_request_t *req = (http_request_t*)hdr;
1596 switch(option) {
1597 case INTERNET_OPTION_SECURITY_FLAGS:
1599 http_session_t *lpwhs;
1600 lpwhs = req->lpHttpSession;
1602 if (*size < sizeof(ULONG))
1603 return ERROR_INSUFFICIENT_BUFFER;
1605 *size = sizeof(DWORD);
1606 if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1607 *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1608 else
1609 *(DWORD*)buffer = 0;
1610 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1611 return ERROR_SUCCESS;
1614 case INTERNET_OPTION_HANDLE_TYPE:
1615 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1617 if (*size < sizeof(ULONG))
1618 return ERROR_INSUFFICIENT_BUFFER;
1620 *size = sizeof(DWORD);
1621 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1622 return ERROR_SUCCESS;
1624 case INTERNET_OPTION_URL: {
1625 WCHAR url[INTERNET_MAX_URL_LENGTH];
1626 HTTPHEADERW *host;
1627 DWORD len;
1628 WCHAR *pch;
1630 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1632 TRACE("INTERNET_OPTION_URL\n");
1634 host = HTTP_GetHeader(req, hostW);
1635 strcpyW(url, httpW);
1636 strcatW(url, host->lpszValue);
1637 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1638 *pch = 0;
1639 strcatW(url, req->lpszPath);
1641 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1643 if(unicode) {
1644 len = (strlenW(url)+1) * sizeof(WCHAR);
1645 if(*size < len)
1646 return ERROR_INSUFFICIENT_BUFFER;
1648 *size = len;
1649 strcpyW(buffer, url);
1650 return ERROR_SUCCESS;
1651 }else {
1652 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1653 if(len > *size)
1654 return ERROR_INSUFFICIENT_BUFFER;
1656 *size = len;
1657 return ERROR_SUCCESS;
1661 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1662 INTERNET_CACHE_ENTRY_INFOW *info;
1663 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1664 WCHAR url[INTERNET_MAX_URL_LENGTH];
1665 DWORD nbytes, error;
1666 BOOL ret;
1668 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1670 if (*size < sizeof(*ts))
1672 *size = sizeof(*ts);
1673 return ERROR_INSUFFICIENT_BUFFER;
1675 nbytes = 0;
1676 HTTP_GetRequestURL(req, url);
1677 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1678 error = GetLastError();
1679 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1681 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1682 return ERROR_OUTOFMEMORY;
1684 GetUrlCacheEntryInfoW(url, info, &nbytes);
1686 ts->ftExpires = info->ExpireTime;
1687 ts->ftLastModified = info->LastModifiedTime;
1689 HeapFree(GetProcessHeap(), 0, info);
1690 *size = sizeof(*ts);
1691 return ERROR_SUCCESS;
1693 return error;
1696 case INTERNET_OPTION_DATAFILE_NAME: {
1697 DWORD req_size;
1699 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1701 if(!req->lpszCacheFile) {
1702 *size = 0;
1703 return ERROR_INTERNET_ITEM_NOT_FOUND;
1706 if(unicode) {
1707 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1708 if(*size < req_size)
1709 return ERROR_INSUFFICIENT_BUFFER;
1711 *size = req_size;
1712 memcpy(buffer, req->lpszCacheFile, *size);
1713 return ERROR_SUCCESS;
1714 }else {
1715 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1716 if (req_size > *size)
1717 return ERROR_INSUFFICIENT_BUFFER;
1719 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1720 -1, buffer, *size, NULL, NULL);
1721 return ERROR_SUCCESS;
1725 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1726 PCCERT_CONTEXT context;
1728 if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1729 *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1730 return ERROR_INSUFFICIENT_BUFFER;
1733 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1734 if(context) {
1735 INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1736 DWORD len;
1738 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1739 info->ftExpiry = context->pCertInfo->NotAfter;
1740 info->ftStart = context->pCertInfo->NotBefore;
1741 if(unicode) {
1742 len = CertNameToStrW(context->dwCertEncodingType,
1743 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1744 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1745 if(info->lpszSubjectInfo)
1746 CertNameToStrW(context->dwCertEncodingType,
1747 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1748 info->lpszSubjectInfo, len);
1749 len = CertNameToStrW(context->dwCertEncodingType,
1750 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1751 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1752 if (info->lpszIssuerInfo)
1753 CertNameToStrW(context->dwCertEncodingType,
1754 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1755 info->lpszIssuerInfo, len);
1756 }else {
1757 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1759 len = CertNameToStrA(context->dwCertEncodingType,
1760 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1761 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1762 if(infoA->lpszSubjectInfo)
1763 CertNameToStrA(context->dwCertEncodingType,
1764 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1765 infoA->lpszSubjectInfo, len);
1766 len = CertNameToStrA(context->dwCertEncodingType,
1767 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1768 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1769 if(infoA->lpszIssuerInfo)
1770 CertNameToStrA(context->dwCertEncodingType,
1771 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1772 infoA->lpszIssuerInfo, len);
1776 * Contrary to MSDN, these do not appear to be set.
1777 * lpszProtocolName
1778 * lpszSignatureAlgName
1779 * lpszEncryptionAlgName
1780 * dwKeySize
1782 CertFreeCertificateContext(context);
1783 return ERROR_SUCCESS;
1788 return INET_QueryOption(option, buffer, size, unicode);
1791 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1793 http_request_t *req = (http_request_t*)hdr;
1795 switch(option) {
1796 case INTERNET_OPTION_SEND_TIMEOUT:
1797 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1798 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1800 if (size != sizeof(DWORD))
1801 return ERROR_INVALID_PARAMETER;
1803 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1804 *(DWORD*)buffer);
1806 case INTERNET_OPTION_USERNAME:
1807 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1808 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1809 return ERROR_SUCCESS;
1811 case INTERNET_OPTION_PASSWORD:
1812 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1813 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1814 return ERROR_SUCCESS;
1815 case INTERNET_OPTION_HTTP_DECODING:
1816 if(size != sizeof(BOOL))
1817 return ERROR_INVALID_PARAMETER;
1818 req->decoding = *(BOOL*)buffer;
1819 return ERROR_SUCCESS;
1822 return ERROR_INTERNET_INVALID_OPTION;
1825 /* read some more data into the read buffer (the read section must be held) */
1826 static BOOL read_more_data( http_request_t *req, int maxlen )
1828 int len;
1830 if (req->read_pos)
1832 /* move existing data to the start of the buffer */
1833 if(req->read_size)
1834 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1835 req->read_pos = 0;
1838 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1840 if(!NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1841 maxlen - req->read_size, 0, &len ))
1842 return FALSE;
1844 req->read_size += len;
1845 return TRUE;
1848 /* remove some amount of data from the read buffer (the read section must be held) */
1849 static void remove_data( http_request_t *req, int count )
1851 if (!(req->read_size -= count)) req->read_pos = 0;
1852 else req->read_pos += count;
1855 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1857 int count, bytes_read, pos = 0;
1859 EnterCriticalSection( &req->read_section );
1860 for (;;)
1862 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1864 if (eol)
1866 count = eol - (req->read_buf + req->read_pos);
1867 bytes_read = count + 1;
1869 else count = bytes_read = req->read_size;
1871 count = min( count, *len - pos );
1872 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1873 pos += count;
1874 remove_data( req, bytes_read );
1875 if (eol) break;
1877 if (!read_more_data( req, -1 ) || !req->read_size)
1879 *len = 0;
1880 TRACE( "returning empty string\n" );
1881 LeaveCriticalSection( &req->read_section );
1882 return FALSE;
1885 LeaveCriticalSection( &req->read_section );
1887 if (pos < *len)
1889 if (pos && buffer[pos - 1] == '\r') pos--;
1890 *len = pos + 1;
1892 buffer[*len - 1] = 0;
1893 TRACE( "returning %s\n", debugstr_a(buffer));
1894 return TRUE;
1897 /* discard data contents until we reach end of line (the read section must be held) */
1898 static BOOL discard_eol( http_request_t *req )
1902 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1903 if (eol)
1905 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
1906 break;
1908 req->read_pos = req->read_size = 0; /* discard everything */
1909 if (!read_more_data( req, -1 )) return FALSE;
1910 } while (req->read_size);
1911 return TRUE;
1914 /* read the size of the next chunk (the read section must be held) */
1915 static BOOL start_next_chunk( http_request_t *req )
1917 DWORD chunk_size = 0;
1919 if (!req->dwContentLength) return TRUE;
1920 if (req->dwContentLength == req->dwContentRead)
1922 /* read terminator for the previous chunk */
1923 if (!discard_eol( req )) return FALSE;
1924 req->dwContentLength = ~0u;
1925 req->dwContentRead = 0;
1927 for (;;)
1929 while (req->read_size)
1931 char ch = req->read_buf[req->read_pos];
1932 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
1933 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
1934 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
1935 else if (ch == ';' || ch == '\r' || ch == '\n')
1937 TRACE( "reading %u byte chunk\n", chunk_size );
1938 req->dwContentLength = chunk_size;
1939 req->dwContentRead = 0;
1940 if (!discard_eol( req )) return FALSE;
1941 return TRUE;
1943 remove_data( req, 1 );
1945 if (!read_more_data( req, -1 )) return FALSE;
1946 if (!req->read_size)
1948 req->dwContentLength = req->dwContentRead = 0;
1949 return TRUE;
1954 /* check if we have reached the end of the data to read (the read section must be held) */
1955 static BOOL end_of_read_data( http_request_t *req )
1957 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
1958 if (req->read_chunked) return (req->dwContentLength == 0);
1959 if (req->dwContentLength == ~0u) return FALSE;
1960 return (req->dwContentLength == req->dwContentRead);
1963 /* fetch some more data into the read buffer (the read section must be held) */
1964 static BOOL refill_buffer( http_request_t *req )
1966 int len = sizeof(req->read_buf);
1968 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
1970 if (!start_next_chunk( req )) return FALSE;
1973 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
1974 if (len <= req->read_size) return TRUE;
1976 if (!read_more_data( req, len )) return FALSE;
1977 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
1978 return TRUE;
1981 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
1983 DWORD ret = ERROR_SUCCESS;
1984 int read = 0;
1986 #ifdef HAVE_ZLIB
1987 z_stream *zstream = &req->gzip_stream->zstream;
1988 int zres;
1990 while(read < size && !req->gzip_stream->end_of_data) {
1991 if(!req->read_size) {
1992 if(!sync || !refill_buffer(req))
1993 break;
1996 zstream->next_in = req->read_buf+req->read_pos;
1997 zstream->avail_in = req->read_size;
1998 zstream->next_out = buf+read;
1999 zstream->avail_out = size-read;
2000 zres = inflate(zstream, Z_FULL_FLUSH);
2001 read = size - zstream->avail_out;
2002 remove_data(req, req->read_size-zstream->avail_in);
2003 if(zres == Z_STREAM_END) {
2004 TRACE("end of data\n");
2005 req->gzip_stream->end_of_data = TRUE;
2006 }else if(zres != Z_OK) {
2007 WARN("inflate failed %d\n", zres);
2008 if(!read)
2009 ret = ERROR_INTERNET_DECODING_FAILED;
2010 break;
2013 #endif
2015 *read_ret = read;
2016 return ret;
2019 static void refill_gzip_buffer(http_request_t *req)
2021 DWORD res;
2022 int len;
2024 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2025 return;
2027 if(req->gzip_stream->buf_pos) {
2028 if(req->gzip_stream->buf_size)
2029 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2030 req->gzip_stream->buf_pos = 0;
2033 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2034 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2035 if(res == ERROR_SUCCESS)
2036 req->gzip_stream->buf_size += len;
2039 /* return the size of data available to be read immediately (the read section must be held) */
2040 static DWORD get_avail_data( http_request_t *req )
2042 if (req->gzip_stream) {
2043 refill_gzip_buffer(req);
2044 return req->gzip_stream->buf_size;
2046 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2047 return 0;
2048 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2051 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2053 INTERNET_ASYNC_RESULT iar;
2055 TRACE("%p\n", req);
2057 EnterCriticalSection( &req->read_section );
2058 if (refill_buffer( req )) {
2059 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2060 iar.dwError = first_notif ? 0 : get_avail_data(req);
2061 }else {
2062 iar.dwResult = 0;
2063 iar.dwError = INTERNET_GetLastError();
2065 LeaveCriticalSection( &req->read_section );
2067 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2068 sizeof(INTERNET_ASYNC_RESULT));
2071 /* read data from the http connection (the read section must be held) */
2072 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2074 BOOL finished_reading = FALSE;
2075 int len, bytes_read = 0;
2076 DWORD ret = ERROR_SUCCESS;
2078 EnterCriticalSection( &req->read_section );
2080 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2082 if (!start_next_chunk( req )) goto done;
2085 if(req->gzip_stream) {
2086 if(req->gzip_stream->buf_size) {
2087 bytes_read = min(req->gzip_stream->buf_size, size);
2088 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2089 req->gzip_stream->buf_pos += bytes_read;
2090 req->gzip_stream->buf_size -= bytes_read;
2091 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2092 refill_buffer(req);
2095 if(size > bytes_read) {
2096 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2097 if(ret == ERROR_SUCCESS)
2098 bytes_read += len;
2101 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2102 }else {
2103 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2105 if (req->read_size) {
2106 bytes_read = min( req->read_size, size );
2107 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2108 remove_data( req, bytes_read );
2111 if (size > bytes_read && (!bytes_read || sync)) {
2112 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2113 sync ? MSG_WAITALL : 0, &len))
2114 bytes_read += len;
2115 /* always return success, even if the network layer returns an error */
2118 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2120 done:
2121 req->dwContentRead += bytes_read;
2122 *read = bytes_read;
2124 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2125 LeaveCriticalSection( &req->read_section );
2127 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2128 BOOL res;
2129 DWORD dwBytesWritten;
2131 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2132 if(!res)
2133 WARN("WriteFile failed: %u\n", GetLastError());
2136 if(finished_reading)
2137 HTTP_FinishedReading(req);
2139 return ret;
2143 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2145 http_request_t *req = (http_request_t*)hdr;
2146 return HTTPREQ_Read(req, buffer, size, read, TRUE);
2149 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2151 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2152 http_request_t *req = (http_request_t*)workRequest->hdr;
2153 INTERNET_ASYNC_RESULT iar;
2154 DWORD res;
2156 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2158 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2159 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2161 iar.dwResult = res == ERROR_SUCCESS;
2162 iar.dwError = res;
2164 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2165 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2166 sizeof(INTERNET_ASYNC_RESULT));
2169 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2170 DWORD flags, DWORD_PTR context)
2172 http_request_t *req = (http_request_t*)hdr;
2173 DWORD res;
2175 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2176 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2178 if (buffers->dwStructSize != sizeof(*buffers))
2179 return ERROR_INVALID_PARAMETER;
2181 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2183 if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
2185 WORKREQUEST workRequest;
2187 if (TryEnterCriticalSection( &req->read_section ))
2189 if (get_avail_data(req))
2191 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2192 &buffers->dwBufferLength, FALSE);
2193 LeaveCriticalSection( &req->read_section );
2194 goto done;
2196 LeaveCriticalSection( &req->read_section );
2199 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2200 workRequest.hdr = WININET_AddRef(&req->hdr);
2201 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2203 INTERNET_AsyncCall(&workRequest);
2205 return ERROR_IO_PENDING;
2208 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2209 !(flags & IRF_NO_WAIT));
2211 done:
2212 if (res == ERROR_SUCCESS) {
2213 DWORD size = buffers->dwBufferLength;
2214 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2215 &size, sizeof(size));
2218 return res;
2221 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2223 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2224 http_request_t *req = (http_request_t*)workRequest->hdr;
2225 INTERNET_ASYNC_RESULT iar;
2226 DWORD res;
2228 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2230 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2231 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2233 iar.dwResult = res == ERROR_SUCCESS;
2234 iar.dwError = res;
2236 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2237 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2238 sizeof(INTERNET_ASYNC_RESULT));
2241 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2242 DWORD flags, DWORD_PTR context)
2245 http_request_t *req = (http_request_t*)hdr;
2246 DWORD res;
2248 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2249 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2251 if (buffers->dwStructSize != sizeof(*buffers))
2252 return ERROR_INVALID_PARAMETER;
2254 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2256 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2258 WORKREQUEST workRequest;
2260 if (TryEnterCriticalSection( &req->read_section ))
2262 if (get_avail_data(req))
2264 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2265 &buffers->dwBufferLength, FALSE);
2266 LeaveCriticalSection( &req->read_section );
2267 goto done;
2269 LeaveCriticalSection( &req->read_section );
2272 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2273 workRequest.hdr = WININET_AddRef(&req->hdr);
2274 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2276 INTERNET_AsyncCall(&workRequest);
2278 return ERROR_IO_PENDING;
2281 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2282 !(flags & IRF_NO_WAIT));
2284 done:
2285 if (res == ERROR_SUCCESS) {
2286 DWORD size = buffers->dwBufferLength;
2287 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2288 &size, sizeof(size));
2291 return res;
2294 static BOOL HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2296 BOOL ret;
2297 http_request_t *lpwhr = (http_request_t*)hdr;
2299 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2301 *written = 0;
2302 if ((ret = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written)))
2303 lpwhr->dwBytesWritten += *written;
2305 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2306 return ret;
2309 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2311 http_request_t *req = (http_request_t*)workRequest->hdr;
2313 HTTP_ReceiveRequestData(req, FALSE);
2316 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2318 http_request_t *req = (http_request_t*)hdr;
2320 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2322 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2324 WORKREQUEST workRequest;
2326 /* never wait, if we can't enter the section we queue an async request right away */
2327 if (TryEnterCriticalSection( &req->read_section ))
2329 if ((*available = get_avail_data( req ))) goto done;
2330 if (end_of_read_data( req )) goto done;
2331 LeaveCriticalSection( &req->read_section );
2334 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2335 workRequest.hdr = WININET_AddRef( &req->hdr );
2337 INTERNET_AsyncCall(&workRequest);
2339 return ERROR_IO_PENDING;
2342 EnterCriticalSection( &req->read_section );
2344 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2346 refill_buffer( req );
2347 *available = get_avail_data( req );
2350 done:
2351 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2353 DWORD extra;
2354 if (NETCON_query_data_available(&req->netConnection, &extra))
2355 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2357 LeaveCriticalSection( &req->read_section );
2359 TRACE( "returning %u\n", *available );
2360 return ERROR_SUCCESS;
2363 static const object_vtbl_t HTTPREQVtbl = {
2364 HTTPREQ_Destroy,
2365 HTTPREQ_CloseConnection,
2366 HTTPREQ_QueryOption,
2367 HTTPREQ_SetOption,
2368 HTTPREQ_ReadFile,
2369 HTTPREQ_ReadFileExA,
2370 HTTPREQ_ReadFileExW,
2371 HTTPREQ_WriteFile,
2372 HTTPREQ_QueryDataAvailable,
2373 NULL
2376 /***********************************************************************
2377 * HTTP_HttpOpenRequestW (internal)
2379 * Open a HTTP request handle
2381 * RETURNS
2382 * HINTERNET a HTTP request handle on success
2383 * NULL on failure
2386 HINTERNET WINAPI HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2387 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2388 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2389 DWORD dwFlags, DWORD_PTR dwContext)
2391 appinfo_t *hIC = NULL;
2392 http_request_t *lpwhr;
2393 LPWSTR lpszHostName = NULL;
2394 HINTERNET handle = NULL;
2395 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2396 DWORD len;
2398 TRACE("-->\n");
2400 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2401 hIC = lpwhs->lpAppInfo;
2403 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2404 if (NULL == lpwhr)
2406 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2407 goto lend;
2409 lpwhr->hdr.htype = WH_HHTTPREQ;
2410 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2411 lpwhr->hdr.dwFlags = dwFlags;
2412 lpwhr->hdr.dwContext = dwContext;
2413 lpwhr->hdr.refs = 1;
2414 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2415 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2416 lpwhr->dwContentLength = ~0u;
2417 InitializeCriticalSection( &lpwhr->read_section );
2419 WININET_AddRef( &lpwhs->hdr );
2420 lpwhr->lpHttpSession = lpwhs;
2421 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2423 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2424 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2425 if (NULL == lpszHostName)
2427 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2428 goto lend;
2431 handle = WININET_AllocHandle( &lpwhr->hdr );
2432 if (NULL == handle)
2434 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2435 goto lend;
2438 if (!NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE))
2440 InternetCloseHandle( handle );
2441 handle = NULL;
2442 goto lend;
2445 if (lpszObjectName && *lpszObjectName) {
2446 HRESULT rc;
2448 len = 0;
2449 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2450 if (rc != E_POINTER)
2451 len = strlenW(lpszObjectName)+1;
2452 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2453 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2454 URL_ESCAPE_SPACES_ONLY);
2455 if (rc != S_OK)
2457 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2458 strcpyW(lpwhr->lpszPath,lpszObjectName);
2460 }else {
2461 static const WCHAR slashW[] = {'/',0};
2463 lpwhr->lpszPath = heap_strdupW(slashW);
2466 if (lpszReferrer && *lpszReferrer)
2467 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2469 if (lpszAcceptTypes)
2471 int i;
2472 for (i = 0; lpszAcceptTypes[i]; i++)
2474 if (!*lpszAcceptTypes[i]) continue;
2475 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2476 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2477 HTTP_ADDHDR_FLAG_REQ |
2478 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2482 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2483 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2485 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2486 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2487 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2489 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2490 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2491 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2493 else
2494 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2495 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2497 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2498 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2499 INTERNET_DEFAULT_HTTPS_PORT :
2500 INTERNET_DEFAULT_HTTP_PORT);
2502 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2503 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2504 INTERNET_DEFAULT_HTTPS_PORT :
2505 INTERNET_DEFAULT_HTTP_PORT);
2507 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2508 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2510 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2511 INTERNET_STATUS_HANDLE_CREATED, &handle,
2512 sizeof(handle));
2514 lend:
2515 HeapFree(GetProcessHeap(), 0, lpszHostName);
2516 if( lpwhr )
2517 WININET_Release( &lpwhr->hdr );
2519 TRACE("<-- %p (%p)\n", handle, lpwhr);
2520 return handle;
2523 /* read any content returned by the server so that the connection can be
2524 * reused */
2525 static void HTTP_DrainContent(http_request_t *req)
2527 DWORD bytes_read;
2529 if (!NETCON_connected(&req->netConnection)) return;
2531 if (req->dwContentLength == -1)
2533 NETCON_close(&req->netConnection);
2534 return;
2536 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2540 char buffer[2048];
2541 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2542 return;
2543 } while (bytes_read);
2546 static const LPCWSTR header_lookup[] = {
2547 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2548 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2549 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2550 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2551 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2552 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2553 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2554 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2555 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2556 szDate, /* HTTP_QUERY_DATE = 9 */
2557 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2558 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2559 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2560 szURI, /* HTTP_QUERY_URI = 13 */
2561 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2562 NULL, /* HTTP_QUERY_COST = 15 */
2563 NULL, /* HTTP_QUERY_LINK = 16 */
2564 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2565 NULL, /* HTTP_QUERY_VERSION = 18 */
2566 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2567 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2568 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2569 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2570 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2571 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2572 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2573 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2574 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2575 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2576 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2577 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2578 NULL, /* HTTP_QUERY_FROM = 31 */
2579 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2580 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2581 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2582 szReferer, /* HTTP_QUERY_REFERER = 35 */
2583 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2584 szServer, /* HTTP_QUERY_SERVER = 37 */
2585 NULL, /* HTTP_TITLE = 38 */
2586 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2587 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2588 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2589 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2590 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2591 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2592 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2593 NULL, /* HTTP_QUERY_REFRESH = 46 */
2594 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2595 szAge, /* HTTP_QUERY_AGE = 48 */
2596 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2597 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2598 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2599 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2600 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2601 szETag, /* HTTP_QUERY_ETAG = 54 */
2602 hostW, /* HTTP_QUERY_HOST = 55 */
2603 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2604 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2605 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2606 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2607 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2608 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2609 szRange, /* HTTP_QUERY_RANGE = 62 */
2610 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2611 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2612 szVary, /* HTTP_QUERY_VARY = 65 */
2613 szVia, /* HTTP_QUERY_VIA = 66 */
2614 szWarning, /* HTTP_QUERY_WARNING = 67 */
2615 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2616 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2617 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2620 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2622 /***********************************************************************
2623 * HTTP_HttpQueryInfoW (internal)
2625 static BOOL HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2626 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2628 LPHTTPHEADERW lphttpHdr = NULL;
2629 BOOL bSuccess = FALSE;
2630 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2631 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2632 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2633 INT index = -1;
2635 /* Find requested header structure */
2636 switch (level)
2638 case HTTP_QUERY_CUSTOM:
2639 if (!lpBuffer) return FALSE;
2640 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2641 break;
2642 case HTTP_QUERY_RAW_HEADERS_CRLF:
2644 LPWSTR headers;
2645 DWORD len = 0;
2646 BOOL ret = FALSE;
2648 if (request_only)
2649 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2650 else
2651 headers = lpwhr->lpszRawHeaders;
2653 if (headers)
2654 len = strlenW(headers) * sizeof(WCHAR);
2656 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2658 len += sizeof(WCHAR);
2659 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2660 ret = FALSE;
2662 else if (lpBuffer)
2664 if (headers)
2665 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2666 else
2668 len = strlenW(szCrLf) * sizeof(WCHAR);
2669 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2671 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2672 ret = TRUE;
2674 *lpdwBufferLength = len;
2676 if (request_only)
2677 HeapFree(GetProcessHeap(), 0, headers);
2678 return ret;
2680 case HTTP_QUERY_RAW_HEADERS:
2682 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2683 DWORD i, size = 0;
2684 LPWSTR pszString = lpBuffer;
2686 for (i = 0; ppszRawHeaderLines[i]; i++)
2687 size += strlenW(ppszRawHeaderLines[i]) + 1;
2689 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2691 HTTP_FreeTokens(ppszRawHeaderLines);
2692 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2693 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2694 return FALSE;
2696 if (pszString)
2698 for (i = 0; ppszRawHeaderLines[i]; i++)
2700 DWORD len = strlenW(ppszRawHeaderLines[i]);
2701 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2702 pszString += len+1;
2704 *pszString = '\0';
2705 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2707 *lpdwBufferLength = size * sizeof(WCHAR);
2708 HTTP_FreeTokens(ppszRawHeaderLines);
2710 return TRUE;
2712 case HTTP_QUERY_STATUS_TEXT:
2713 if (lpwhr->lpszStatusText)
2715 DWORD len = strlenW(lpwhr->lpszStatusText);
2716 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2718 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2719 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2720 return FALSE;
2722 if (lpBuffer)
2724 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2725 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2727 *lpdwBufferLength = len * sizeof(WCHAR);
2728 return TRUE;
2730 break;
2731 case HTTP_QUERY_VERSION:
2732 if (lpwhr->lpszVersion)
2734 DWORD len = strlenW(lpwhr->lpszVersion);
2735 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2737 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2738 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2739 return FALSE;
2741 if (lpBuffer)
2743 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2744 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2746 *lpdwBufferLength = len * sizeof(WCHAR);
2747 return TRUE;
2749 break;
2750 case HTTP_QUERY_CONTENT_ENCODING:
2751 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2752 requested_index,request_only);
2753 break;
2754 default:
2755 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2757 if (level < LAST_TABLE_HEADER && header_lookup[level])
2758 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2759 requested_index,request_only);
2762 if (index >= 0)
2763 lphttpHdr = &lpwhr->pCustHeaders[index];
2765 /* Ensure header satisfies requested attributes */
2766 if (!lphttpHdr ||
2767 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2768 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2770 INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
2771 return bSuccess;
2774 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2776 /* coalesce value to requested type */
2777 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2779 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2780 TRACE(" returning number: %d\n", *(int *)lpBuffer);
2781 bSuccess = TRUE;
2783 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2785 time_t tmpTime;
2786 struct tm tmpTM;
2787 SYSTEMTIME *STHook;
2789 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2791 tmpTM = *gmtime(&tmpTime);
2792 STHook = (SYSTEMTIME *)lpBuffer;
2793 STHook->wDay = tmpTM.tm_mday;
2794 STHook->wHour = tmpTM.tm_hour;
2795 STHook->wMilliseconds = 0;
2796 STHook->wMinute = tmpTM.tm_min;
2797 STHook->wDayOfWeek = tmpTM.tm_wday;
2798 STHook->wMonth = tmpTM.tm_mon + 1;
2799 STHook->wSecond = tmpTM.tm_sec;
2800 STHook->wYear = tmpTM.tm_year;
2801 bSuccess = TRUE;
2803 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
2804 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
2805 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
2807 else if (lphttpHdr->lpszValue)
2809 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
2811 if (len > *lpdwBufferLength)
2813 *lpdwBufferLength = len;
2814 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2815 return bSuccess;
2817 if (lpBuffer)
2819 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
2820 TRACE(" returning string: %s\n", debugstr_w(lpBuffer));
2822 *lpdwBufferLength = len - sizeof(WCHAR);
2823 bSuccess = TRUE;
2825 return bSuccess;
2828 /***********************************************************************
2829 * HttpQueryInfoW (WININET.@)
2831 * Queries for information about an HTTP request
2833 * RETURNS
2834 * TRUE on success
2835 * FALSE on failure
2838 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
2839 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2841 BOOL bSuccess = FALSE;
2842 http_request_t *lpwhr;
2844 if (TRACE_ON(wininet)) {
2845 #define FE(x) { x, #x }
2846 static const wininet_flag_info query_flags[] = {
2847 FE(HTTP_QUERY_MIME_VERSION),
2848 FE(HTTP_QUERY_CONTENT_TYPE),
2849 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
2850 FE(HTTP_QUERY_CONTENT_ID),
2851 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
2852 FE(HTTP_QUERY_CONTENT_LENGTH),
2853 FE(HTTP_QUERY_CONTENT_LANGUAGE),
2854 FE(HTTP_QUERY_ALLOW),
2855 FE(HTTP_QUERY_PUBLIC),
2856 FE(HTTP_QUERY_DATE),
2857 FE(HTTP_QUERY_EXPIRES),
2858 FE(HTTP_QUERY_LAST_MODIFIED),
2859 FE(HTTP_QUERY_MESSAGE_ID),
2860 FE(HTTP_QUERY_URI),
2861 FE(HTTP_QUERY_DERIVED_FROM),
2862 FE(HTTP_QUERY_COST),
2863 FE(HTTP_QUERY_LINK),
2864 FE(HTTP_QUERY_PRAGMA),
2865 FE(HTTP_QUERY_VERSION),
2866 FE(HTTP_QUERY_STATUS_CODE),
2867 FE(HTTP_QUERY_STATUS_TEXT),
2868 FE(HTTP_QUERY_RAW_HEADERS),
2869 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
2870 FE(HTTP_QUERY_CONNECTION),
2871 FE(HTTP_QUERY_ACCEPT),
2872 FE(HTTP_QUERY_ACCEPT_CHARSET),
2873 FE(HTTP_QUERY_ACCEPT_ENCODING),
2874 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
2875 FE(HTTP_QUERY_AUTHORIZATION),
2876 FE(HTTP_QUERY_CONTENT_ENCODING),
2877 FE(HTTP_QUERY_FORWARDED),
2878 FE(HTTP_QUERY_FROM),
2879 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
2880 FE(HTTP_QUERY_LOCATION),
2881 FE(HTTP_QUERY_ORIG_URI),
2882 FE(HTTP_QUERY_REFERER),
2883 FE(HTTP_QUERY_RETRY_AFTER),
2884 FE(HTTP_QUERY_SERVER),
2885 FE(HTTP_QUERY_TITLE),
2886 FE(HTTP_QUERY_USER_AGENT),
2887 FE(HTTP_QUERY_WWW_AUTHENTICATE),
2888 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
2889 FE(HTTP_QUERY_ACCEPT_RANGES),
2890 FE(HTTP_QUERY_SET_COOKIE),
2891 FE(HTTP_QUERY_COOKIE),
2892 FE(HTTP_QUERY_REQUEST_METHOD),
2893 FE(HTTP_QUERY_REFRESH),
2894 FE(HTTP_QUERY_CONTENT_DISPOSITION),
2895 FE(HTTP_QUERY_AGE),
2896 FE(HTTP_QUERY_CACHE_CONTROL),
2897 FE(HTTP_QUERY_CONTENT_BASE),
2898 FE(HTTP_QUERY_CONTENT_LOCATION),
2899 FE(HTTP_QUERY_CONTENT_MD5),
2900 FE(HTTP_QUERY_CONTENT_RANGE),
2901 FE(HTTP_QUERY_ETAG),
2902 FE(HTTP_QUERY_HOST),
2903 FE(HTTP_QUERY_IF_MATCH),
2904 FE(HTTP_QUERY_IF_NONE_MATCH),
2905 FE(HTTP_QUERY_IF_RANGE),
2906 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
2907 FE(HTTP_QUERY_MAX_FORWARDS),
2908 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
2909 FE(HTTP_QUERY_RANGE),
2910 FE(HTTP_QUERY_TRANSFER_ENCODING),
2911 FE(HTTP_QUERY_UPGRADE),
2912 FE(HTTP_QUERY_VARY),
2913 FE(HTTP_QUERY_VIA),
2914 FE(HTTP_QUERY_WARNING),
2915 FE(HTTP_QUERY_CUSTOM)
2917 static const wininet_flag_info modifier_flags[] = {
2918 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
2919 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
2920 FE(HTTP_QUERY_FLAG_NUMBER),
2921 FE(HTTP_QUERY_FLAG_COALESCE)
2923 #undef FE
2924 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
2925 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
2926 DWORD i;
2928 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, dwInfoLevel);
2929 TRACE(" Attribute:");
2930 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
2931 if (query_flags[i].val == info) {
2932 TRACE(" %s", query_flags[i].name);
2933 break;
2936 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
2937 TRACE(" Unknown (%08x)", info);
2940 TRACE(" Modifier:");
2941 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
2942 if (modifier_flags[i].val & info_mod) {
2943 TRACE(" %s", modifier_flags[i].name);
2944 info_mod &= ~ modifier_flags[i].val;
2948 if (info_mod) {
2949 TRACE(" Unknown (%08x)", info_mod);
2951 TRACE("\n");
2954 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
2955 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
2957 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2958 goto lend;
2961 if (lpBuffer == NULL)
2962 *lpdwBufferLength = 0;
2963 bSuccess = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
2964 lpBuffer, lpdwBufferLength, lpdwIndex);
2966 lend:
2967 if( lpwhr )
2968 WININET_Release( &lpwhr->hdr );
2970 TRACE("%d <--\n", bSuccess);
2971 return bSuccess;
2974 /***********************************************************************
2975 * HttpQueryInfoA (WININET.@)
2977 * Queries for information about an HTTP request
2979 * RETURNS
2980 * TRUE on success
2981 * FALSE on failure
2984 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
2985 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2987 BOOL result;
2988 DWORD len;
2989 WCHAR* bufferW;
2991 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
2992 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
2994 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
2995 lpdwBufferLength, lpdwIndex );
2998 if (lpBuffer)
3000 DWORD alloclen;
3001 len = (*lpdwBufferLength)*sizeof(WCHAR);
3002 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3004 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3005 if (alloclen < len)
3006 alloclen = len;
3008 else
3009 alloclen = len;
3010 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3011 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3012 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3013 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3014 } else
3016 bufferW = NULL;
3017 len = 0;
3020 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3021 &len, lpdwIndex );
3022 if( result )
3024 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3025 lpBuffer, *lpdwBufferLength, NULL, NULL );
3026 *lpdwBufferLength = len - 1;
3028 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3030 else
3031 /* since the strings being returned from HttpQueryInfoW should be
3032 * only ASCII characters, it is reasonable to assume that all of
3033 * the Unicode characters can be reduced to a single byte */
3034 *lpdwBufferLength = len / sizeof(WCHAR);
3036 HeapFree(GetProcessHeap(), 0, bufferW );
3038 return result;
3041 /***********************************************************************
3042 * HttpSendRequestExA (WININET.@)
3044 * Sends the specified request to the HTTP server and allows chunked
3045 * transfers.
3047 * RETURNS
3048 * Success: TRUE
3049 * Failure: FALSE, call GetLastError() for more information.
3051 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3052 LPINTERNET_BUFFERSA lpBuffersIn,
3053 LPINTERNET_BUFFERSA lpBuffersOut,
3054 DWORD dwFlags, DWORD_PTR dwContext)
3056 INTERNET_BUFFERSW BuffersInW;
3057 BOOL rc = FALSE;
3058 DWORD headerlen;
3059 LPWSTR header = NULL;
3061 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3062 lpBuffersOut, dwFlags, dwContext);
3064 if (lpBuffersIn)
3066 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3067 if (lpBuffersIn->lpcszHeader)
3069 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3070 lpBuffersIn->dwHeadersLength,0,0);
3071 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3072 if (!(BuffersInW.lpcszHeader = header))
3074 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3075 return FALSE;
3077 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3078 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3079 header, headerlen);
3081 else
3082 BuffersInW.lpcszHeader = NULL;
3083 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3084 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3085 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3086 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3087 BuffersInW.Next = NULL;
3090 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3092 HeapFree(GetProcessHeap(),0,header);
3094 return rc;
3097 /***********************************************************************
3098 * HttpSendRequestExW (WININET.@)
3100 * Sends the specified request to the HTTP server and allows chunked
3101 * transfers
3103 * RETURNS
3104 * Success: TRUE
3105 * Failure: FALSE, call GetLastError() for more information.
3107 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3108 LPINTERNET_BUFFERSW lpBuffersIn,
3109 LPINTERNET_BUFFERSW lpBuffersOut,
3110 DWORD dwFlags, DWORD_PTR dwContext)
3112 BOOL ret = FALSE;
3113 http_request_t *lpwhr;
3114 http_session_t *lpwhs;
3115 appinfo_t *hIC;
3117 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3118 lpBuffersOut, dwFlags, dwContext);
3120 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3122 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3124 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3125 goto lend;
3128 lpwhs = lpwhr->lpHttpSession;
3129 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3130 hIC = lpwhs->lpAppInfo;
3131 assert(hIC->hdr.htype == WH_HINIT);
3133 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3135 WORKREQUEST workRequest;
3136 struct WORKREQ_HTTPSENDREQUESTW *req;
3138 workRequest.asyncproc = AsyncHttpSendRequestProc;
3139 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3140 req = &workRequest.u.HttpSendRequestW;
3141 if (lpBuffersIn)
3143 /* FIXME: this should use dwHeadersLength or may not be necessary at all */
3144 req->lpszHeader = heap_strdupW(lpBuffersIn->lpcszHeader);
3145 req->dwHeaderLength = lpBuffersIn->dwHeadersLength;
3146 req->lpOptional = lpBuffersIn->lpvBuffer;
3147 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3148 req->dwContentLength = lpBuffersIn->dwBufferTotal;
3150 else
3152 req->lpszHeader = NULL;
3153 req->dwHeaderLength = 0;
3154 req->lpOptional = NULL;
3155 req->dwOptionalLength = 0;
3156 req->dwContentLength = 0;
3159 req->bEndRequest = FALSE;
3161 INTERNET_AsyncCall(&workRequest);
3163 * This is from windows.
3165 INTERNET_SetLastError(ERROR_IO_PENDING);
3167 else
3169 if (lpBuffersIn)
3170 ret = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3171 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
3172 lpBuffersIn->dwBufferTotal, FALSE);
3173 else
3174 ret = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
3177 lend:
3178 if ( lpwhr )
3179 WININET_Release( &lpwhr->hdr );
3181 TRACE("<---\n");
3182 return ret;
3185 /***********************************************************************
3186 * HttpSendRequestW (WININET.@)
3188 * Sends the specified request to the HTTP server
3190 * RETURNS
3191 * TRUE on success
3192 * FALSE on failure
3195 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
3196 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3198 http_request_t *lpwhr;
3199 http_session_t *lpwhs = NULL;
3200 appinfo_t *hIC = NULL;
3201 BOOL r;
3203 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
3204 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
3206 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3207 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3209 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3210 r = FALSE;
3211 goto lend;
3214 lpwhs = lpwhr->lpHttpSession;
3215 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
3217 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3218 r = FALSE;
3219 goto lend;
3222 hIC = lpwhs->lpAppInfo;
3223 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
3225 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3226 r = FALSE;
3227 goto lend;
3230 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3232 WORKREQUEST workRequest;
3233 struct WORKREQ_HTTPSENDREQUESTW *req;
3235 workRequest.asyncproc = AsyncHttpSendRequestProc;
3236 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3237 req = &workRequest.u.HttpSendRequestW;
3238 if (lpszHeaders)
3240 DWORD size;
3242 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
3243 else size = dwHeaderLength * sizeof(WCHAR);
3245 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
3246 memcpy(req->lpszHeader, lpszHeaders, size);
3248 else
3249 req->lpszHeader = 0;
3250 req->dwHeaderLength = dwHeaderLength;
3251 req->lpOptional = lpOptional;
3252 req->dwOptionalLength = dwOptionalLength;
3253 req->dwContentLength = dwOptionalLength;
3254 req->bEndRequest = TRUE;
3256 INTERNET_AsyncCall(&workRequest);
3258 * This is from windows.
3260 INTERNET_SetLastError(ERROR_IO_PENDING);
3261 r = FALSE;
3263 else
3265 r = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
3266 dwHeaderLength, lpOptional, dwOptionalLength,
3267 dwOptionalLength, TRUE);
3269 lend:
3270 if( lpwhr )
3271 WININET_Release( &lpwhr->hdr );
3272 return r;
3275 /***********************************************************************
3276 * HttpSendRequestA (WININET.@)
3278 * Sends the specified request to the HTTP server
3280 * RETURNS
3281 * TRUE on success
3282 * FALSE on failure
3285 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
3286 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3288 BOOL result;
3289 LPWSTR szHeaders=NULL;
3290 DWORD nLen=dwHeaderLength;
3291 if(lpszHeaders!=NULL)
3293 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
3294 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
3295 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
3297 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
3298 HeapFree(GetProcessHeap(),0,szHeaders);
3299 return result;
3302 /***********************************************************************
3303 * HTTP_GetRedirectURL (internal)
3305 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3307 static WCHAR szHttp[] = {'h','t','t','p',0};
3308 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3309 http_session_t *lpwhs = lpwhr->lpHttpSession;
3310 URL_COMPONENTSW urlComponents;
3311 DWORD url_length = 0;
3312 LPWSTR orig_url;
3313 LPWSTR combined_url;
3315 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3316 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3317 urlComponents.dwSchemeLength = 0;
3318 urlComponents.lpszHostName = lpwhs->lpszHostName;
3319 urlComponents.dwHostNameLength = 0;
3320 urlComponents.nPort = lpwhs->nHostPort;
3321 urlComponents.lpszUserName = lpwhs->lpszUserName;
3322 urlComponents.dwUserNameLength = 0;
3323 urlComponents.lpszPassword = NULL;
3324 urlComponents.dwPasswordLength = 0;
3325 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3326 urlComponents.dwUrlPathLength = 0;
3327 urlComponents.lpszExtraInfo = NULL;
3328 urlComponents.dwExtraInfoLength = 0;
3330 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3331 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3332 return NULL;
3334 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3336 /* convert from bytes to characters */
3337 url_length = url_length / sizeof(WCHAR) - 1;
3338 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3340 HeapFree(GetProcessHeap(), 0, orig_url);
3341 return NULL;
3344 url_length = 0;
3345 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3346 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3348 HeapFree(GetProcessHeap(), 0, orig_url);
3349 return NULL;
3351 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3353 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3355 HeapFree(GetProcessHeap(), 0, orig_url);
3356 HeapFree(GetProcessHeap(), 0, combined_url);
3357 return NULL;
3359 HeapFree(GetProcessHeap(), 0, orig_url);
3360 return combined_url;
3364 /***********************************************************************
3365 * HTTP_HandleRedirect (internal)
3367 static BOOL HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3369 http_session_t *lpwhs = lpwhr->lpHttpSession;
3370 appinfo_t *hIC = lpwhs->lpAppInfo;
3371 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3372 WCHAR path[INTERNET_MAX_URL_LENGTH];
3373 int index;
3375 if(lpszUrl[0]=='/')
3377 /* if it's an absolute path, keep the same session info */
3378 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3380 else
3382 URL_COMPONENTSW urlComponents;
3383 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3384 static WCHAR szHttp[] = {'h','t','t','p',0};
3385 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3387 userName[0] = 0;
3388 hostName[0] = 0;
3389 protocol[0] = 0;
3391 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3392 urlComponents.lpszScheme = protocol;
3393 urlComponents.dwSchemeLength = 32;
3394 urlComponents.lpszHostName = hostName;
3395 urlComponents.dwHostNameLength = MAXHOSTNAME;
3396 urlComponents.lpszUserName = userName;
3397 urlComponents.dwUserNameLength = 1024;
3398 urlComponents.lpszPassword = NULL;
3399 urlComponents.dwPasswordLength = 0;
3400 urlComponents.lpszUrlPath = path;
3401 urlComponents.dwUrlPathLength = 2048;
3402 urlComponents.lpszExtraInfo = NULL;
3403 urlComponents.dwExtraInfoLength = 0;
3404 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3405 return FALSE;
3407 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3408 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3410 TRACE("redirect from secure page to non-secure page\n");
3411 /* FIXME: warn about from secure redirect to non-secure page */
3412 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3414 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3415 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3417 TRACE("redirect from non-secure page to secure page\n");
3418 /* FIXME: notify about redirect to secure page */
3419 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3422 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3424 if (lstrlenW(protocol)>4) /*https*/
3425 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3426 else /*http*/
3427 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3430 #if 0
3432 * This upsets redirects to binary files on sourceforge.net
3433 * and gives an html page instead of the target file
3434 * Examination of the HTTP request sent by native wininet.dll
3435 * reveals that it doesn't send a referrer in that case.
3436 * Maybe there's a flag that enables this, or maybe a referrer
3437 * shouldn't be added in case of a redirect.
3440 /* consider the current host as the referrer */
3441 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3442 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3443 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3444 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3445 #endif
3447 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3448 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3449 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3451 int len;
3452 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3453 len = lstrlenW(hostName);
3454 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3455 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3456 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3458 else
3459 lpwhs->lpszHostName = heap_strdupW(hostName);
3461 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3463 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3464 lpwhs->lpszUserName = NULL;
3465 if (userName[0])
3466 lpwhs->lpszUserName = heap_strdupW(userName);
3468 if (!using_proxy)
3470 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3472 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3473 lpwhs->lpszServerName = heap_strdupW(hostName);
3474 lpwhs->nServerPort = urlComponents.nPort;
3476 NETCON_close(&lpwhr->netConnection);
3477 if (!HTTP_ResolveName(lpwhr)) return FALSE;
3478 if (!NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)) return FALSE;
3479 lpwhr->read_pos = lpwhr->read_size = 0;
3480 lpwhr->read_chunked = FALSE;
3483 else
3484 TRACE("Redirect through proxy\n");
3487 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3488 lpwhr->lpszPath=NULL;
3489 if (*path)
3491 DWORD needed = 0;
3492 HRESULT rc;
3494 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3495 if (rc != E_POINTER)
3496 needed = strlenW(path)+1;
3497 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3498 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3499 URL_ESCAPE_SPACES_ONLY);
3500 if (rc != S_OK)
3502 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3503 strcpyW(lpwhr->lpszPath,path);
3507 /* Remove custom content-type/length headers on redirects. */
3508 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3509 if (0 <= index)
3510 HTTP_DeleteCustomHeader(lpwhr, index);
3511 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3512 if (0 <= index)
3513 HTTP_DeleteCustomHeader(lpwhr, index);
3515 return TRUE;
3518 /***********************************************************************
3519 * HTTP_build_req (internal)
3521 * concatenate all the strings in the request together
3523 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3525 LPCWSTR *t;
3526 LPWSTR str;
3528 for( t = list; *t ; t++ )
3529 len += strlenW( *t );
3530 len++;
3532 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3533 *str = 0;
3535 for( t = list; *t ; t++ )
3536 strcatW( str, *t );
3538 return str;
3541 static BOOL HTTP_SecureProxyConnect(http_request_t *lpwhr)
3543 LPWSTR lpszPath;
3544 LPWSTR requestString;
3545 INT len;
3546 INT cnt;
3547 INT responseLen;
3548 char *ascii_req;
3549 BOOL ret;
3550 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3551 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3552 http_session_t *lpwhs = lpwhr->lpHttpSession;
3554 TRACE("\n");
3556 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3557 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3558 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3559 HeapFree( GetProcessHeap(), 0, lpszPath );
3561 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3562 NULL, 0, NULL, NULL );
3563 len--; /* the nul terminator isn't needed */
3564 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3565 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3566 ascii_req, len, NULL, NULL );
3567 HeapFree( GetProcessHeap(), 0, requestString );
3569 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3571 ret = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3572 HeapFree( GetProcessHeap(), 0, ascii_req );
3573 if (!ret || cnt < 0)
3574 return FALSE;
3576 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3577 if (!responseLen)
3578 return FALSE;
3580 return TRUE;
3583 static void HTTP_InsertCookies(http_request_t *lpwhr)
3585 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3586 LPWSTR lpszCookies, lpszUrl = NULL;
3587 DWORD nCookieSize, size;
3588 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3590 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3591 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3592 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3594 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3596 int cnt = 0;
3597 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3599 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3600 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3602 cnt += sprintfW(lpszCookies, szCookie);
3603 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3604 strcatW(lpszCookies, szCrLf);
3606 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3607 HeapFree(GetProcessHeap(), 0, lpszCookies);
3610 HeapFree(GetProcessHeap(), 0, lpszUrl);
3613 /***********************************************************************
3614 * HTTP_HttpSendRequestW (internal)
3616 * Sends the specified request to the HTTP server
3618 * RETURNS
3619 * TRUE on success
3620 * FALSE on failure
3623 BOOL WINAPI HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3624 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3625 DWORD dwContentLength, BOOL bEndRequest)
3627 INT cnt;
3628 BOOL bSuccess = FALSE, redirected = FALSE;
3629 LPWSTR requestString = NULL;
3630 INT responseLen;
3631 BOOL loop_next;
3632 INTERNET_ASYNC_RESULT iar;
3633 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3634 static const WCHAR szContentLength[] =
3635 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3636 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3638 TRACE("--> %p\n", lpwhr);
3640 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3642 /* if the verb is NULL default to GET */
3643 if (!lpwhr->lpszVerb)
3644 lpwhr->lpszVerb = heap_strdupW(szGET);
3646 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3648 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3649 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3650 lpwhr->dwBytesToWrite = dwContentLength;
3652 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3654 WCHAR *agent_header;
3655 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3656 int len;
3658 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3659 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3660 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3662 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3663 HeapFree(GetProcessHeap(), 0, agent_header);
3665 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3667 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3668 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3670 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3672 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3673 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3674 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3679 DWORD len;
3680 char *ascii_req;
3682 loop_next = FALSE;
3684 /* like native, just in case the caller forgot to call InternetReadFile
3685 * for all the data */
3686 HTTP_DrainContent(lpwhr);
3687 lpwhr->dwContentRead = 0;
3689 if (TRACE_ON(wininet))
3691 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3692 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3695 HTTP_FixURL(lpwhr);
3696 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3698 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3700 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3701 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3703 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3704 HTTP_InsertCookies(lpwhr);
3706 /* add the headers the caller supplied */
3707 if( lpszHeaders && dwHeaderLength )
3709 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3710 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3713 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3715 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3716 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3717 HeapFree(GetProcessHeap(), 0, url);
3719 else
3720 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3723 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3725 /* Send the request and store the results */
3726 if (!HTTP_OpenConnection(lpwhr))
3727 goto lend;
3729 /* send the request as ASCII, tack on the optional data */
3730 if (!lpOptional || redirected)
3731 dwOptionalLength = 0;
3732 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3733 NULL, 0, NULL, NULL );
3734 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3735 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3736 ascii_req, len, NULL, NULL );
3737 if( lpOptional )
3738 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3739 len = (len + dwOptionalLength - 1);
3740 ascii_req[len] = 0;
3741 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3743 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3744 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3746 NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3747 HeapFree( GetProcessHeap(), 0, ascii_req );
3749 lpwhr->dwBytesWritten = dwOptionalLength;
3751 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3752 INTERNET_STATUS_REQUEST_SENT,
3753 &len, sizeof(DWORD));
3755 if (bEndRequest)
3757 DWORD dwBufferSize;
3758 DWORD dwStatusCode;
3760 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3761 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3763 if (cnt < 0)
3764 goto lend;
3766 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3767 if (responseLen)
3768 bSuccess = TRUE;
3770 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3771 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3772 sizeof(DWORD));
3774 HTTP_ProcessCookies(lpwhr);
3776 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3778 dwBufferSize = sizeof(dwStatusCode);
3779 if (!HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3780 &dwStatusCode,&dwBufferSize,NULL))
3781 dwStatusCode = 0;
3783 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && bSuccess)
3785 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3786 dwBufferSize=sizeof(szNewLocation);
3787 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
3788 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL))
3790 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3792 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3793 lpwhr->lpszVerb = heap_strdupW(szGET);
3795 HTTP_DrainContent(lpwhr);
3796 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3798 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3799 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3800 bSuccess = HTTP_HandleRedirect(lpwhr, new_url);
3801 if (bSuccess)
3803 HeapFree(GetProcessHeap(), 0, requestString);
3804 loop_next = TRUE;
3806 HeapFree( GetProcessHeap(), 0, new_url );
3808 redirected = TRUE;
3811 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && bSuccess)
3813 WCHAR szAuthValue[2048];
3814 dwBufferSize=2048;
3815 if (dwStatusCode == HTTP_STATUS_DENIED)
3817 DWORD dwIndex = 0;
3818 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
3820 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3821 &lpwhr->pAuthInfo,
3822 lpwhr->lpHttpSession->lpszUserName,
3823 lpwhr->lpHttpSession->lpszPassword))
3825 loop_next = TRUE;
3826 break;
3830 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3832 DWORD dwIndex = 0;
3833 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
3835 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3836 &lpwhr->pProxyAuthInfo,
3837 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3838 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword))
3840 loop_next = TRUE;
3841 break;
3847 else
3848 bSuccess = TRUE;
3850 while (loop_next);
3852 if(bSuccess) {
3853 WCHAR url[INTERNET_MAX_URL_LENGTH];
3854 WCHAR cacheFileName[MAX_PATH+1];
3855 BOOL b;
3857 b = HTTP_GetRequestURL(lpwhr, url);
3858 if(!b) {
3859 WARN("Could not get URL\n");
3860 goto lend;
3863 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3864 if(b) {
3865 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3866 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3867 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3868 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3869 WARN("Could not create file: %u\n", GetLastError());
3870 lpwhr->hCacheFile = NULL;
3872 }else {
3873 WARN("Could not create cache entry: %08x\n", GetLastError());
3877 lend:
3879 HeapFree(GetProcessHeap(), 0, requestString);
3881 /* TODO: send notification for P3P header */
3883 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3885 if (bSuccess)
3887 if (lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite) HTTP_ReceiveRequestData(lpwhr, TRUE);
3888 else
3890 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3891 iar.dwError = 0;
3893 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3894 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3895 sizeof(INTERNET_ASYNC_RESULT));
3898 else
3900 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3901 iar.dwError = INTERNET_GetLastError();
3903 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3904 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3905 sizeof(INTERNET_ASYNC_RESULT));
3909 TRACE("<--\n");
3910 if (bSuccess) INTERNET_SetLastError(ERROR_SUCCESS);
3911 return bSuccess;
3914 /***********************************************************************
3915 * HTTPSESSION_Destroy (internal)
3917 * Deallocate session handle
3920 static void HTTPSESSION_Destroy(object_header_t *hdr)
3922 http_session_t *lpwhs = (http_session_t*) hdr;
3924 TRACE("%p\n", lpwhs);
3926 WININET_Release(&lpwhs->lpAppInfo->hdr);
3928 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3929 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3930 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
3931 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3932 HeapFree(GetProcessHeap(), 0, lpwhs);
3935 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3937 switch(option) {
3938 case INTERNET_OPTION_HANDLE_TYPE:
3939 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3941 if (*size < sizeof(ULONG))
3942 return ERROR_INSUFFICIENT_BUFFER;
3944 *size = sizeof(DWORD);
3945 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
3946 return ERROR_SUCCESS;
3949 return INET_QueryOption(option, buffer, size, unicode);
3952 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
3954 http_session_t *ses = (http_session_t*)hdr;
3956 switch(option) {
3957 case INTERNET_OPTION_USERNAME:
3959 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
3960 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
3961 return ERROR_SUCCESS;
3963 case INTERNET_OPTION_PASSWORD:
3965 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
3966 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
3967 return ERROR_SUCCESS;
3969 default: break;
3972 return ERROR_INTERNET_INVALID_OPTION;
3975 static const object_vtbl_t HTTPSESSIONVtbl = {
3976 HTTPSESSION_Destroy,
3977 NULL,
3978 HTTPSESSION_QueryOption,
3979 HTTPSESSION_SetOption,
3980 NULL,
3981 NULL,
3982 NULL,
3983 NULL,
3984 NULL
3988 /***********************************************************************
3989 * HTTP_Connect (internal)
3991 * Create http session handle
3993 * RETURNS
3994 * HINTERNET a session handle on success
3995 * NULL on failure
3998 HINTERNET HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
3999 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4000 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4001 DWORD dwInternalFlags)
4003 http_session_t *lpwhs = NULL;
4004 HINTERNET handle = NULL;
4006 TRACE("-->\n");
4008 if (!lpszServerName || !lpszServerName[0])
4010 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4011 goto lerror;
4014 assert( hIC->hdr.htype == WH_HINIT );
4016 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4017 if (NULL == lpwhs)
4019 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4020 goto lerror;
4024 * According to my tests. The name is not resolved until a request is sent
4027 lpwhs->hdr.htype = WH_HHTTPSESSION;
4028 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4029 lpwhs->hdr.dwFlags = dwFlags;
4030 lpwhs->hdr.dwContext = dwContext;
4031 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4032 lpwhs->hdr.refs = 1;
4033 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4035 WININET_AddRef( &hIC->hdr );
4036 lpwhs->lpAppInfo = hIC;
4037 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4039 handle = WININET_AllocHandle( &lpwhs->hdr );
4040 if (NULL == handle)
4042 ERR("Failed to alloc handle\n");
4043 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4044 goto lerror;
4047 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4048 if(strchrW(hIC->lpszProxy, ' '))
4049 FIXME("Several proxies not implemented.\n");
4050 if(hIC->lpszProxyBypass)
4051 FIXME("Proxy bypass is ignored.\n");
4053 if (lpszServerName && lpszServerName[0])
4055 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4056 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4058 if (lpszUserName && lpszUserName[0])
4059 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4060 if (lpszPassword && lpszPassword[0])
4061 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4062 lpwhs->nServerPort = nServerPort;
4063 lpwhs->nHostPort = nServerPort;
4065 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4066 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4068 INTERNET_SendCallback(&hIC->hdr, dwContext,
4069 INTERNET_STATUS_HANDLE_CREATED, &handle,
4070 sizeof(handle));
4073 lerror:
4074 if( lpwhs )
4075 WININET_Release( &lpwhs->hdr );
4078 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4079 * windows
4082 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4083 return handle;
4087 /***********************************************************************
4088 * HTTP_OpenConnection (internal)
4090 * Connect to a web server
4092 * RETURNS
4094 * TRUE on success
4095 * FALSE on failure
4097 static BOOL HTTP_OpenConnection(http_request_t *lpwhr)
4099 BOOL bSuccess = FALSE;
4100 http_session_t *lpwhs;
4101 appinfo_t *hIC = NULL;
4102 char szaddr[INET6_ADDRSTRLEN];
4103 const void *addr;
4105 TRACE("-->\n");
4108 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4110 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4111 goto lend;
4114 if (NETCON_connected(&lpwhr->netConnection))
4116 bSuccess = TRUE;
4117 goto lend;
4119 if (!HTTP_ResolveName(lpwhr)) goto lend;
4121 lpwhs = lpwhr->lpHttpSession;
4123 hIC = lpwhs->lpAppInfo;
4124 switch (lpwhs->socketAddress.ss_family)
4126 case AF_INET:
4127 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4128 break;
4129 case AF_INET6:
4130 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4131 break;
4132 default:
4133 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4134 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
4135 return FALSE;
4137 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4138 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4139 INTERNET_STATUS_CONNECTING_TO_SERVER,
4140 szaddr,
4141 strlen(szaddr)+1);
4143 if (!NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family,
4144 SOCK_STREAM, 0))
4146 WARN("Socket creation failed: %u\n", INTERNET_GetLastError());
4147 goto lend;
4150 if (!NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4151 lpwhs->sa_len))
4152 goto lend;
4154 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4156 /* Note: we differ from Microsoft's WinINet here. they seem to have
4157 * a bug that causes no status callbacks to be sent when starting
4158 * a tunnel to a proxy server using the CONNECT verb. i believe our
4159 * behaviour to be more correct and to not cause any incompatibilities
4160 * because using a secure connection through a proxy server is a rare
4161 * case that would be hard for anyone to depend on */
4162 if (hIC->lpszProxy && !HTTP_SecureProxyConnect(lpwhr))
4163 goto lend;
4165 if (!NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName))
4167 WARN("Couldn't connect securely to host\n");
4168 goto lend;
4172 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4173 INTERNET_STATUS_CONNECTED_TO_SERVER,
4174 szaddr, strlen(szaddr)+1);
4176 bSuccess = TRUE;
4178 lend:
4179 lpwhr->read_pos = lpwhr->read_size = 0;
4180 lpwhr->read_chunked = FALSE;
4182 TRACE("%d <--\n", bSuccess);
4183 return bSuccess;
4187 /***********************************************************************
4188 * HTTP_clear_response_headers (internal)
4190 * clear out any old response headers
4192 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4194 DWORD i;
4196 for( i=0; i<lpwhr->nCustHeaders; i++)
4198 if( !lpwhr->pCustHeaders[i].lpszField )
4199 continue;
4200 if( !lpwhr->pCustHeaders[i].lpszValue )
4201 continue;
4202 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4203 continue;
4204 HTTP_DeleteCustomHeader( lpwhr, i );
4205 i--;
4209 /***********************************************************************
4210 * HTTP_GetResponseHeaders (internal)
4212 * Read server response
4214 * RETURNS
4216 * TRUE on success
4217 * FALSE on error
4219 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4221 INT cbreaks = 0;
4222 WCHAR buffer[MAX_REPLY_LEN];
4223 DWORD buflen = MAX_REPLY_LEN;
4224 BOOL bSuccess = FALSE;
4225 INT rc = 0;
4226 char bufferA[MAX_REPLY_LEN];
4227 LPWSTR status_code = NULL, status_text = NULL;
4228 DWORD cchMaxRawHeaders = 1024;
4229 LPWSTR lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4230 LPWSTR temp;
4231 DWORD cchRawHeaders = 0;
4232 BOOL codeHundred = FALSE;
4234 TRACE("-->\n");
4236 /* clear old response headers (eg. from a redirect response) */
4237 if (clear) HTTP_clear_response_headers( lpwhr );
4239 if (!NETCON_connected(&lpwhr->netConnection))
4240 goto lend;
4242 do {
4243 static const WCHAR szHundred[] = {'1','0','0',0};
4245 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4247 buflen = MAX_REPLY_LEN;
4248 if (!read_line(lpwhr, bufferA, &buflen))
4249 goto lend;
4250 rc += buflen;
4251 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4252 /* check is this a status code line? */
4253 if (!strncmpW(buffer, g_szHttp1_0, 4))
4255 /* split the version from the status code */
4256 status_code = strchrW( buffer, ' ' );
4257 if( !status_code )
4258 goto lend;
4259 *status_code++=0;
4261 /* split the status code from the status text */
4262 status_text = strchrW( status_code, ' ' );
4263 if( !status_text )
4264 goto lend;
4265 *status_text++=0;
4267 TRACE("version [%s] status code [%s] status text [%s]\n",
4268 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4270 codeHundred = (!strcmpW(status_code, szHundred));
4272 else if (!codeHundred)
4274 FIXME("Non status line at head of response (%s)\n",debugstr_w(buffer));
4275 continue;
4277 } while (codeHundred);
4279 /* Add status code */
4280 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4281 HTTP_ADDHDR_FLAG_REPLACE);
4283 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4284 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4286 lpwhr->lpszVersion = heap_strdupW(buffer);
4287 lpwhr->lpszStatusText = heap_strdupW(status_text);
4289 /* Restore the spaces */
4290 *(status_code-1) = ' ';
4291 *(status_text-1) = ' ';
4293 /* regenerate raw headers */
4294 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4295 cchMaxRawHeaders *= 2;
4296 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4297 if (temp == NULL) goto lend;
4298 lpszRawHeaders = temp;
4299 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4300 cchRawHeaders += (buflen-1);
4301 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4302 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4303 lpszRawHeaders[cchRawHeaders] = '\0';
4305 /* Parse each response line */
4308 buflen = MAX_REPLY_LEN;
4309 if (read_line(lpwhr, bufferA, &buflen))
4311 LPWSTR * pFieldAndValue;
4313 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4315 if (!bufferA[0]) break;
4316 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4318 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4319 if (pFieldAndValue)
4321 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4322 cchMaxRawHeaders *= 2;
4323 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4324 if (temp == NULL) goto lend;
4325 lpszRawHeaders = temp;
4326 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4327 cchRawHeaders += (buflen-1);
4328 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4329 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4330 lpszRawHeaders[cchRawHeaders] = '\0';
4332 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4333 HTTP_ADDREQ_FLAG_ADD );
4335 HTTP_FreeTokens(pFieldAndValue);
4338 else
4340 cbreaks++;
4341 if (cbreaks >= 2)
4342 break;
4344 }while(1);
4346 /* make sure the response header is terminated with an empty line. Some apps really
4347 truly care about that empty line being there for some reason. Just add it to the
4348 header. */
4349 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4351 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4352 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4353 if (temp == NULL) goto lend;
4354 lpszRawHeaders = temp;
4357 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4359 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4360 lpwhr->lpszRawHeaders = lpszRawHeaders;
4361 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4362 bSuccess = TRUE;
4364 lend:
4366 TRACE("<--\n");
4367 if (bSuccess)
4368 return rc;
4369 else
4371 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4372 return 0;
4377 static void strip_spaces(LPWSTR start)
4379 LPWSTR str = start;
4380 LPWSTR end;
4382 while (*str == ' ' && *str != '\0')
4383 str++;
4385 if (str != start)
4386 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
4388 end = start + strlenW(start) - 1;
4389 while (end >= start && *end == ' ')
4391 *end = '\0';
4392 end--;
4397 /***********************************************************************
4398 * HTTP_InterpretHttpHeader (internal)
4400 * Parse server response
4402 * RETURNS
4404 * Pointer to array of field, value, NULL on success.
4405 * NULL on error.
4407 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4409 LPWSTR * pTokenPair;
4410 LPWSTR pszColon;
4411 INT len;
4413 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4415 pszColon = strchrW(buffer, ':');
4416 /* must have two tokens */
4417 if (!pszColon)
4419 HTTP_FreeTokens(pTokenPair);
4420 if (buffer[0])
4421 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4422 return NULL;
4425 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4426 if (!pTokenPair[0])
4428 HTTP_FreeTokens(pTokenPair);
4429 return NULL;
4431 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4432 pTokenPair[0][pszColon - buffer] = '\0';
4434 /* skip colon */
4435 pszColon++;
4436 len = strlenW(pszColon);
4437 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4438 if (!pTokenPair[1])
4440 HTTP_FreeTokens(pTokenPair);
4441 return NULL;
4443 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4445 strip_spaces(pTokenPair[0]);
4446 strip_spaces(pTokenPair[1]);
4448 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4449 return pTokenPair;
4452 /***********************************************************************
4453 * HTTP_ProcessHeader (internal)
4455 * Stuff header into header tables according to <dwModifier>
4459 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4461 static BOOL HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4463 LPHTTPHEADERW lphttpHdr = NULL;
4464 BOOL bSuccess = FALSE;
4465 INT index = -1;
4466 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4468 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4470 /* REPLACE wins out over ADD */
4471 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4472 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4474 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4475 index = -1;
4476 else
4477 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4479 if (index >= 0)
4481 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4483 return FALSE;
4485 lphttpHdr = &lpwhr->pCustHeaders[index];
4487 else if (value)
4489 HTTPHEADERW hdr;
4491 hdr.lpszField = (LPWSTR)field;
4492 hdr.lpszValue = (LPWSTR)value;
4493 hdr.wFlags = hdr.wCount = 0;
4495 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4496 hdr.wFlags |= HDR_ISREQUEST;
4498 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4500 /* no value to delete */
4501 else return TRUE;
4503 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4504 lphttpHdr->wFlags |= HDR_ISREQUEST;
4505 else
4506 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4508 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4510 HTTP_DeleteCustomHeader( lpwhr, index );
4512 if (value)
4514 HTTPHEADERW hdr;
4516 hdr.lpszField = (LPWSTR)field;
4517 hdr.lpszValue = (LPWSTR)value;
4518 hdr.wFlags = hdr.wCount = 0;
4520 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4521 hdr.wFlags |= HDR_ISREQUEST;
4523 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4526 return TRUE;
4528 else if (dwModifier & COALESCEFLAGS)
4530 LPWSTR lpsztmp;
4531 WCHAR ch = 0;
4532 INT len = 0;
4533 INT origlen = strlenW(lphttpHdr->lpszValue);
4534 INT valuelen = strlenW(value);
4536 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4538 ch = ',';
4539 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4541 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4543 ch = ';';
4544 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4547 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4549 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4550 if (lpsztmp)
4552 lphttpHdr->lpszValue = lpsztmp;
4553 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4554 if (ch > 0)
4556 lphttpHdr->lpszValue[origlen] = ch;
4557 origlen++;
4558 lphttpHdr->lpszValue[origlen] = ' ';
4559 origlen++;
4562 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4563 lphttpHdr->lpszValue[len] = '\0';
4564 bSuccess = TRUE;
4566 else
4568 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4569 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4572 TRACE("<-- %d\n",bSuccess);
4573 return bSuccess;
4577 /***********************************************************************
4578 * HTTP_FinishedReading (internal)
4580 * Called when all content from server has been read by client.
4583 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4585 WCHAR szVersion[10];
4586 WCHAR szConnectionResponse[20];
4587 DWORD dwBufferSize = sizeof(szVersion);
4588 BOOL keepalive = FALSE;
4590 TRACE("\n");
4592 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
4593 * the connection is keep-alive by default */
4594 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion,
4595 &dwBufferSize, NULL) &&
4596 !strcmpiW(szVersion, g_szHttp1_1))
4598 keepalive = TRUE;
4601 dwBufferSize = sizeof(szConnectionResponse);
4602 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) ||
4603 HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL))
4605 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
4608 if (!keepalive)
4610 HTTPREQ_CloseConnection(&lpwhr->hdr);
4613 /* FIXME: store data in the URL cache here */
4615 return TRUE;
4619 /***********************************************************************
4620 * HTTP_GetCustomHeaderIndex (internal)
4622 * Return index of custom header from header array
4625 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
4626 int requested_index, BOOL request_only)
4628 DWORD index;
4630 TRACE("%s\n", debugstr_w(lpszField));
4632 for (index = 0; index < lpwhr->nCustHeaders; index++)
4634 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
4635 continue;
4637 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4638 continue;
4640 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4641 continue;
4643 if (requested_index == 0)
4644 break;
4645 requested_index --;
4648 if (index >= lpwhr->nCustHeaders)
4649 index = -1;
4651 TRACE("Return: %d\n", index);
4652 return index;
4656 /***********************************************************************
4657 * HTTP_InsertCustomHeader (internal)
4659 * Insert header into array
4662 static BOOL HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
4664 INT count;
4665 LPHTTPHEADERW lph = NULL;
4666 BOOL r = FALSE;
4668 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
4669 count = lpwhr->nCustHeaders + 1;
4670 if (count > 1)
4671 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
4672 else
4673 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
4675 if (NULL != lph)
4677 lpwhr->pCustHeaders = lph;
4678 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
4679 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
4680 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
4681 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
4682 lpwhr->nCustHeaders++;
4683 r = TRUE;
4685 else
4687 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4690 return r;
4694 /***********************************************************************
4695 * HTTP_DeleteCustomHeader (internal)
4697 * Delete header from array
4698 * If this function is called, the indexs may change.
4700 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
4702 if( lpwhr->nCustHeaders <= 0 )
4703 return FALSE;
4704 if( index >= lpwhr->nCustHeaders )
4705 return FALSE;
4706 lpwhr->nCustHeaders--;
4708 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
4709 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
4711 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
4712 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
4713 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
4715 return TRUE;
4719 /***********************************************************************
4720 * HTTP_VerifyValidHeader (internal)
4722 * Verify the given header is not invalid for the given http request
4725 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
4727 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4728 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
4729 return FALSE;
4731 return TRUE;
4734 /***********************************************************************
4735 * IsHostInProxyBypassList (@)
4737 * Undocumented
4740 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
4742 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
4743 return FALSE;