push 8c61147e396035865ef2da2e6aa2d0f3b0907179
[wine/hacks.git] / dlls / wininet / http.c
blobb4f792481280156d1cfc05d3715c7ef3addfe155
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 szCrLf[] = {'\r','\n', 0};
84 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
85 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
86 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
87 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
88 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
89 static const WCHAR szAge[] = { 'A','g','e',0 };
90 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
91 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
92 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
93 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
94 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
95 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
96 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
97 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
98 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
99 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
100 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
101 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 };
102 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
103 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
104 static const WCHAR szDate[] = { 'D','a','t','e',0 };
105 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
106 static const WCHAR szETag[] = { 'E','T','a','g',0 };
107 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
108 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
109 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
110 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
111 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
112 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
113 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
114 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
115 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
116 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
117 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
118 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
119 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
120 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
121 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
122 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
123 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
124 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
125 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
126 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
127 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
128 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 };
129 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
130 static const WCHAR szURI[] = { 'U','R','I',0 };
131 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
132 static const WCHAR szVary[] = { 'V','a','r','y',0 };
133 static const WCHAR szVia[] = { 'V','i','a',0 };
134 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
135 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
137 #define MAXHOSTNAME 100
138 #define MAX_FIELD_VALUE_LEN 256
139 #define MAX_FIELD_LEN 256
141 #define HTTP_REFERER szReferer
142 #define HTTP_ACCEPT szAccept
143 #define HTTP_USERAGENT szUser_Agent
145 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
146 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
147 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
148 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
149 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
150 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
151 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
153 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
155 struct HttpAuthInfo
157 LPWSTR scheme;
158 CredHandle cred;
159 CtxtHandle ctx;
160 TimeStamp exp;
161 ULONG attr;
162 ULONG max_token;
163 void *auth_data;
164 unsigned int auth_data_len;
165 BOOL finished; /* finished authenticating */
169 struct gzip_stream_t {
170 #ifdef HAVE_ZLIB
171 z_stream zstream;
172 #endif
173 BYTE buf[8192];
174 DWORD buf_size;
175 DWORD buf_pos;
176 BOOL end_of_data;
179 static BOOL HTTP_OpenConnection(http_request_t *req);
180 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
181 static BOOL HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
182 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
183 static BOOL HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
184 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
185 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
186 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
187 static BOOL HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
188 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
189 static BOOL HTTP_HandleRedirect(http_request_t *req, LPCWSTR lpszUrl);
190 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
191 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
192 static void HTTP_DrainContent(http_request_t *req);
193 static BOOL HTTP_FinishedReading(http_request_t *req);
195 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
197 int HeaderIndex = 0;
198 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
199 if (HeaderIndex == -1)
200 return NULL;
201 else
202 return &req->pCustHeaders[HeaderIndex];
205 #ifdef HAVE_ZLIB
207 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
209 return HeapAlloc(GetProcessHeap(), 0, items*size);
212 static void wininet_zfree(voidpf opaque, voidpf address)
214 HeapFree(GetProcessHeap(), 0, address);
217 static void init_gzip_stream(http_request_t *req)
219 gzip_stream_t *gzip_stream;
220 int zres;
222 gzip_stream = HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t));
223 gzip_stream->zstream.zalloc = wininet_zalloc;
224 gzip_stream->zstream.zfree = wininet_zfree;
225 gzip_stream->zstream.opaque = NULL;
226 gzip_stream->zstream.next_in = NULL;
227 gzip_stream->zstream.avail_in = 0;
228 gzip_stream->zstream.next_out = NULL;
229 gzip_stream->zstream.avail_out = 0;
230 gzip_stream->buf_pos = 0;
231 gzip_stream->buf_size = 0;
232 gzip_stream->end_of_data = FALSE;
234 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
235 if(zres != Z_OK) {
236 ERR("inflateInit failed: %d\n", zres);
237 HeapFree(GetProcessHeap(), 0, gzip_stream);
238 return;
241 req->gzip_stream = gzip_stream;
244 #else
246 static void init_gzip_stream(http_request_t *req)
248 ERR("gzip stream not supported, missing zlib.\n");
251 #endif
253 /* set the request content length based on the headers */
254 static DWORD set_content_length( http_request_t *lpwhr )
256 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
257 WCHAR encoding[20];
258 DWORD size;
260 size = sizeof(lpwhr->dwContentLength);
261 if (!HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
262 &lpwhr->dwContentLength, &size, NULL))
263 lpwhr->dwContentLength = ~0u;
265 size = sizeof(encoding);
266 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) &&
267 !strcmpiW(encoding, szChunked))
269 lpwhr->dwContentLength = ~0u;
270 lpwhr->read_chunked = TRUE;
273 if(lpwhr->decoding) {
274 int encoding_idx;
276 static const WCHAR gzipW[] = {'g','z','i','p',0};
278 encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
279 if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
280 init_gzip_stream(lpwhr);
283 return lpwhr->dwContentLength;
286 /***********************************************************************
287 * HTTP_Tokenize (internal)
289 * Tokenize a string, allocating memory for the tokens.
291 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
293 LPWSTR * token_array;
294 int tokens = 0;
295 int i;
296 LPCWSTR next_token;
298 if (string)
300 /* empty string has no tokens */
301 if (*string)
302 tokens++;
303 /* count tokens */
304 for (i = 0; string[i]; i++)
306 if (!strncmpW(string+i, token_string, strlenW(token_string)))
308 DWORD j;
309 tokens++;
310 /* we want to skip over separators, but not the null terminator */
311 for (j = 0; j < strlenW(token_string) - 1; j++)
312 if (!string[i+j])
313 break;
314 i += j;
319 /* add 1 for terminating NULL */
320 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
321 token_array[tokens] = NULL;
322 if (!tokens)
323 return token_array;
324 for (i = 0; i < tokens; i++)
326 int len;
327 next_token = strstrW(string, token_string);
328 if (!next_token) next_token = string+strlenW(string);
329 len = next_token - string;
330 token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
331 memcpy(token_array[i], string, len*sizeof(WCHAR));
332 token_array[i][len] = '\0';
333 string = next_token+strlenW(token_string);
335 return token_array;
338 /***********************************************************************
339 * HTTP_FreeTokens (internal)
341 * Frees memory returned from HTTP_Tokenize.
343 static void HTTP_FreeTokens(LPWSTR * token_array)
345 int i;
346 for (i = 0; token_array[i]; i++)
347 HeapFree(GetProcessHeap(), 0, token_array[i]);
348 HeapFree(GetProcessHeap(), 0, token_array);
351 /* **********************************************************************
353 * Helper functions for the HttpSendRequest(Ex) functions
356 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
358 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
359 http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
361 TRACE("%p\n", lpwhr);
363 HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
364 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
365 req->dwContentLength, req->bEndRequest);
367 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
370 static void HTTP_FixURL(http_request_t *lpwhr)
372 static const WCHAR szSlash[] = { '/',0 };
373 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
375 /* If we don't have a path we set it to root */
376 if (NULL == lpwhr->lpszPath)
377 lpwhr->lpszPath = heap_strdupW(szSlash);
378 else /* remove \r and \n*/
380 int nLen = strlenW(lpwhr->lpszPath);
381 while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
383 nLen--;
384 lpwhr->lpszPath[nLen]='\0';
386 /* Replace '\' with '/' */
387 while (nLen>0) {
388 nLen--;
389 if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
393 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
394 lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
395 && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
397 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
398 (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
399 *fixurl = '/';
400 strcpyW(fixurl + 1, lpwhr->lpszPath);
401 HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
402 lpwhr->lpszPath = fixurl;
406 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
408 LPWSTR requestString;
409 DWORD len, n;
410 LPCWSTR *req;
411 UINT i;
412 LPWSTR p;
414 static const WCHAR szSpace[] = { ' ',0 };
415 static const WCHAR szColon[] = { ':',' ',0 };
416 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
418 /* allocate space for an array of all the string pointers to be added */
419 len = (lpwhr->nCustHeaders)*4 + 10;
420 req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
422 /* add the verb, path and HTTP version string */
423 n = 0;
424 req[n++] = verb;
425 req[n++] = szSpace;
426 req[n++] = path;
427 req[n++] = szSpace;
428 req[n++] = version;
430 /* Append custom request headers */
431 for (i = 0; i < lpwhr->nCustHeaders; i++)
433 if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
435 req[n++] = szCrLf;
436 req[n++] = lpwhr->pCustHeaders[i].lpszField;
437 req[n++] = szColon;
438 req[n++] = lpwhr->pCustHeaders[i].lpszValue;
440 TRACE("Adding custom header %s (%s)\n",
441 debugstr_w(lpwhr->pCustHeaders[i].lpszField),
442 debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
446 if( n >= len )
447 ERR("oops. buffer overrun\n");
449 req[n] = NULL;
450 requestString = HTTP_build_req( req, 4 );
451 HeapFree( GetProcessHeap(), 0, req );
454 * Set (header) termination string for request
455 * Make sure there's exactly two new lines at the end of the request
457 p = &requestString[strlenW(requestString)-1];
458 while ( (*p == '\n') || (*p == '\r') )
459 p--;
460 strcpyW( p+1, sztwocrlf );
462 return requestString;
465 static void HTTP_ProcessCookies( http_request_t *lpwhr )
467 int HeaderIndex;
468 int numCookies = 0;
469 LPHTTPHEADERW setCookieHeader;
471 while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
473 setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
475 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
477 int len;
478 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
479 LPWSTR buf_url;
480 LPHTTPHEADERW Host;
482 Host = HTTP_GetHeader(lpwhr, hostW);
483 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
484 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
485 sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
486 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
488 HeapFree(GetProcessHeap(), 0, buf_url);
490 numCookies++;
494 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue )
496 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
497 return !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
498 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
501 static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
502 struct HttpAuthInfo **ppAuthInfo,
503 LPWSTR domain_and_username, LPWSTR password )
505 SECURITY_STATUS sec_status;
506 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
507 BOOL first = FALSE;
509 TRACE("%s\n", debugstr_w(pszAuthValue));
511 if (!pAuthInfo)
513 TimeStamp exp;
515 first = TRUE;
516 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
517 if (!pAuthInfo)
518 return FALSE;
520 SecInvalidateHandle(&pAuthInfo->cred);
521 SecInvalidateHandle(&pAuthInfo->ctx);
522 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
523 pAuthInfo->attr = 0;
524 pAuthInfo->auth_data = NULL;
525 pAuthInfo->auth_data_len = 0;
526 pAuthInfo->finished = FALSE;
528 if (is_basic_auth_value(pszAuthValue))
530 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
531 pAuthInfo->scheme = heap_strdupW(szBasic);
532 if (!pAuthInfo->scheme)
534 HeapFree(GetProcessHeap(), 0, pAuthInfo);
535 return FALSE;
538 else
540 PVOID pAuthData;
541 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
543 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
544 if (!pAuthInfo->scheme)
546 HeapFree(GetProcessHeap(), 0, pAuthInfo);
547 return FALSE;
550 if (domain_and_username)
552 WCHAR *user = strchrW(domain_and_username, '\\');
553 WCHAR *domain = domain_and_username;
555 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
557 pAuthData = &nt_auth_identity;
559 if (user) user++;
560 else
562 user = domain_and_username;
563 domain = NULL;
566 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
567 nt_auth_identity.User = user;
568 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
569 nt_auth_identity.Domain = domain;
570 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
571 nt_auth_identity.Password = password;
572 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
574 else
575 /* use default credentials */
576 pAuthData = NULL;
578 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
579 SECPKG_CRED_OUTBOUND, NULL,
580 pAuthData, NULL,
581 NULL, &pAuthInfo->cred,
582 &exp);
583 if (sec_status == SEC_E_OK)
585 PSecPkgInfoW sec_pkg_info;
586 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
587 if (sec_status == SEC_E_OK)
589 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
590 FreeContextBuffer(sec_pkg_info);
593 if (sec_status != SEC_E_OK)
595 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
596 debugstr_w(pAuthInfo->scheme), sec_status);
597 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
598 HeapFree(GetProcessHeap(), 0, pAuthInfo);
599 return FALSE;
602 *ppAuthInfo = pAuthInfo;
604 else if (pAuthInfo->finished)
605 return FALSE;
607 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
608 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
610 ERR("authentication scheme changed from %s to %s\n",
611 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
612 return FALSE;
615 if (is_basic_auth_value(pszAuthValue))
617 int userlen;
618 int passlen;
619 char *auth_data;
621 TRACE("basic authentication\n");
623 /* we don't cache credentials for basic authentication, so we can't
624 * retrieve them if the application didn't pass us any credentials */
625 if (!domain_and_username) return FALSE;
627 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
628 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
630 /* length includes a nul terminator, which will be re-used for the ':' */
631 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
632 if (!auth_data)
633 return FALSE;
635 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
636 auth_data[userlen] = ':';
637 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
639 pAuthInfo->auth_data = auth_data;
640 pAuthInfo->auth_data_len = userlen + 1 + passlen;
641 pAuthInfo->finished = TRUE;
643 return TRUE;
645 else
647 LPCWSTR pszAuthData;
648 SecBufferDesc out_desc, in_desc;
649 SecBuffer out, in;
650 unsigned char *buffer;
651 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
652 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
654 in.BufferType = SECBUFFER_TOKEN;
655 in.cbBuffer = 0;
656 in.pvBuffer = NULL;
658 in_desc.ulVersion = 0;
659 in_desc.cBuffers = 1;
660 in_desc.pBuffers = &in;
662 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
663 if (*pszAuthData == ' ')
665 pszAuthData++;
666 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
667 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
668 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
671 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
673 out.BufferType = SECBUFFER_TOKEN;
674 out.cbBuffer = pAuthInfo->max_token;
675 out.pvBuffer = buffer;
677 out_desc.ulVersion = 0;
678 out_desc.cBuffers = 1;
679 out_desc.pBuffers = &out;
681 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
682 first ? NULL : &pAuthInfo->ctx,
683 first ? lpwhr->lpHttpSession->lpszServerName : NULL,
684 context_req, 0, SECURITY_NETWORK_DREP,
685 in.pvBuffer ? &in_desc : NULL,
686 0, &pAuthInfo->ctx, &out_desc,
687 &pAuthInfo->attr, &pAuthInfo->exp);
688 if (sec_status == SEC_E_OK)
690 pAuthInfo->finished = TRUE;
691 pAuthInfo->auth_data = out.pvBuffer;
692 pAuthInfo->auth_data_len = out.cbBuffer;
693 TRACE("sending last auth packet\n");
695 else if (sec_status == SEC_I_CONTINUE_NEEDED)
697 pAuthInfo->auth_data = out.pvBuffer;
698 pAuthInfo->auth_data_len = out.cbBuffer;
699 TRACE("sending next auth packet\n");
701 else
703 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
704 pAuthInfo->finished = TRUE;
705 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
706 return FALSE;
710 return TRUE;
713 /***********************************************************************
714 * HTTP_HttpAddRequestHeadersW (internal)
716 static BOOL HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
717 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
719 LPWSTR lpszStart;
720 LPWSTR lpszEnd;
721 LPWSTR buffer;
722 BOOL bSuccess = FALSE;
723 DWORD len;
725 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
727 if( dwHeaderLength == ~0U )
728 len = strlenW(lpszHeader);
729 else
730 len = dwHeaderLength;
731 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
732 lstrcpynW( buffer, lpszHeader, len + 1);
734 lpszStart = buffer;
738 LPWSTR * pFieldAndValue;
740 lpszEnd = lpszStart;
742 while (*lpszEnd != '\0')
744 if (*lpszEnd == '\r' || *lpszEnd == '\n')
745 break;
746 lpszEnd++;
749 if (*lpszStart == '\0')
750 break;
752 if (*lpszEnd == '\r' || *lpszEnd == '\n')
754 *lpszEnd = '\0';
755 lpszEnd++; /* Jump over newline */
757 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
758 if (*lpszStart == '\0')
760 /* Skip 0-length headers */
761 lpszStart = lpszEnd;
762 bSuccess = TRUE;
763 continue;
765 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
766 if (pFieldAndValue)
768 bSuccess = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
769 if (bSuccess)
770 bSuccess = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
771 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
772 HTTP_FreeTokens(pFieldAndValue);
775 lpszStart = lpszEnd;
776 } while (bSuccess);
778 HeapFree(GetProcessHeap(), 0, buffer);
780 return bSuccess;
783 /***********************************************************************
784 * HttpAddRequestHeadersW (WININET.@)
786 * Adds one or more HTTP header to the request handler
788 * NOTE
789 * On Windows if dwHeaderLength includes the trailing '\0', then
790 * HttpAddRequestHeadersW() adds it too. However this results in an
791 * invalid Http header which is rejected by some servers so we probably
792 * don't need to match Windows on that point.
794 * RETURNS
795 * TRUE on success
796 * FALSE on failure
799 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
800 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
802 BOOL bSuccess = FALSE;
803 http_request_t *lpwhr;
805 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
807 if (!lpszHeader)
808 return TRUE;
810 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
811 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
813 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
814 goto lend;
816 bSuccess = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
817 lend:
818 if( lpwhr )
819 WININET_Release( &lpwhr->hdr );
821 return bSuccess;
824 /***********************************************************************
825 * HttpAddRequestHeadersA (WININET.@)
827 * Adds one or more HTTP header to the request handler
829 * RETURNS
830 * TRUE on success
831 * FALSE on failure
834 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
835 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
837 DWORD len;
838 LPWSTR hdr;
839 BOOL r;
841 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
843 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
844 hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
845 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
846 if( dwHeaderLength != ~0U )
847 dwHeaderLength = len;
849 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
851 HeapFree( GetProcessHeap(), 0, hdr );
853 return r;
856 /***********************************************************************
857 * HttpEndRequestA (WININET.@)
859 * Ends an HTTP request that was started by HttpSendRequestEx
861 * RETURNS
862 * TRUE if successful
863 * FALSE on failure
866 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
867 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
869 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
871 if (lpBuffersOut)
873 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
874 return FALSE;
877 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
880 static BOOL HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
882 BOOL rc = FALSE;
883 INT responseLen;
884 DWORD dwBufferSize;
885 INTERNET_ASYNC_RESULT iar;
887 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
888 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
890 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
891 if (responseLen)
892 rc = TRUE;
894 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
895 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
897 /* process cookies here. Is this right? */
898 HTTP_ProcessCookies(lpwhr);
900 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
902 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
904 DWORD dwCode,dwCodeLength = sizeof(DWORD);
905 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) &&
906 (dwCode == 302 || dwCode == 301 || dwCode == 303))
908 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
909 dwBufferSize=sizeof(szNewLocation);
910 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL))
912 /* redirects are always GETs */
913 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
914 lpwhr->lpszVerb = heap_strdupW(szGET);
915 HTTP_DrainContent(lpwhr);
916 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
918 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
919 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
920 rc = HTTP_HandleRedirect(lpwhr, new_url);
921 if (rc)
922 rc = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
923 HeapFree( GetProcessHeap(), 0, new_url );
929 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
930 iar.dwError = rc ? 0 : INTERNET_GetLastError();
932 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
933 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
934 sizeof(INTERNET_ASYNC_RESULT));
935 return rc;
938 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
940 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
941 http_request_t *lpwhr = (http_request_t*)work->hdr;
943 TRACE("%p\n", lpwhr);
945 HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
948 /***********************************************************************
949 * HttpEndRequestW (WININET.@)
951 * Ends an HTTP request that was started by HttpSendRequestEx
953 * RETURNS
954 * TRUE if successful
955 * FALSE on failure
958 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
959 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
961 BOOL rc = FALSE;
962 http_request_t *lpwhr;
964 TRACE("-->\n");
966 if (lpBuffersOut)
968 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
969 return FALSE;
972 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
974 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
976 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
977 if (lpwhr)
978 WININET_Release( &lpwhr->hdr );
979 return FALSE;
981 lpwhr->hdr.dwFlags |= dwFlags;
983 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
985 WORKREQUEST work;
986 struct WORKREQ_HTTPENDREQUESTW *request;
988 work.asyncproc = AsyncHttpEndRequestProc;
989 work.hdr = WININET_AddRef( &lpwhr->hdr );
991 request = &work.u.HttpEndRequestW;
992 request->dwFlags = dwFlags;
993 request->dwContext = dwContext;
995 INTERNET_AsyncCall(&work);
996 INTERNET_SetLastError(ERROR_IO_PENDING);
998 else
999 rc = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
1001 WININET_Release( &lpwhr->hdr );
1002 TRACE("%i <--\n",rc);
1003 return rc;
1006 /***********************************************************************
1007 * HttpOpenRequestW (WININET.@)
1009 * Open a HTTP request handle
1011 * RETURNS
1012 * HINTERNET a HTTP request handle on success
1013 * NULL on failure
1016 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
1017 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
1018 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
1019 DWORD dwFlags, DWORD_PTR dwContext)
1021 http_session_t *lpwhs;
1022 HINTERNET handle = NULL;
1024 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1025 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
1026 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
1027 dwFlags, dwContext);
1028 if(lpszAcceptTypes!=NULL)
1030 int i;
1031 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
1032 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
1035 lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
1036 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
1038 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1039 goto lend;
1043 * My tests seem to show that the windows version does not
1044 * become asynchronous until after this point. And anyhow
1045 * if this call was asynchronous then how would you get the
1046 * necessary HINTERNET pointer returned by this function.
1049 handle = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
1050 lpszVersion, lpszReferrer, lpszAcceptTypes,
1051 dwFlags, dwContext);
1052 lend:
1053 if( lpwhs )
1054 WININET_Release( &lpwhs->hdr );
1055 TRACE("returning %p\n", handle);
1056 return handle;
1060 /***********************************************************************
1061 * HttpOpenRequestA (WININET.@)
1063 * Open a HTTP request handle
1065 * RETURNS
1066 * HINTERNET a HTTP request handle on success
1067 * NULL on failure
1070 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1071 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1072 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1073 DWORD dwFlags, DWORD_PTR dwContext)
1075 LPWSTR szVerb = NULL, szObjectName = NULL;
1076 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1077 INT acceptTypesCount;
1078 HINTERNET rc = FALSE;
1079 LPCSTR *types;
1081 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1082 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1083 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1084 dwFlags, dwContext);
1086 if (lpszVerb)
1088 szVerb = heap_strdupAtoW(lpszVerb);
1089 if ( !szVerb )
1090 goto end;
1093 if (lpszObjectName)
1095 szObjectName = heap_strdupAtoW(lpszObjectName);
1096 if ( !szObjectName )
1097 goto end;
1100 if (lpszVersion)
1102 szVersion = heap_strdupAtoW(lpszVersion);
1103 if ( !szVersion )
1104 goto end;
1107 if (lpszReferrer)
1109 szReferrer = heap_strdupAtoW(lpszReferrer);
1110 if ( !szReferrer )
1111 goto end;
1114 if (lpszAcceptTypes)
1116 acceptTypesCount = 0;
1117 types = lpszAcceptTypes;
1118 while (*types)
1120 __TRY
1122 /* find out how many there are */
1123 if (*types && **types)
1125 TRACE("accept type: %s\n", debugstr_a(*types));
1126 acceptTypesCount++;
1129 __EXCEPT_PAGE_FAULT
1131 WARN("invalid accept type pointer\n");
1133 __ENDTRY;
1134 types++;
1136 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1137 if (!szAcceptTypes) goto end;
1139 acceptTypesCount = 0;
1140 types = lpszAcceptTypes;
1141 while (*types)
1143 __TRY
1145 if (*types && **types)
1146 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1148 __EXCEPT_PAGE_FAULT
1150 /* ignore invalid pointer */
1152 __ENDTRY;
1153 types++;
1155 szAcceptTypes[acceptTypesCount] = NULL;
1158 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1159 szVersion, szReferrer,
1160 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1162 end:
1163 if (szAcceptTypes)
1165 acceptTypesCount = 0;
1166 while (szAcceptTypes[acceptTypesCount])
1168 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1169 acceptTypesCount++;
1171 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1173 HeapFree(GetProcessHeap(), 0, szReferrer);
1174 HeapFree(GetProcessHeap(), 0, szVersion);
1175 HeapFree(GetProcessHeap(), 0, szObjectName);
1176 HeapFree(GetProcessHeap(), 0, szVerb);
1178 return rc;
1181 /***********************************************************************
1182 * HTTP_EncodeBase64
1184 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1186 UINT n = 0, x;
1187 static const CHAR HTTP_Base64Enc[] =
1188 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1190 while( len > 0 )
1192 /* first 6 bits, all from bin[0] */
1193 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1194 x = (bin[0] & 3) << 4;
1196 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1197 if( len == 1 )
1199 base64[n++] = HTTP_Base64Enc[x];
1200 base64[n++] = '=';
1201 base64[n++] = '=';
1202 break;
1204 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1205 x = ( bin[1] & 0x0f ) << 2;
1207 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1208 if( len == 2 )
1210 base64[n++] = HTTP_Base64Enc[x];
1211 base64[n++] = '=';
1212 break;
1214 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1216 /* last 6 bits, all from bin [2] */
1217 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1218 bin += 3;
1219 len -= 3;
1221 base64[n] = 0;
1222 return n;
1225 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1226 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1227 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1228 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1229 static const signed char HTTP_Base64Dec[256] =
1231 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1232 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1233 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1234 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1235 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1236 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1237 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1238 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1239 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1240 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1241 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1242 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1243 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1244 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1245 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1246 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1247 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1248 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1249 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1250 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1251 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1252 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1253 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1254 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1255 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1256 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1258 #undef CH
1260 /***********************************************************************
1261 * HTTP_DecodeBase64
1263 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1265 unsigned int n = 0;
1267 while(*base64)
1269 signed char in[4];
1271 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1272 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1273 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1274 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1276 WARN("invalid base64: %s\n", debugstr_w(base64));
1277 return 0;
1279 if (bin)
1280 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1281 n++;
1283 if ((base64[2] == '=') && (base64[3] == '='))
1284 break;
1285 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1286 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1288 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1289 return 0;
1291 if (bin)
1292 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1293 n++;
1295 if (base64[3] == '=')
1296 break;
1297 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1298 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1300 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1301 return 0;
1303 if (bin)
1304 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1305 n++;
1307 base64 += 4;
1310 return n;
1313 /***********************************************************************
1314 * HTTP_InsertAuthorization
1316 * Insert or delete the authorization field in the request header.
1318 static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1320 if (pAuthInfo)
1322 static const WCHAR wszSpace[] = {' ',0};
1323 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1324 unsigned int len;
1325 WCHAR *authorization = NULL;
1327 if (pAuthInfo->auth_data_len)
1329 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1330 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1331 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1332 if (!authorization)
1333 return FALSE;
1335 strcpyW(authorization, pAuthInfo->scheme);
1336 strcatW(authorization, wszSpace);
1337 HTTP_EncodeBase64(pAuthInfo->auth_data,
1338 pAuthInfo->auth_data_len,
1339 authorization+strlenW(authorization));
1341 /* clear the data as it isn't valid now that it has been sent to the
1342 * server, unless it's Basic authentication which doesn't do
1343 * connection tracking */
1344 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1346 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1347 pAuthInfo->auth_data = NULL;
1348 pAuthInfo->auth_data_len = 0;
1352 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1354 HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1356 HeapFree(GetProcessHeap(), 0, authorization);
1358 return TRUE;
1361 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1363 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1364 DWORD size;
1366 size = sizeof(new_location);
1367 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL))
1369 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1370 strcpyW( url, new_location );
1372 else
1374 static const WCHAR slash[] = { '/',0 };
1375 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1376 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1377 http_session_t *session = req->lpHttpSession;
1379 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1380 size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
1382 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1384 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1385 sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
1386 else
1387 sprintfW( url, format, session->lpszHostName, session->nHostPort );
1388 if (req->lpszPath[0] != '/') strcatW( url, slash );
1389 strcatW( url, req->lpszPath );
1391 TRACE("url=%s\n", debugstr_w(url));
1392 return url;
1395 /***********************************************************************
1396 * HTTP_DealWithProxy
1398 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
1400 WCHAR buf[MAXHOSTNAME];
1401 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1402 static WCHAR szNul[] = { 0 };
1403 URL_COMPONENTSW UrlComponents;
1404 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1405 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1407 memset( &UrlComponents, 0, sizeof UrlComponents );
1408 UrlComponents.dwStructSize = sizeof UrlComponents;
1409 UrlComponents.lpszHostName = buf;
1410 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1412 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1413 hIC->lpszProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1414 sprintfW(proxy, szFormat, hIC->lpszProxy);
1415 else
1416 strcpyW(proxy, hIC->lpszProxy);
1417 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1418 return FALSE;
1419 if( UrlComponents.dwHostNameLength == 0 )
1420 return FALSE;
1422 if( !lpwhr->lpszPath )
1423 lpwhr->lpszPath = szNul;
1425 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1426 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1428 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1429 lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1430 lpwhs->nServerPort = UrlComponents.nPort;
1432 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1433 return TRUE;
1436 #ifndef INET6_ADDRSTRLEN
1437 #define INET6_ADDRSTRLEN 46
1438 #endif
1440 static BOOL HTTP_ResolveName(http_request_t *lpwhr)
1442 char szaddr[INET6_ADDRSTRLEN];
1443 http_session_t *lpwhs = lpwhr->lpHttpSession;
1444 const void *addr;
1446 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1447 INTERNET_STATUS_RESOLVING_NAME,
1448 lpwhs->lpszServerName,
1449 strlenW(lpwhs->lpszServerName)+1);
1451 lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1452 if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1453 (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1455 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1456 return FALSE;
1459 switch (lpwhs->socketAddress.ss_family)
1461 case AF_INET:
1462 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1463 break;
1464 case AF_INET6:
1465 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1466 break;
1467 default:
1468 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1469 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1470 return FALSE;
1472 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1473 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1474 INTERNET_STATUS_NAME_RESOLVED,
1475 szaddr, strlen(szaddr)+1);
1477 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1478 return TRUE;
1482 /***********************************************************************
1483 * HTTPREQ_Destroy (internal)
1485 * Deallocate request handle
1488 static void HTTPREQ_Destroy(object_header_t *hdr)
1490 http_request_t *lpwhr = (http_request_t*) hdr;
1491 DWORD i;
1493 TRACE("\n");
1495 if(lpwhr->hCacheFile)
1496 CloseHandle(lpwhr->hCacheFile);
1498 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1500 DeleteCriticalSection( &lpwhr->read_section );
1501 WININET_Release(&lpwhr->lpHttpSession->hdr);
1503 if (lpwhr->pAuthInfo)
1505 if (SecIsValidHandle(&lpwhr->pAuthInfo->ctx))
1506 DeleteSecurityContext(&lpwhr->pAuthInfo->ctx);
1507 if (SecIsValidHandle(&lpwhr->pAuthInfo->cred))
1508 FreeCredentialsHandle(&lpwhr->pAuthInfo->cred);
1510 HeapFree(GetProcessHeap(), 0, lpwhr->pAuthInfo->auth_data);
1511 HeapFree(GetProcessHeap(), 0, lpwhr->pAuthInfo->scheme);
1512 HeapFree(GetProcessHeap(), 0, lpwhr->pAuthInfo);
1513 lpwhr->pAuthInfo = NULL;
1516 if (lpwhr->pProxyAuthInfo)
1518 if (SecIsValidHandle(&lpwhr->pProxyAuthInfo->ctx))
1519 DeleteSecurityContext(&lpwhr->pProxyAuthInfo->ctx);
1520 if (SecIsValidHandle(&lpwhr->pProxyAuthInfo->cred))
1521 FreeCredentialsHandle(&lpwhr->pProxyAuthInfo->cred);
1523 HeapFree(GetProcessHeap(), 0, lpwhr->pProxyAuthInfo->auth_data);
1524 HeapFree(GetProcessHeap(), 0, lpwhr->pProxyAuthInfo->scheme);
1525 HeapFree(GetProcessHeap(), 0, lpwhr->pProxyAuthInfo);
1526 lpwhr->pProxyAuthInfo = NULL;
1529 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1530 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1531 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1532 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1533 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1535 for (i = 0; i < lpwhr->nCustHeaders; i++)
1537 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1538 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1541 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1542 HeapFree(GetProcessHeap(), 0, lpwhr);
1545 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1547 http_request_t *lpwhr = (http_request_t*) hdr;
1549 TRACE("%p\n",lpwhr);
1551 #ifdef HAVE_ZLIB
1552 if(lpwhr->gzip_stream) {
1553 inflateEnd(&lpwhr->gzip_stream->zstream);
1554 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1555 lpwhr->gzip_stream = NULL;
1557 #endif
1559 if (!NETCON_connected(&lpwhr->netConnection))
1560 return;
1562 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1563 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1565 NETCON_close(&lpwhr->netConnection);
1567 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1568 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1571 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1573 LPHTTPHEADERW host_header;
1575 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1577 host_header = HTTP_GetHeader(req, hostW);
1578 if(!host_header)
1579 return FALSE;
1581 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1582 return TRUE;
1585 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1587 http_request_t *req = (http_request_t*)hdr;
1589 switch(option) {
1590 case INTERNET_OPTION_SECURITY_FLAGS:
1592 http_session_t *lpwhs;
1593 lpwhs = req->lpHttpSession;
1595 if (*size < sizeof(ULONG))
1596 return ERROR_INSUFFICIENT_BUFFER;
1598 *size = sizeof(DWORD);
1599 if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1600 *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1601 else
1602 *(DWORD*)buffer = 0;
1603 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1604 return ERROR_SUCCESS;
1607 case INTERNET_OPTION_HANDLE_TYPE:
1608 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1610 if (*size < sizeof(ULONG))
1611 return ERROR_INSUFFICIENT_BUFFER;
1613 *size = sizeof(DWORD);
1614 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1615 return ERROR_SUCCESS;
1617 case INTERNET_OPTION_URL: {
1618 WCHAR url[INTERNET_MAX_URL_LENGTH];
1619 HTTPHEADERW *host;
1620 DWORD len;
1621 WCHAR *pch;
1623 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1625 TRACE("INTERNET_OPTION_URL\n");
1627 host = HTTP_GetHeader(req, hostW);
1628 strcpyW(url, httpW);
1629 strcatW(url, host->lpszValue);
1630 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1631 *pch = 0;
1632 strcatW(url, req->lpszPath);
1634 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1636 if(unicode) {
1637 len = (strlenW(url)+1) * sizeof(WCHAR);
1638 if(*size < len)
1639 return ERROR_INSUFFICIENT_BUFFER;
1641 *size = len;
1642 strcpyW(buffer, url);
1643 return ERROR_SUCCESS;
1644 }else {
1645 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1646 if(len > *size)
1647 return ERROR_INSUFFICIENT_BUFFER;
1649 *size = len;
1650 return ERROR_SUCCESS;
1654 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1655 INTERNET_CACHE_ENTRY_INFOW *info;
1656 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1657 WCHAR url[INTERNET_MAX_URL_LENGTH];
1658 DWORD nbytes, error;
1659 BOOL ret;
1661 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1663 if (*size < sizeof(*ts))
1665 *size = sizeof(*ts);
1666 return ERROR_INSUFFICIENT_BUFFER;
1668 nbytes = 0;
1669 HTTP_GetRequestURL(req, url);
1670 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1671 error = GetLastError();
1672 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1674 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1675 return ERROR_OUTOFMEMORY;
1677 GetUrlCacheEntryInfoW(url, info, &nbytes);
1679 ts->ftExpires = info->ExpireTime;
1680 ts->ftLastModified = info->LastModifiedTime;
1682 HeapFree(GetProcessHeap(), 0, info);
1683 *size = sizeof(*ts);
1684 return ERROR_SUCCESS;
1686 return error;
1689 case INTERNET_OPTION_DATAFILE_NAME: {
1690 DWORD req_size;
1692 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1694 if(!req->lpszCacheFile) {
1695 *size = 0;
1696 return ERROR_INTERNET_ITEM_NOT_FOUND;
1699 if(unicode) {
1700 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1701 if(*size < req_size)
1702 return ERROR_INSUFFICIENT_BUFFER;
1704 *size = req_size;
1705 memcpy(buffer, req->lpszCacheFile, *size);
1706 return ERROR_SUCCESS;
1707 }else {
1708 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1709 if (req_size > *size)
1710 return ERROR_INSUFFICIENT_BUFFER;
1712 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1713 -1, buffer, *size, NULL, NULL);
1714 return ERROR_SUCCESS;
1718 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1719 PCCERT_CONTEXT context;
1721 if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1722 *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1723 return ERROR_INSUFFICIENT_BUFFER;
1726 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1727 if(context) {
1728 INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1729 DWORD len;
1731 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1732 info->ftExpiry = context->pCertInfo->NotAfter;
1733 info->ftStart = context->pCertInfo->NotBefore;
1734 if(unicode) {
1735 len = CertNameToStrW(context->dwCertEncodingType,
1736 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1737 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1738 if(info->lpszSubjectInfo)
1739 CertNameToStrW(context->dwCertEncodingType,
1740 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1741 info->lpszSubjectInfo, len);
1742 len = CertNameToStrW(context->dwCertEncodingType,
1743 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1744 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1745 if (info->lpszIssuerInfo)
1746 CertNameToStrW(context->dwCertEncodingType,
1747 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1748 info->lpszIssuerInfo, len);
1749 }else {
1750 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1752 len = CertNameToStrA(context->dwCertEncodingType,
1753 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1754 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1755 if(infoA->lpszSubjectInfo)
1756 CertNameToStrA(context->dwCertEncodingType,
1757 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1758 infoA->lpszSubjectInfo, len);
1759 len = CertNameToStrA(context->dwCertEncodingType,
1760 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1761 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1762 if(infoA->lpszIssuerInfo)
1763 CertNameToStrA(context->dwCertEncodingType,
1764 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1765 infoA->lpszIssuerInfo, len);
1769 * Contrary to MSDN, these do not appear to be set.
1770 * lpszProtocolName
1771 * lpszSignatureAlgName
1772 * lpszEncryptionAlgName
1773 * dwKeySize
1775 CertFreeCertificateContext(context);
1776 return ERROR_SUCCESS;
1781 return INET_QueryOption(option, buffer, size, unicode);
1784 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1786 http_request_t *req = (http_request_t*)hdr;
1788 switch(option) {
1789 case INTERNET_OPTION_SEND_TIMEOUT:
1790 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1791 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1793 if (size != sizeof(DWORD))
1794 return ERROR_INVALID_PARAMETER;
1796 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1797 *(DWORD*)buffer);
1799 case INTERNET_OPTION_USERNAME:
1800 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1801 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1802 return ERROR_SUCCESS;
1804 case INTERNET_OPTION_PASSWORD:
1805 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1806 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1807 return ERROR_SUCCESS;
1808 case INTERNET_OPTION_HTTP_DECODING:
1809 if(size != sizeof(BOOL))
1810 return ERROR_INVALID_PARAMETER;
1811 req->decoding = *(BOOL*)buffer;
1812 return ERROR_SUCCESS;
1815 return ERROR_INTERNET_INVALID_OPTION;
1818 /* read some more data into the read buffer (the read section must be held) */
1819 static BOOL read_more_data( http_request_t *req, int maxlen )
1821 int len;
1823 if (req->read_pos)
1825 /* move existing data to the start of the buffer */
1826 if(req->read_size)
1827 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1828 req->read_pos = 0;
1831 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1833 if(!NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1834 maxlen - req->read_size, 0, &len ))
1835 return FALSE;
1837 req->read_size += len;
1838 return TRUE;
1841 /* remove some amount of data from the read buffer (the read section must be held) */
1842 static void remove_data( http_request_t *req, int count )
1844 if (!(req->read_size -= count)) req->read_pos = 0;
1845 else req->read_pos += count;
1848 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1850 int count, bytes_read, pos = 0;
1852 EnterCriticalSection( &req->read_section );
1853 for (;;)
1855 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1857 if (eol)
1859 count = eol - (req->read_buf + req->read_pos);
1860 bytes_read = count + 1;
1862 else count = bytes_read = req->read_size;
1864 count = min( count, *len - pos );
1865 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1866 pos += count;
1867 remove_data( req, bytes_read );
1868 if (eol) break;
1870 if (!read_more_data( req, -1 ) || !req->read_size)
1872 *len = 0;
1873 TRACE( "returning empty string\n" );
1874 LeaveCriticalSection( &req->read_section );
1875 return FALSE;
1878 LeaveCriticalSection( &req->read_section );
1880 if (pos < *len)
1882 if (pos && buffer[pos - 1] == '\r') pos--;
1883 *len = pos + 1;
1885 buffer[*len - 1] = 0;
1886 TRACE( "returning %s\n", debugstr_a(buffer));
1887 return TRUE;
1890 /* discard data contents until we reach end of line (the read section must be held) */
1891 static BOOL discard_eol( http_request_t *req )
1895 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1896 if (eol)
1898 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
1899 break;
1901 req->read_pos = req->read_size = 0; /* discard everything */
1902 if (!read_more_data( req, -1 )) return FALSE;
1903 } while (req->read_size);
1904 return TRUE;
1907 /* read the size of the next chunk (the read section must be held) */
1908 static BOOL start_next_chunk( http_request_t *req )
1910 DWORD chunk_size = 0;
1912 if (!req->dwContentLength) return TRUE;
1913 if (req->dwContentLength == req->dwContentRead)
1915 /* read terminator for the previous chunk */
1916 if (!discard_eol( req )) return FALSE;
1917 req->dwContentLength = ~0u;
1918 req->dwContentRead = 0;
1920 for (;;)
1922 while (req->read_size)
1924 char ch = req->read_buf[req->read_pos];
1925 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
1926 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
1927 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
1928 else if (ch == ';' || ch == '\r' || ch == '\n')
1930 TRACE( "reading %u byte chunk\n", chunk_size );
1931 req->dwContentLength = chunk_size;
1932 req->dwContentRead = 0;
1933 if (!discard_eol( req )) return FALSE;
1934 return TRUE;
1936 remove_data( req, 1 );
1938 if (!read_more_data( req, -1 )) return FALSE;
1939 if (!req->read_size)
1941 req->dwContentLength = req->dwContentRead = 0;
1942 return TRUE;
1947 /* check if we have reached the end of the data to read (the read section must be held) */
1948 static BOOL end_of_read_data( http_request_t *req )
1950 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
1951 if (req->read_chunked) return (req->dwContentLength == 0);
1952 if (req->dwContentLength == ~0u) return FALSE;
1953 return (req->dwContentLength == req->dwContentRead);
1956 /* fetch some more data into the read buffer (the read section must be held) */
1957 static BOOL refill_buffer( http_request_t *req )
1959 int len = sizeof(req->read_buf);
1961 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
1963 if (!start_next_chunk( req )) return FALSE;
1966 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
1967 if (len <= req->read_size) return TRUE;
1969 if (!read_more_data( req, len )) return FALSE;
1970 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
1971 return TRUE;
1974 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
1976 DWORD ret = ERROR_SUCCESS;
1977 int read = 0;
1979 #ifdef HAVE_ZLIB
1980 z_stream *zstream = &req->gzip_stream->zstream;
1981 int zres;
1983 while(read < size && !req->gzip_stream->end_of_data) {
1984 if(!req->read_size) {
1985 if(!sync || !refill_buffer(req))
1986 break;
1989 zstream->next_in = req->read_buf+req->read_pos;
1990 zstream->avail_in = req->read_size;
1991 zstream->next_out = buf+read;
1992 zstream->avail_out = size-read;
1993 zres = inflate(zstream, Z_FULL_FLUSH);
1994 read = size - zstream->avail_out;
1995 remove_data(req, req->read_size-zstream->avail_in);
1996 if(zres == Z_STREAM_END) {
1997 TRACE("end of data\n");
1998 req->gzip_stream->end_of_data = TRUE;
1999 }else if(zres != Z_OK) {
2000 WARN("inflate failed %d\n", zres);
2001 if(!read)
2002 ret = ERROR_INTERNET_DECODING_FAILED;
2003 break;
2006 #endif
2008 *read_ret = read;
2009 return ret;
2012 static void refill_gzip_buffer(http_request_t *req)
2014 DWORD res;
2015 int len;
2017 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2018 return;
2020 if(req->gzip_stream->buf_pos) {
2021 if(req->gzip_stream->buf_size)
2022 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2023 req->gzip_stream->buf_pos = 0;
2026 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2027 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2028 if(res == ERROR_SUCCESS)
2029 req->gzip_stream->buf_size += len;
2032 /* return the size of data available to be read immediately (the read section must be held) */
2033 static DWORD get_avail_data( http_request_t *req )
2035 if (req->gzip_stream) {
2036 refill_gzip_buffer(req);
2037 return req->gzip_stream->buf_size;
2039 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2040 return 0;
2041 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2044 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2046 INTERNET_ASYNC_RESULT iar;
2048 TRACE("%p\n", req);
2050 EnterCriticalSection( &req->read_section );
2051 if (refill_buffer( req )) {
2052 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2053 iar.dwError = first_notif ? 0 : get_avail_data(req);
2054 }else {
2055 iar.dwResult = 0;
2056 iar.dwError = INTERNET_GetLastError();
2058 LeaveCriticalSection( &req->read_section );
2060 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2061 sizeof(INTERNET_ASYNC_RESULT));
2064 /* read data from the http connection (the read section must be held) */
2065 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2067 BOOL finished_reading = FALSE;
2068 int len, bytes_read = 0;
2069 DWORD ret = ERROR_SUCCESS;
2071 EnterCriticalSection( &req->read_section );
2073 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2075 if (!start_next_chunk( req )) goto done;
2078 if(req->gzip_stream) {
2079 if(req->gzip_stream->buf_size) {
2080 bytes_read = min(req->gzip_stream->buf_size, size);
2081 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2082 req->gzip_stream->buf_pos += bytes_read;
2083 req->gzip_stream->buf_size -= bytes_read;
2084 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2085 refill_buffer(req);
2088 if(size > bytes_read) {
2089 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2090 if(ret == ERROR_SUCCESS)
2091 bytes_read += len;
2094 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2095 }else {
2096 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2098 if (req->read_size) {
2099 bytes_read = min( req->read_size, size );
2100 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2101 remove_data( req, bytes_read );
2104 if (size > bytes_read && (!bytes_read || sync)) {
2105 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2106 sync ? MSG_WAITALL : 0, &len))
2107 bytes_read += len;
2108 /* always return success, even if the network layer returns an error */
2111 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2113 done:
2114 req->dwContentRead += bytes_read;
2115 *read = bytes_read;
2117 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2118 LeaveCriticalSection( &req->read_section );
2120 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2121 BOOL res;
2122 DWORD dwBytesWritten;
2124 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2125 if(!res)
2126 WARN("WriteFile failed: %u\n", GetLastError());
2129 if(finished_reading)
2130 HTTP_FinishedReading(req);
2132 return ret;
2136 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2138 http_request_t *req = (http_request_t*)hdr;
2139 return HTTPREQ_Read(req, buffer, size, read, TRUE);
2142 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2144 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2145 http_request_t *req = (http_request_t*)workRequest->hdr;
2146 INTERNET_ASYNC_RESULT iar;
2147 DWORD res;
2149 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2151 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2152 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2154 iar.dwResult = res == ERROR_SUCCESS;
2155 iar.dwError = res;
2157 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2158 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2159 sizeof(INTERNET_ASYNC_RESULT));
2162 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2163 DWORD flags, DWORD_PTR context)
2165 http_request_t *req = (http_request_t*)hdr;
2166 DWORD res;
2168 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2169 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2171 if (buffers->dwStructSize != sizeof(*buffers))
2172 return ERROR_INVALID_PARAMETER;
2174 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2176 if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
2178 WORKREQUEST workRequest;
2180 if (TryEnterCriticalSection( &req->read_section ))
2182 if (get_avail_data(req))
2184 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2185 &buffers->dwBufferLength, FALSE);
2186 LeaveCriticalSection( &req->read_section );
2187 goto done;
2189 LeaveCriticalSection( &req->read_section );
2192 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2193 workRequest.hdr = WININET_AddRef(&req->hdr);
2194 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2196 INTERNET_AsyncCall(&workRequest);
2198 return ERROR_IO_PENDING;
2201 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2202 !(flags & IRF_NO_WAIT));
2204 done:
2205 if (res == ERROR_SUCCESS) {
2206 DWORD size = buffers->dwBufferLength;
2207 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2208 &size, sizeof(size));
2211 return res;
2214 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2216 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2217 http_request_t *req = (http_request_t*)workRequest->hdr;
2218 INTERNET_ASYNC_RESULT iar;
2219 DWORD res;
2221 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2223 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2224 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2226 iar.dwResult = res == ERROR_SUCCESS;
2227 iar.dwError = res;
2229 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2230 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2231 sizeof(INTERNET_ASYNC_RESULT));
2234 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2235 DWORD flags, DWORD_PTR context)
2238 http_request_t *req = (http_request_t*)hdr;
2239 DWORD res;
2241 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2242 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2244 if (buffers->dwStructSize != sizeof(*buffers))
2245 return ERROR_INVALID_PARAMETER;
2247 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2249 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2251 WORKREQUEST workRequest;
2253 if (TryEnterCriticalSection( &req->read_section ))
2255 if (get_avail_data(req))
2257 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2258 &buffers->dwBufferLength, FALSE);
2259 LeaveCriticalSection( &req->read_section );
2260 goto done;
2262 LeaveCriticalSection( &req->read_section );
2265 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2266 workRequest.hdr = WININET_AddRef(&req->hdr);
2267 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2269 INTERNET_AsyncCall(&workRequest);
2271 return ERROR_IO_PENDING;
2274 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2275 !(flags & IRF_NO_WAIT));
2277 done:
2278 if (res == ERROR_SUCCESS) {
2279 DWORD size = buffers->dwBufferLength;
2280 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2281 &size, sizeof(size));
2284 return res;
2287 static BOOL HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2289 BOOL ret;
2290 http_request_t *lpwhr = (http_request_t*)hdr;
2292 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2294 *written = 0;
2295 if ((ret = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written)))
2296 lpwhr->dwBytesWritten += *written;
2298 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2299 return ret;
2302 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2304 http_request_t *req = (http_request_t*)workRequest->hdr;
2306 HTTP_ReceiveRequestData(req, FALSE);
2309 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2311 http_request_t *req = (http_request_t*)hdr;
2313 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2315 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2317 WORKREQUEST workRequest;
2319 /* never wait, if we can't enter the section we queue an async request right away */
2320 if (TryEnterCriticalSection( &req->read_section ))
2322 if ((*available = get_avail_data( req ))) goto done;
2323 if (end_of_read_data( req )) goto done;
2324 LeaveCriticalSection( &req->read_section );
2327 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2328 workRequest.hdr = WININET_AddRef( &req->hdr );
2330 INTERNET_AsyncCall(&workRequest);
2332 return ERROR_IO_PENDING;
2335 EnterCriticalSection( &req->read_section );
2337 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2339 refill_buffer( req );
2340 *available = get_avail_data( req );
2343 done:
2344 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2346 DWORD extra;
2347 if (NETCON_query_data_available(&req->netConnection, &extra))
2348 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2350 LeaveCriticalSection( &req->read_section );
2352 TRACE( "returning %u\n", *available );
2353 return ERROR_SUCCESS;
2356 static const object_vtbl_t HTTPREQVtbl = {
2357 HTTPREQ_Destroy,
2358 HTTPREQ_CloseConnection,
2359 HTTPREQ_QueryOption,
2360 HTTPREQ_SetOption,
2361 HTTPREQ_ReadFile,
2362 HTTPREQ_ReadFileExA,
2363 HTTPREQ_ReadFileExW,
2364 HTTPREQ_WriteFile,
2365 HTTPREQ_QueryDataAvailable,
2366 NULL
2369 /***********************************************************************
2370 * HTTP_HttpOpenRequestW (internal)
2372 * Open a HTTP request handle
2374 * RETURNS
2375 * HINTERNET a HTTP request handle on success
2376 * NULL on failure
2379 HINTERNET WINAPI HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2380 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2381 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2382 DWORD dwFlags, DWORD_PTR dwContext)
2384 appinfo_t *hIC = NULL;
2385 http_request_t *lpwhr;
2386 LPWSTR lpszHostName = NULL;
2387 HINTERNET handle = NULL;
2388 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2389 DWORD len;
2391 TRACE("-->\n");
2393 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2394 hIC = lpwhs->lpAppInfo;
2396 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2397 if (NULL == lpwhr)
2399 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2400 goto lend;
2402 lpwhr->hdr.htype = WH_HHTTPREQ;
2403 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2404 lpwhr->hdr.dwFlags = dwFlags;
2405 lpwhr->hdr.dwContext = dwContext;
2406 lpwhr->hdr.refs = 1;
2407 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2408 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2409 lpwhr->dwContentLength = ~0u;
2410 InitializeCriticalSection( &lpwhr->read_section );
2412 WININET_AddRef( &lpwhs->hdr );
2413 lpwhr->lpHttpSession = lpwhs;
2414 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2416 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2417 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2418 if (NULL == lpszHostName)
2420 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2421 goto lend;
2424 handle = WININET_AllocHandle( &lpwhr->hdr );
2425 if (NULL == handle)
2427 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2428 goto lend;
2431 if (!NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE))
2433 InternetCloseHandle( handle );
2434 handle = NULL;
2435 goto lend;
2438 if (lpszObjectName && *lpszObjectName) {
2439 HRESULT rc;
2441 len = 0;
2442 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2443 if (rc != E_POINTER)
2444 len = strlenW(lpszObjectName)+1;
2445 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2446 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2447 URL_ESCAPE_SPACES_ONLY);
2448 if (rc != S_OK)
2450 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2451 strcpyW(lpwhr->lpszPath,lpszObjectName);
2453 }else {
2454 static const WCHAR slashW[] = {'/',0};
2456 lpwhr->lpszPath = heap_strdupW(slashW);
2459 if (lpszReferrer && *lpszReferrer)
2460 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2462 if (lpszAcceptTypes)
2464 int i;
2465 for (i = 0; lpszAcceptTypes[i]; i++)
2467 if (!*lpszAcceptTypes[i]) continue;
2468 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2469 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2470 HTTP_ADDHDR_FLAG_REQ |
2471 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2475 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2476 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2478 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2479 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2480 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2482 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2483 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2484 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2486 else
2487 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2488 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2490 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2491 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2492 INTERNET_DEFAULT_HTTPS_PORT :
2493 INTERNET_DEFAULT_HTTP_PORT);
2495 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2496 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2497 INTERNET_DEFAULT_HTTPS_PORT :
2498 INTERNET_DEFAULT_HTTP_PORT);
2500 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2501 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2503 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2504 INTERNET_STATUS_HANDLE_CREATED, &handle,
2505 sizeof(handle));
2507 lend:
2508 HeapFree(GetProcessHeap(), 0, lpszHostName);
2509 if( lpwhr )
2510 WININET_Release( &lpwhr->hdr );
2512 TRACE("<-- %p (%p)\n", handle, lpwhr);
2513 return handle;
2516 /* read any content returned by the server so that the connection can be
2517 * reused */
2518 static void HTTP_DrainContent(http_request_t *req)
2520 DWORD bytes_read;
2522 if (!NETCON_connected(&req->netConnection)) return;
2524 if (req->dwContentLength == -1)
2526 NETCON_close(&req->netConnection);
2527 return;
2532 char buffer[2048];
2533 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2534 return;
2535 } while (bytes_read);
2538 static const LPCWSTR header_lookup[] = {
2539 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2540 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2541 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2542 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2543 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2544 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2545 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2546 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2547 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2548 szDate, /* HTTP_QUERY_DATE = 9 */
2549 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2550 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2551 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2552 szURI, /* HTTP_QUERY_URI = 13 */
2553 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2554 NULL, /* HTTP_QUERY_COST = 15 */
2555 NULL, /* HTTP_QUERY_LINK = 16 */
2556 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2557 NULL, /* HTTP_QUERY_VERSION = 18 */
2558 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2559 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2560 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2561 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2562 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2563 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2564 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2565 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2566 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2567 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2568 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2569 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2570 NULL, /* HTTP_QUERY_FROM = 31 */
2571 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2572 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2573 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2574 szReferer, /* HTTP_QUERY_REFERER = 35 */
2575 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2576 szServer, /* HTTP_QUERY_SERVER = 37 */
2577 NULL, /* HTTP_TITLE = 38 */
2578 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2579 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2580 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2581 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2582 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2583 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2584 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2585 NULL, /* HTTP_QUERY_REFRESH = 46 */
2586 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2587 szAge, /* HTTP_QUERY_AGE = 48 */
2588 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2589 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2590 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2591 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2592 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2593 szETag, /* HTTP_QUERY_ETAG = 54 */
2594 hostW, /* HTTP_QUERY_HOST = 55 */
2595 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2596 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2597 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2598 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2599 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2600 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2601 szRange, /* HTTP_QUERY_RANGE = 62 */
2602 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2603 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2604 szVary, /* HTTP_QUERY_VARY = 65 */
2605 szVia, /* HTTP_QUERY_VIA = 66 */
2606 szWarning, /* HTTP_QUERY_WARNING = 67 */
2607 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2608 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2609 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2612 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2614 /***********************************************************************
2615 * HTTP_HttpQueryInfoW (internal)
2617 static BOOL HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2618 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2620 LPHTTPHEADERW lphttpHdr = NULL;
2621 BOOL bSuccess = FALSE;
2622 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2623 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2624 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2625 INT index = -1;
2627 /* Find requested header structure */
2628 switch (level)
2630 case HTTP_QUERY_CUSTOM:
2631 if (!lpBuffer) return FALSE;
2632 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2633 break;
2635 case HTTP_QUERY_CONTENT_LENGTH:
2636 if(lpwhr->gzip_stream) {
2637 INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
2638 return FALSE;
2641 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2642 requested_index,request_only);
2643 break;
2645 case HTTP_QUERY_RAW_HEADERS_CRLF:
2647 LPWSTR headers;
2648 DWORD len = 0;
2649 BOOL ret = FALSE;
2651 if (request_only)
2652 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2653 else
2654 headers = lpwhr->lpszRawHeaders;
2656 if (headers)
2657 len = strlenW(headers) * sizeof(WCHAR);
2659 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2661 len += sizeof(WCHAR);
2662 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2663 ret = FALSE;
2665 else if (lpBuffer)
2667 if (headers)
2668 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2669 else
2671 len = strlenW(szCrLf) * sizeof(WCHAR);
2672 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2674 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2675 ret = TRUE;
2677 *lpdwBufferLength = len;
2679 if (request_only)
2680 HeapFree(GetProcessHeap(), 0, headers);
2681 return ret;
2683 case HTTP_QUERY_RAW_HEADERS:
2685 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2686 DWORD i, size = 0;
2687 LPWSTR pszString = lpBuffer;
2689 for (i = 0; ppszRawHeaderLines[i]; i++)
2690 size += strlenW(ppszRawHeaderLines[i]) + 1;
2692 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2694 HTTP_FreeTokens(ppszRawHeaderLines);
2695 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2696 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2697 return FALSE;
2699 if (pszString)
2701 for (i = 0; ppszRawHeaderLines[i]; i++)
2703 DWORD len = strlenW(ppszRawHeaderLines[i]);
2704 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2705 pszString += len+1;
2707 *pszString = '\0';
2708 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2710 *lpdwBufferLength = size * sizeof(WCHAR);
2711 HTTP_FreeTokens(ppszRawHeaderLines);
2713 return TRUE;
2715 case HTTP_QUERY_STATUS_TEXT:
2716 if (lpwhr->lpszStatusText)
2718 DWORD len = strlenW(lpwhr->lpszStatusText);
2719 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2721 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2722 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2723 return FALSE;
2725 if (lpBuffer)
2727 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2728 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2730 *lpdwBufferLength = len * sizeof(WCHAR);
2731 return TRUE;
2733 break;
2734 case HTTP_QUERY_VERSION:
2735 if (lpwhr->lpszVersion)
2737 DWORD len = strlenW(lpwhr->lpszVersion);
2738 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2740 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2741 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2742 return FALSE;
2744 if (lpBuffer)
2746 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2747 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2749 *lpdwBufferLength = len * sizeof(WCHAR);
2750 return TRUE;
2752 break;
2753 case HTTP_QUERY_CONTENT_ENCODING:
2754 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2755 requested_index,request_only);
2756 break;
2757 default:
2758 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2760 if (level < LAST_TABLE_HEADER && header_lookup[level])
2761 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2762 requested_index,request_only);
2765 if (index >= 0)
2766 lphttpHdr = &lpwhr->pCustHeaders[index];
2768 /* Ensure header satisfies requested attributes */
2769 if (!lphttpHdr ||
2770 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2771 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2773 INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
2774 return bSuccess;
2777 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2779 /* coalesce value to requested type */
2780 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2782 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2783 TRACE(" returning number: %d\n", *(int *)lpBuffer);
2784 bSuccess = TRUE;
2786 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2788 time_t tmpTime;
2789 struct tm tmpTM;
2790 SYSTEMTIME *STHook;
2792 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2794 tmpTM = *gmtime(&tmpTime);
2795 STHook = (SYSTEMTIME *)lpBuffer;
2796 STHook->wDay = tmpTM.tm_mday;
2797 STHook->wHour = tmpTM.tm_hour;
2798 STHook->wMilliseconds = 0;
2799 STHook->wMinute = tmpTM.tm_min;
2800 STHook->wDayOfWeek = tmpTM.tm_wday;
2801 STHook->wMonth = tmpTM.tm_mon + 1;
2802 STHook->wSecond = tmpTM.tm_sec;
2803 STHook->wYear = tmpTM.tm_year;
2804 bSuccess = TRUE;
2806 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
2807 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
2808 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
2810 else if (lphttpHdr->lpszValue)
2812 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
2814 if (len > *lpdwBufferLength)
2816 *lpdwBufferLength = len;
2817 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2818 return bSuccess;
2820 if (lpBuffer)
2822 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
2823 TRACE(" returning string: %s\n", debugstr_w(lpBuffer));
2825 *lpdwBufferLength = len - sizeof(WCHAR);
2826 bSuccess = TRUE;
2828 return bSuccess;
2831 /***********************************************************************
2832 * HttpQueryInfoW (WININET.@)
2834 * Queries for information about an HTTP request
2836 * RETURNS
2837 * TRUE on success
2838 * FALSE on failure
2841 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
2842 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2844 BOOL bSuccess = FALSE;
2845 http_request_t *lpwhr;
2847 if (TRACE_ON(wininet)) {
2848 #define FE(x) { x, #x }
2849 static const wininet_flag_info query_flags[] = {
2850 FE(HTTP_QUERY_MIME_VERSION),
2851 FE(HTTP_QUERY_CONTENT_TYPE),
2852 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
2853 FE(HTTP_QUERY_CONTENT_ID),
2854 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
2855 FE(HTTP_QUERY_CONTENT_LENGTH),
2856 FE(HTTP_QUERY_CONTENT_LANGUAGE),
2857 FE(HTTP_QUERY_ALLOW),
2858 FE(HTTP_QUERY_PUBLIC),
2859 FE(HTTP_QUERY_DATE),
2860 FE(HTTP_QUERY_EXPIRES),
2861 FE(HTTP_QUERY_LAST_MODIFIED),
2862 FE(HTTP_QUERY_MESSAGE_ID),
2863 FE(HTTP_QUERY_URI),
2864 FE(HTTP_QUERY_DERIVED_FROM),
2865 FE(HTTP_QUERY_COST),
2866 FE(HTTP_QUERY_LINK),
2867 FE(HTTP_QUERY_PRAGMA),
2868 FE(HTTP_QUERY_VERSION),
2869 FE(HTTP_QUERY_STATUS_CODE),
2870 FE(HTTP_QUERY_STATUS_TEXT),
2871 FE(HTTP_QUERY_RAW_HEADERS),
2872 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
2873 FE(HTTP_QUERY_CONNECTION),
2874 FE(HTTP_QUERY_ACCEPT),
2875 FE(HTTP_QUERY_ACCEPT_CHARSET),
2876 FE(HTTP_QUERY_ACCEPT_ENCODING),
2877 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
2878 FE(HTTP_QUERY_AUTHORIZATION),
2879 FE(HTTP_QUERY_CONTENT_ENCODING),
2880 FE(HTTP_QUERY_FORWARDED),
2881 FE(HTTP_QUERY_FROM),
2882 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
2883 FE(HTTP_QUERY_LOCATION),
2884 FE(HTTP_QUERY_ORIG_URI),
2885 FE(HTTP_QUERY_REFERER),
2886 FE(HTTP_QUERY_RETRY_AFTER),
2887 FE(HTTP_QUERY_SERVER),
2888 FE(HTTP_QUERY_TITLE),
2889 FE(HTTP_QUERY_USER_AGENT),
2890 FE(HTTP_QUERY_WWW_AUTHENTICATE),
2891 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
2892 FE(HTTP_QUERY_ACCEPT_RANGES),
2893 FE(HTTP_QUERY_SET_COOKIE),
2894 FE(HTTP_QUERY_COOKIE),
2895 FE(HTTP_QUERY_REQUEST_METHOD),
2896 FE(HTTP_QUERY_REFRESH),
2897 FE(HTTP_QUERY_CONTENT_DISPOSITION),
2898 FE(HTTP_QUERY_AGE),
2899 FE(HTTP_QUERY_CACHE_CONTROL),
2900 FE(HTTP_QUERY_CONTENT_BASE),
2901 FE(HTTP_QUERY_CONTENT_LOCATION),
2902 FE(HTTP_QUERY_CONTENT_MD5),
2903 FE(HTTP_QUERY_CONTENT_RANGE),
2904 FE(HTTP_QUERY_ETAG),
2905 FE(HTTP_QUERY_HOST),
2906 FE(HTTP_QUERY_IF_MATCH),
2907 FE(HTTP_QUERY_IF_NONE_MATCH),
2908 FE(HTTP_QUERY_IF_RANGE),
2909 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
2910 FE(HTTP_QUERY_MAX_FORWARDS),
2911 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
2912 FE(HTTP_QUERY_RANGE),
2913 FE(HTTP_QUERY_TRANSFER_ENCODING),
2914 FE(HTTP_QUERY_UPGRADE),
2915 FE(HTTP_QUERY_VARY),
2916 FE(HTTP_QUERY_VIA),
2917 FE(HTTP_QUERY_WARNING),
2918 FE(HTTP_QUERY_CUSTOM)
2920 static const wininet_flag_info modifier_flags[] = {
2921 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
2922 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
2923 FE(HTTP_QUERY_FLAG_NUMBER),
2924 FE(HTTP_QUERY_FLAG_COALESCE)
2926 #undef FE
2927 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
2928 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
2929 DWORD i;
2931 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, dwInfoLevel);
2932 TRACE(" Attribute:");
2933 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
2934 if (query_flags[i].val == info) {
2935 TRACE(" %s", query_flags[i].name);
2936 break;
2939 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
2940 TRACE(" Unknown (%08x)", info);
2943 TRACE(" Modifier:");
2944 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
2945 if (modifier_flags[i].val & info_mod) {
2946 TRACE(" %s", modifier_flags[i].name);
2947 info_mod &= ~ modifier_flags[i].val;
2951 if (info_mod) {
2952 TRACE(" Unknown (%08x)", info_mod);
2954 TRACE("\n");
2957 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
2958 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
2960 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2961 goto lend;
2964 if (lpBuffer == NULL)
2965 *lpdwBufferLength = 0;
2966 bSuccess = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
2967 lpBuffer, lpdwBufferLength, lpdwIndex);
2969 lend:
2970 if( lpwhr )
2971 WININET_Release( &lpwhr->hdr );
2973 TRACE("%d <--\n", bSuccess);
2974 return bSuccess;
2977 /***********************************************************************
2978 * HttpQueryInfoA (WININET.@)
2980 * Queries for information about an HTTP request
2982 * RETURNS
2983 * TRUE on success
2984 * FALSE on failure
2987 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
2988 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2990 BOOL result;
2991 DWORD len;
2992 WCHAR* bufferW;
2994 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
2995 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
2997 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
2998 lpdwBufferLength, lpdwIndex );
3001 if (lpBuffer)
3003 DWORD alloclen;
3004 len = (*lpdwBufferLength)*sizeof(WCHAR);
3005 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3007 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3008 if (alloclen < len)
3009 alloclen = len;
3011 else
3012 alloclen = len;
3013 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3014 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3015 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3016 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3017 } else
3019 bufferW = NULL;
3020 len = 0;
3023 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3024 &len, lpdwIndex );
3025 if( result )
3027 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3028 lpBuffer, *lpdwBufferLength, NULL, NULL );
3029 *lpdwBufferLength = len - 1;
3031 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3033 else
3034 /* since the strings being returned from HttpQueryInfoW should be
3035 * only ASCII characters, it is reasonable to assume that all of
3036 * the Unicode characters can be reduced to a single byte */
3037 *lpdwBufferLength = len / sizeof(WCHAR);
3039 HeapFree(GetProcessHeap(), 0, bufferW );
3041 return result;
3044 /***********************************************************************
3045 * HttpSendRequestExA (WININET.@)
3047 * Sends the specified request to the HTTP server and allows chunked
3048 * transfers.
3050 * RETURNS
3051 * Success: TRUE
3052 * Failure: FALSE, call GetLastError() for more information.
3054 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3055 LPINTERNET_BUFFERSA lpBuffersIn,
3056 LPINTERNET_BUFFERSA lpBuffersOut,
3057 DWORD dwFlags, DWORD_PTR dwContext)
3059 INTERNET_BUFFERSW BuffersInW;
3060 BOOL rc = FALSE;
3061 DWORD headerlen;
3062 LPWSTR header = NULL;
3064 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3065 lpBuffersOut, dwFlags, dwContext);
3067 if (lpBuffersIn)
3069 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3070 if (lpBuffersIn->lpcszHeader)
3072 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3073 lpBuffersIn->dwHeadersLength,0,0);
3074 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3075 if (!(BuffersInW.lpcszHeader = header))
3077 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3078 return FALSE;
3080 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3081 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3082 header, headerlen);
3084 else
3085 BuffersInW.lpcszHeader = NULL;
3086 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3087 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3088 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3089 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3090 BuffersInW.Next = NULL;
3093 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3095 HeapFree(GetProcessHeap(),0,header);
3097 return rc;
3100 /***********************************************************************
3101 * HttpSendRequestExW (WININET.@)
3103 * Sends the specified request to the HTTP server and allows chunked
3104 * transfers
3106 * RETURNS
3107 * Success: TRUE
3108 * Failure: FALSE, call GetLastError() for more information.
3110 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3111 LPINTERNET_BUFFERSW lpBuffersIn,
3112 LPINTERNET_BUFFERSW lpBuffersOut,
3113 DWORD dwFlags, DWORD_PTR dwContext)
3115 BOOL ret = FALSE;
3116 http_request_t *lpwhr;
3117 http_session_t *lpwhs;
3118 appinfo_t *hIC;
3120 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3121 lpBuffersOut, dwFlags, dwContext);
3123 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3125 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3127 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3128 goto lend;
3131 lpwhs = lpwhr->lpHttpSession;
3132 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3133 hIC = lpwhs->lpAppInfo;
3134 assert(hIC->hdr.htype == WH_HINIT);
3136 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3138 WORKREQUEST workRequest;
3139 struct WORKREQ_HTTPSENDREQUESTW *req;
3141 workRequest.asyncproc = AsyncHttpSendRequestProc;
3142 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3143 req = &workRequest.u.HttpSendRequestW;
3144 if (lpBuffersIn)
3146 /* FIXME: this should use dwHeadersLength or may not be necessary at all */
3147 req->lpszHeader = heap_strdupW(lpBuffersIn->lpcszHeader);
3148 req->dwHeaderLength = lpBuffersIn->dwHeadersLength;
3149 req->lpOptional = lpBuffersIn->lpvBuffer;
3150 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3151 req->dwContentLength = lpBuffersIn->dwBufferTotal;
3153 else
3155 req->lpszHeader = NULL;
3156 req->dwHeaderLength = 0;
3157 req->lpOptional = NULL;
3158 req->dwOptionalLength = 0;
3159 req->dwContentLength = 0;
3162 req->bEndRequest = FALSE;
3164 INTERNET_AsyncCall(&workRequest);
3166 * This is from windows.
3168 INTERNET_SetLastError(ERROR_IO_PENDING);
3170 else
3172 if (lpBuffersIn)
3173 ret = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3174 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
3175 lpBuffersIn->dwBufferTotal, FALSE);
3176 else
3177 ret = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
3180 lend:
3181 if ( lpwhr )
3182 WININET_Release( &lpwhr->hdr );
3184 TRACE("<---\n");
3185 return ret;
3188 /***********************************************************************
3189 * HttpSendRequestW (WININET.@)
3191 * Sends the specified request to the HTTP server
3193 * RETURNS
3194 * TRUE on success
3195 * FALSE on failure
3198 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
3199 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3201 http_request_t *lpwhr;
3202 http_session_t *lpwhs = NULL;
3203 appinfo_t *hIC = NULL;
3204 BOOL r;
3206 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
3207 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
3209 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3210 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3212 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3213 r = FALSE;
3214 goto lend;
3217 lpwhs = lpwhr->lpHttpSession;
3218 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
3220 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3221 r = FALSE;
3222 goto lend;
3225 hIC = lpwhs->lpAppInfo;
3226 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
3228 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3229 r = FALSE;
3230 goto lend;
3233 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3235 WORKREQUEST workRequest;
3236 struct WORKREQ_HTTPSENDREQUESTW *req;
3238 workRequest.asyncproc = AsyncHttpSendRequestProc;
3239 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3240 req = &workRequest.u.HttpSendRequestW;
3241 if (lpszHeaders)
3243 DWORD size;
3245 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
3246 else size = dwHeaderLength * sizeof(WCHAR);
3248 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
3249 memcpy(req->lpszHeader, lpszHeaders, size);
3251 else
3252 req->lpszHeader = 0;
3253 req->dwHeaderLength = dwHeaderLength;
3254 req->lpOptional = lpOptional;
3255 req->dwOptionalLength = dwOptionalLength;
3256 req->dwContentLength = dwOptionalLength;
3257 req->bEndRequest = TRUE;
3259 INTERNET_AsyncCall(&workRequest);
3261 * This is from windows.
3263 INTERNET_SetLastError(ERROR_IO_PENDING);
3264 r = FALSE;
3266 else
3268 r = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
3269 dwHeaderLength, lpOptional, dwOptionalLength,
3270 dwOptionalLength, TRUE);
3272 lend:
3273 if( lpwhr )
3274 WININET_Release( &lpwhr->hdr );
3275 return r;
3278 /***********************************************************************
3279 * HttpSendRequestA (WININET.@)
3281 * Sends the specified request to the HTTP server
3283 * RETURNS
3284 * TRUE on success
3285 * FALSE on failure
3288 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
3289 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3291 BOOL result;
3292 LPWSTR szHeaders=NULL;
3293 DWORD nLen=dwHeaderLength;
3294 if(lpszHeaders!=NULL)
3296 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
3297 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
3298 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
3300 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
3301 HeapFree(GetProcessHeap(),0,szHeaders);
3302 return result;
3305 /***********************************************************************
3306 * HTTP_GetRedirectURL (internal)
3308 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3310 static WCHAR szHttp[] = {'h','t','t','p',0};
3311 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3312 http_session_t *lpwhs = lpwhr->lpHttpSession;
3313 URL_COMPONENTSW urlComponents;
3314 DWORD url_length = 0;
3315 LPWSTR orig_url;
3316 LPWSTR combined_url;
3318 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3319 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3320 urlComponents.dwSchemeLength = 0;
3321 urlComponents.lpszHostName = lpwhs->lpszHostName;
3322 urlComponents.dwHostNameLength = 0;
3323 urlComponents.nPort = lpwhs->nHostPort;
3324 urlComponents.lpszUserName = lpwhs->lpszUserName;
3325 urlComponents.dwUserNameLength = 0;
3326 urlComponents.lpszPassword = NULL;
3327 urlComponents.dwPasswordLength = 0;
3328 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3329 urlComponents.dwUrlPathLength = 0;
3330 urlComponents.lpszExtraInfo = NULL;
3331 urlComponents.dwExtraInfoLength = 0;
3333 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3334 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3335 return NULL;
3337 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3339 /* convert from bytes to characters */
3340 url_length = url_length / sizeof(WCHAR) - 1;
3341 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3343 HeapFree(GetProcessHeap(), 0, orig_url);
3344 return NULL;
3347 url_length = 0;
3348 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3349 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3351 HeapFree(GetProcessHeap(), 0, orig_url);
3352 return NULL;
3354 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3356 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3358 HeapFree(GetProcessHeap(), 0, orig_url);
3359 HeapFree(GetProcessHeap(), 0, combined_url);
3360 return NULL;
3362 HeapFree(GetProcessHeap(), 0, orig_url);
3363 return combined_url;
3367 /***********************************************************************
3368 * HTTP_HandleRedirect (internal)
3370 static BOOL HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3372 http_session_t *lpwhs = lpwhr->lpHttpSession;
3373 appinfo_t *hIC = lpwhs->lpAppInfo;
3374 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3375 WCHAR path[INTERNET_MAX_URL_LENGTH];
3376 int index;
3378 if(lpszUrl[0]=='/')
3380 /* if it's an absolute path, keep the same session info */
3381 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3383 else
3385 URL_COMPONENTSW urlComponents;
3386 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3387 static WCHAR szHttp[] = {'h','t','t','p',0};
3388 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3390 userName[0] = 0;
3391 hostName[0] = 0;
3392 protocol[0] = 0;
3394 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3395 urlComponents.lpszScheme = protocol;
3396 urlComponents.dwSchemeLength = 32;
3397 urlComponents.lpszHostName = hostName;
3398 urlComponents.dwHostNameLength = MAXHOSTNAME;
3399 urlComponents.lpszUserName = userName;
3400 urlComponents.dwUserNameLength = 1024;
3401 urlComponents.lpszPassword = NULL;
3402 urlComponents.dwPasswordLength = 0;
3403 urlComponents.lpszUrlPath = path;
3404 urlComponents.dwUrlPathLength = 2048;
3405 urlComponents.lpszExtraInfo = NULL;
3406 urlComponents.dwExtraInfoLength = 0;
3407 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3408 return FALSE;
3410 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3411 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3413 TRACE("redirect from secure page to non-secure page\n");
3414 /* FIXME: warn about from secure redirect to non-secure page */
3415 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3417 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3418 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3420 TRACE("redirect from non-secure page to secure page\n");
3421 /* FIXME: notify about redirect to secure page */
3422 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3425 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3427 if (lstrlenW(protocol)>4) /*https*/
3428 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3429 else /*http*/
3430 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3433 #if 0
3435 * This upsets redirects to binary files on sourceforge.net
3436 * and gives an html page instead of the target file
3437 * Examination of the HTTP request sent by native wininet.dll
3438 * reveals that it doesn't send a referrer in that case.
3439 * Maybe there's a flag that enables this, or maybe a referrer
3440 * shouldn't be added in case of a redirect.
3443 /* consider the current host as the referrer */
3444 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3445 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3446 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3447 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3448 #endif
3450 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3451 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3452 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3454 int len;
3455 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3456 len = lstrlenW(hostName);
3457 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3458 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3459 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3461 else
3462 lpwhs->lpszHostName = heap_strdupW(hostName);
3464 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3466 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3467 lpwhs->lpszUserName = NULL;
3468 if (userName[0])
3469 lpwhs->lpszUserName = heap_strdupW(userName);
3471 if (!using_proxy)
3473 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3475 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3476 lpwhs->lpszServerName = heap_strdupW(hostName);
3477 lpwhs->nServerPort = urlComponents.nPort;
3479 NETCON_close(&lpwhr->netConnection);
3480 if (!HTTP_ResolveName(lpwhr)) return FALSE;
3481 if (!NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)) return FALSE;
3482 lpwhr->read_pos = lpwhr->read_size = 0;
3483 lpwhr->read_chunked = FALSE;
3486 else
3487 TRACE("Redirect through proxy\n");
3490 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3491 lpwhr->lpszPath=NULL;
3492 if (*path)
3494 DWORD needed = 0;
3495 HRESULT rc;
3497 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3498 if (rc != E_POINTER)
3499 needed = strlenW(path)+1;
3500 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3501 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3502 URL_ESCAPE_SPACES_ONLY);
3503 if (rc != S_OK)
3505 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3506 strcpyW(lpwhr->lpszPath,path);
3510 /* Remove custom content-type/length headers on redirects. */
3511 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3512 if (0 <= index)
3513 HTTP_DeleteCustomHeader(lpwhr, index);
3514 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3515 if (0 <= index)
3516 HTTP_DeleteCustomHeader(lpwhr, index);
3518 return TRUE;
3521 /***********************************************************************
3522 * HTTP_build_req (internal)
3524 * concatenate all the strings in the request together
3526 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3528 LPCWSTR *t;
3529 LPWSTR str;
3531 for( t = list; *t ; t++ )
3532 len += strlenW( *t );
3533 len++;
3535 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3536 *str = 0;
3538 for( t = list; *t ; t++ )
3539 strcatW( str, *t );
3541 return str;
3544 static BOOL HTTP_SecureProxyConnect(http_request_t *lpwhr)
3546 LPWSTR lpszPath;
3547 LPWSTR requestString;
3548 INT len;
3549 INT cnt;
3550 INT responseLen;
3551 char *ascii_req;
3552 BOOL ret;
3553 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3554 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3555 http_session_t *lpwhs = lpwhr->lpHttpSession;
3557 TRACE("\n");
3559 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3560 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3561 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3562 HeapFree( GetProcessHeap(), 0, lpszPath );
3564 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3565 NULL, 0, NULL, NULL );
3566 len--; /* the nul terminator isn't needed */
3567 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3568 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3569 ascii_req, len, NULL, NULL );
3570 HeapFree( GetProcessHeap(), 0, requestString );
3572 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3574 ret = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3575 HeapFree( GetProcessHeap(), 0, ascii_req );
3576 if (!ret || cnt < 0)
3577 return FALSE;
3579 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3580 if (!responseLen)
3581 return FALSE;
3583 return TRUE;
3586 static void HTTP_InsertCookies(http_request_t *lpwhr)
3588 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3589 LPWSTR lpszCookies, lpszUrl = NULL;
3590 DWORD nCookieSize, size;
3591 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3593 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3594 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3595 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3597 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3599 int cnt = 0;
3600 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3602 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3603 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3605 cnt += sprintfW(lpszCookies, szCookie);
3606 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3607 strcatW(lpszCookies, szCrLf);
3609 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3610 HeapFree(GetProcessHeap(), 0, lpszCookies);
3613 HeapFree(GetProcessHeap(), 0, lpszUrl);
3616 /***********************************************************************
3617 * HTTP_HttpSendRequestW (internal)
3619 * Sends the specified request to the HTTP server
3621 * RETURNS
3622 * TRUE on success
3623 * FALSE on failure
3626 BOOL WINAPI HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3627 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3628 DWORD dwContentLength, BOOL bEndRequest)
3630 INT cnt;
3631 BOOL bSuccess = FALSE, redirected = FALSE;
3632 LPWSTR requestString = NULL;
3633 INT responseLen;
3634 BOOL loop_next;
3635 INTERNET_ASYNC_RESULT iar;
3636 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3637 static const WCHAR szContentLength[] =
3638 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3639 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3641 TRACE("--> %p\n", lpwhr);
3643 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3645 /* if the verb is NULL default to GET */
3646 if (!lpwhr->lpszVerb)
3647 lpwhr->lpszVerb = heap_strdupW(szGET);
3649 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3651 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3652 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3653 lpwhr->dwBytesToWrite = dwContentLength;
3655 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3657 WCHAR *agent_header;
3658 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3659 int len;
3661 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3662 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3663 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3665 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3666 HeapFree(GetProcessHeap(), 0, agent_header);
3668 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3670 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3671 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3673 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3675 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3676 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3677 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3682 DWORD len;
3683 char *ascii_req;
3685 loop_next = FALSE;
3687 /* like native, just in case the caller forgot to call InternetReadFile
3688 * for all the data */
3689 HTTP_DrainContent(lpwhr);
3690 lpwhr->dwContentRead = 0;
3692 if (TRACE_ON(wininet))
3694 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3695 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3698 HTTP_FixURL(lpwhr);
3699 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3701 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3703 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3704 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3706 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3707 HTTP_InsertCookies(lpwhr);
3709 /* add the headers the caller supplied */
3710 if( lpszHeaders && dwHeaderLength )
3712 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3713 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3716 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3718 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3719 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3720 HeapFree(GetProcessHeap(), 0, url);
3722 else
3723 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3726 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3728 /* Send the request and store the results */
3729 if (!HTTP_OpenConnection(lpwhr))
3730 goto lend;
3732 /* send the request as ASCII, tack on the optional data */
3733 if (!lpOptional || redirected)
3734 dwOptionalLength = 0;
3735 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3736 NULL, 0, NULL, NULL );
3737 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3738 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3739 ascii_req, len, NULL, NULL );
3740 if( lpOptional )
3741 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3742 len = (len + dwOptionalLength - 1);
3743 ascii_req[len] = 0;
3744 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3746 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3747 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3749 NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3750 HeapFree( GetProcessHeap(), 0, ascii_req );
3752 lpwhr->dwBytesWritten = dwOptionalLength;
3754 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3755 INTERNET_STATUS_REQUEST_SENT,
3756 &len, sizeof(DWORD));
3758 if (bEndRequest)
3760 DWORD dwBufferSize;
3761 DWORD dwStatusCode;
3763 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3764 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3766 if (cnt < 0)
3767 goto lend;
3769 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3770 if (responseLen)
3771 bSuccess = TRUE;
3773 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3774 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3775 sizeof(DWORD));
3777 HTTP_ProcessCookies(lpwhr);
3779 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3781 dwBufferSize = sizeof(dwStatusCode);
3782 if (!HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3783 &dwStatusCode,&dwBufferSize,NULL))
3784 dwStatusCode = 0;
3786 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && bSuccess)
3788 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3789 dwBufferSize=sizeof(szNewLocation);
3790 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
3791 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL))
3793 /* redirects are always GETs */
3794 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3795 lpwhr->lpszVerb = heap_strdupW(szGET);
3797 HTTP_DrainContent(lpwhr);
3798 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3800 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3801 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3802 bSuccess = HTTP_HandleRedirect(lpwhr, new_url);
3803 if (bSuccess)
3805 HeapFree(GetProcessHeap(), 0, requestString);
3806 loop_next = TRUE;
3808 HeapFree( GetProcessHeap(), 0, new_url );
3810 redirected = TRUE;
3813 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && bSuccess)
3815 WCHAR szAuthValue[2048];
3816 dwBufferSize=2048;
3817 if (dwStatusCode == HTTP_STATUS_DENIED)
3819 DWORD dwIndex = 0;
3820 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
3822 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3823 &lpwhr->pAuthInfo,
3824 lpwhr->lpHttpSession->lpszUserName,
3825 lpwhr->lpHttpSession->lpszPassword))
3827 loop_next = TRUE;
3828 break;
3832 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3834 DWORD dwIndex = 0;
3835 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
3837 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3838 &lpwhr->pProxyAuthInfo,
3839 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3840 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword))
3842 loop_next = TRUE;
3843 break;
3849 else
3850 bSuccess = TRUE;
3852 while (loop_next);
3854 if(bSuccess) {
3855 WCHAR url[INTERNET_MAX_URL_LENGTH];
3856 WCHAR cacheFileName[MAX_PATH+1];
3857 BOOL b;
3859 b = HTTP_GetRequestURL(lpwhr, url);
3860 if(!b) {
3861 WARN("Could not get URL\n");
3862 goto lend;
3865 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3866 if(b) {
3867 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3868 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3869 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3870 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3871 WARN("Could not create file: %u\n", GetLastError());
3872 lpwhr->hCacheFile = NULL;
3874 }else {
3875 WARN("Could not create cache entry: %08x\n", GetLastError());
3879 lend:
3881 HeapFree(GetProcessHeap(), 0, requestString);
3883 /* TODO: send notification for P3P header */
3885 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3887 if (bSuccess)
3889 if (lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite) HTTP_ReceiveRequestData(lpwhr, TRUE);
3890 else
3892 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3893 iar.dwError = 0;
3895 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3896 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3897 sizeof(INTERNET_ASYNC_RESULT));
3900 else
3902 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3903 iar.dwError = INTERNET_GetLastError();
3905 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3906 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3907 sizeof(INTERNET_ASYNC_RESULT));
3911 TRACE("<--\n");
3912 if (bSuccess) INTERNET_SetLastError(ERROR_SUCCESS);
3913 return bSuccess;
3916 /***********************************************************************
3917 * HTTPSESSION_Destroy (internal)
3919 * Deallocate session handle
3922 static void HTTPSESSION_Destroy(object_header_t *hdr)
3924 http_session_t *lpwhs = (http_session_t*) hdr;
3926 TRACE("%p\n", lpwhs);
3928 WININET_Release(&lpwhs->lpAppInfo->hdr);
3930 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3931 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3932 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
3933 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3934 HeapFree(GetProcessHeap(), 0, lpwhs);
3937 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3939 switch(option) {
3940 case INTERNET_OPTION_HANDLE_TYPE:
3941 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3943 if (*size < sizeof(ULONG))
3944 return ERROR_INSUFFICIENT_BUFFER;
3946 *size = sizeof(DWORD);
3947 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
3948 return ERROR_SUCCESS;
3951 return INET_QueryOption(option, buffer, size, unicode);
3954 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
3956 http_session_t *ses = (http_session_t*)hdr;
3958 switch(option) {
3959 case INTERNET_OPTION_USERNAME:
3961 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
3962 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
3963 return ERROR_SUCCESS;
3965 case INTERNET_OPTION_PASSWORD:
3967 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
3968 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
3969 return ERROR_SUCCESS;
3971 default: break;
3974 return ERROR_INTERNET_INVALID_OPTION;
3977 static const object_vtbl_t HTTPSESSIONVtbl = {
3978 HTTPSESSION_Destroy,
3979 NULL,
3980 HTTPSESSION_QueryOption,
3981 HTTPSESSION_SetOption,
3982 NULL,
3983 NULL,
3984 NULL,
3985 NULL,
3986 NULL
3990 /***********************************************************************
3991 * HTTP_Connect (internal)
3993 * Create http session handle
3995 * RETURNS
3996 * HINTERNET a session handle on success
3997 * NULL on failure
4000 HINTERNET HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4001 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4002 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4003 DWORD dwInternalFlags)
4005 http_session_t *lpwhs = NULL;
4006 HINTERNET handle = NULL;
4008 TRACE("-->\n");
4010 if (!lpszServerName || !lpszServerName[0])
4012 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4013 goto lerror;
4016 assert( hIC->hdr.htype == WH_HINIT );
4018 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4019 if (NULL == lpwhs)
4021 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4022 goto lerror;
4026 * According to my tests. The name is not resolved until a request is sent
4029 lpwhs->hdr.htype = WH_HHTTPSESSION;
4030 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4031 lpwhs->hdr.dwFlags = dwFlags;
4032 lpwhs->hdr.dwContext = dwContext;
4033 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4034 lpwhs->hdr.refs = 1;
4035 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4037 WININET_AddRef( &hIC->hdr );
4038 lpwhs->lpAppInfo = hIC;
4039 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4041 handle = WININET_AllocHandle( &lpwhs->hdr );
4042 if (NULL == handle)
4044 ERR("Failed to alloc handle\n");
4045 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4046 goto lerror;
4049 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4050 if(strchrW(hIC->lpszProxy, ' '))
4051 FIXME("Several proxies not implemented.\n");
4052 if(hIC->lpszProxyBypass)
4053 FIXME("Proxy bypass is ignored.\n");
4055 if (lpszServerName && lpszServerName[0])
4057 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4058 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4060 if (lpszUserName && lpszUserName[0])
4061 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4062 if (lpszPassword && lpszPassword[0])
4063 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4064 lpwhs->nServerPort = nServerPort;
4065 lpwhs->nHostPort = nServerPort;
4067 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4068 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4070 INTERNET_SendCallback(&hIC->hdr, dwContext,
4071 INTERNET_STATUS_HANDLE_CREATED, &handle,
4072 sizeof(handle));
4075 lerror:
4076 if( lpwhs )
4077 WININET_Release( &lpwhs->hdr );
4080 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4081 * windows
4084 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4085 return handle;
4089 /***********************************************************************
4090 * HTTP_OpenConnection (internal)
4092 * Connect to a web server
4094 * RETURNS
4096 * TRUE on success
4097 * FALSE on failure
4099 static BOOL HTTP_OpenConnection(http_request_t *lpwhr)
4101 BOOL bSuccess = FALSE;
4102 http_session_t *lpwhs;
4103 appinfo_t *hIC = NULL;
4104 char szaddr[INET6_ADDRSTRLEN];
4105 const void *addr;
4107 TRACE("-->\n");
4110 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4112 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4113 goto lend;
4116 if (NETCON_connected(&lpwhr->netConnection))
4118 bSuccess = TRUE;
4119 goto lend;
4121 if (!HTTP_ResolveName(lpwhr)) goto lend;
4123 lpwhs = lpwhr->lpHttpSession;
4125 hIC = lpwhs->lpAppInfo;
4126 switch (lpwhs->socketAddress.ss_family)
4128 case AF_INET:
4129 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4130 break;
4131 case AF_INET6:
4132 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4133 break;
4134 default:
4135 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4136 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
4137 return FALSE;
4139 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4140 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4141 INTERNET_STATUS_CONNECTING_TO_SERVER,
4142 szaddr,
4143 strlen(szaddr)+1);
4145 if (!NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family,
4146 SOCK_STREAM, 0))
4148 WARN("Socket creation failed: %u\n", INTERNET_GetLastError());
4149 goto lend;
4152 if (!NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4153 lpwhs->sa_len))
4154 goto lend;
4156 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4158 /* Note: we differ from Microsoft's WinINet here. they seem to have
4159 * a bug that causes no status callbacks to be sent when starting
4160 * a tunnel to a proxy server using the CONNECT verb. i believe our
4161 * behaviour to be more correct and to not cause any incompatibilities
4162 * because using a secure connection through a proxy server is a rare
4163 * case that would be hard for anyone to depend on */
4164 if (hIC->lpszProxy && !HTTP_SecureProxyConnect(lpwhr))
4165 goto lend;
4167 if (!NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName))
4169 WARN("Couldn't connect securely to host\n");
4170 goto lend;
4174 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4175 INTERNET_STATUS_CONNECTED_TO_SERVER,
4176 szaddr, strlen(szaddr)+1);
4178 bSuccess = TRUE;
4180 lend:
4181 lpwhr->read_pos = lpwhr->read_size = 0;
4182 lpwhr->read_chunked = FALSE;
4184 TRACE("%d <--\n", bSuccess);
4185 return bSuccess;
4189 /***********************************************************************
4190 * HTTP_clear_response_headers (internal)
4192 * clear out any old response headers
4194 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4196 DWORD i;
4198 for( i=0; i<lpwhr->nCustHeaders; i++)
4200 if( !lpwhr->pCustHeaders[i].lpszField )
4201 continue;
4202 if( !lpwhr->pCustHeaders[i].lpszValue )
4203 continue;
4204 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4205 continue;
4206 HTTP_DeleteCustomHeader( lpwhr, i );
4207 i--;
4211 /***********************************************************************
4212 * HTTP_GetResponseHeaders (internal)
4214 * Read server response
4216 * RETURNS
4218 * TRUE on success
4219 * FALSE on error
4221 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4223 INT cbreaks = 0;
4224 WCHAR buffer[MAX_REPLY_LEN];
4225 DWORD buflen = MAX_REPLY_LEN;
4226 BOOL bSuccess = FALSE;
4227 INT rc = 0;
4228 char bufferA[MAX_REPLY_LEN];
4229 LPWSTR status_code = NULL, status_text = NULL;
4230 DWORD cchMaxRawHeaders = 1024;
4231 LPWSTR lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4232 LPWSTR temp;
4233 DWORD cchRawHeaders = 0;
4234 BOOL codeHundred = FALSE;
4236 TRACE("-->\n");
4238 /* clear old response headers (eg. from a redirect response) */
4239 if (clear) HTTP_clear_response_headers( lpwhr );
4241 if (!NETCON_connected(&lpwhr->netConnection))
4242 goto lend;
4244 do {
4245 static const WCHAR szHundred[] = {'1','0','0',0};
4247 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4249 buflen = MAX_REPLY_LEN;
4250 if (!read_line(lpwhr, bufferA, &buflen))
4251 goto lend;
4252 rc += buflen;
4253 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4254 /* check is this a status code line? */
4255 if (!strncmpW(buffer, g_szHttp1_0, 4))
4257 /* split the version from the status code */
4258 status_code = strchrW( buffer, ' ' );
4259 if( !status_code )
4260 goto lend;
4261 *status_code++=0;
4263 /* split the status code from the status text */
4264 status_text = strchrW( status_code, ' ' );
4265 if( !status_text )
4266 goto lend;
4267 *status_text++=0;
4269 TRACE("version [%s] status code [%s] status text [%s]\n",
4270 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4272 codeHundred = (!strcmpW(status_code, szHundred));
4274 else if (!codeHundred)
4276 FIXME("Non status line at head of response (%s)\n",debugstr_w(buffer));
4277 continue;
4279 } while (codeHundred);
4281 /* Add status code */
4282 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4283 HTTP_ADDHDR_FLAG_REPLACE);
4285 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4286 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4288 lpwhr->lpszVersion = heap_strdupW(buffer);
4289 lpwhr->lpszStatusText = heap_strdupW(status_text);
4291 /* Restore the spaces */
4292 *(status_code-1) = ' ';
4293 *(status_text-1) = ' ';
4295 /* regenerate raw headers */
4296 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4297 cchMaxRawHeaders *= 2;
4298 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4299 if (temp == NULL) goto lend;
4300 lpszRawHeaders = temp;
4301 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4302 cchRawHeaders += (buflen-1);
4303 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4304 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4305 lpszRawHeaders[cchRawHeaders] = '\0';
4307 /* Parse each response line */
4310 buflen = MAX_REPLY_LEN;
4311 if (read_line(lpwhr, bufferA, &buflen))
4313 LPWSTR * pFieldAndValue;
4315 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4317 if (!bufferA[0]) break;
4318 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4320 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4321 if (pFieldAndValue)
4323 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4324 cchMaxRawHeaders *= 2;
4325 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4326 if (temp == NULL) goto lend;
4327 lpszRawHeaders = temp;
4328 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4329 cchRawHeaders += (buflen-1);
4330 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4331 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4332 lpszRawHeaders[cchRawHeaders] = '\0';
4334 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4335 HTTP_ADDREQ_FLAG_ADD );
4337 HTTP_FreeTokens(pFieldAndValue);
4340 else
4342 cbreaks++;
4343 if (cbreaks >= 2)
4344 break;
4346 }while(1);
4348 /* make sure the response header is terminated with an empty line. Some apps really
4349 truly care about that empty line being there for some reason. Just add it to the
4350 header. */
4351 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4353 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4354 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4355 if (temp == NULL) goto lend;
4356 lpszRawHeaders = temp;
4359 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4361 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4362 lpwhr->lpszRawHeaders = lpszRawHeaders;
4363 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4364 bSuccess = TRUE;
4366 lend:
4368 TRACE("<--\n");
4369 if (bSuccess)
4370 return rc;
4371 else
4373 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4374 return 0;
4379 static void strip_spaces(LPWSTR start)
4381 LPWSTR str = start;
4382 LPWSTR end;
4384 while (*str == ' ' && *str != '\0')
4385 str++;
4387 if (str != start)
4388 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
4390 end = start + strlenW(start) - 1;
4391 while (end >= start && *end == ' ')
4393 *end = '\0';
4394 end--;
4399 /***********************************************************************
4400 * HTTP_InterpretHttpHeader (internal)
4402 * Parse server response
4404 * RETURNS
4406 * Pointer to array of field, value, NULL on success.
4407 * NULL on error.
4409 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4411 LPWSTR * pTokenPair;
4412 LPWSTR pszColon;
4413 INT len;
4415 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4417 pszColon = strchrW(buffer, ':');
4418 /* must have two tokens */
4419 if (!pszColon)
4421 HTTP_FreeTokens(pTokenPair);
4422 if (buffer[0])
4423 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4424 return NULL;
4427 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4428 if (!pTokenPair[0])
4430 HTTP_FreeTokens(pTokenPair);
4431 return NULL;
4433 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4434 pTokenPair[0][pszColon - buffer] = '\0';
4436 /* skip colon */
4437 pszColon++;
4438 len = strlenW(pszColon);
4439 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4440 if (!pTokenPair[1])
4442 HTTP_FreeTokens(pTokenPair);
4443 return NULL;
4445 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4447 strip_spaces(pTokenPair[0]);
4448 strip_spaces(pTokenPair[1]);
4450 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4451 return pTokenPair;
4454 /***********************************************************************
4455 * HTTP_ProcessHeader (internal)
4457 * Stuff header into header tables according to <dwModifier>
4461 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4463 static BOOL HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4465 LPHTTPHEADERW lphttpHdr = NULL;
4466 BOOL bSuccess = FALSE;
4467 INT index = -1;
4468 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4470 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4472 /* REPLACE wins out over ADD */
4473 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4474 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4476 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4477 index = -1;
4478 else
4479 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4481 if (index >= 0)
4483 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4485 return FALSE;
4487 lphttpHdr = &lpwhr->pCustHeaders[index];
4489 else if (value)
4491 HTTPHEADERW hdr;
4493 hdr.lpszField = (LPWSTR)field;
4494 hdr.lpszValue = (LPWSTR)value;
4495 hdr.wFlags = hdr.wCount = 0;
4497 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4498 hdr.wFlags |= HDR_ISREQUEST;
4500 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4502 /* no value to delete */
4503 else return TRUE;
4505 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4506 lphttpHdr->wFlags |= HDR_ISREQUEST;
4507 else
4508 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4510 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4512 HTTP_DeleteCustomHeader( lpwhr, index );
4514 if (value)
4516 HTTPHEADERW hdr;
4518 hdr.lpszField = (LPWSTR)field;
4519 hdr.lpszValue = (LPWSTR)value;
4520 hdr.wFlags = hdr.wCount = 0;
4522 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4523 hdr.wFlags |= HDR_ISREQUEST;
4525 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4528 return TRUE;
4530 else if (dwModifier & COALESCEFLAGS)
4532 LPWSTR lpsztmp;
4533 WCHAR ch = 0;
4534 INT len = 0;
4535 INT origlen = strlenW(lphttpHdr->lpszValue);
4536 INT valuelen = strlenW(value);
4538 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4540 ch = ',';
4541 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4543 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4545 ch = ';';
4546 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4549 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4551 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4552 if (lpsztmp)
4554 lphttpHdr->lpszValue = lpsztmp;
4555 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4556 if (ch > 0)
4558 lphttpHdr->lpszValue[origlen] = ch;
4559 origlen++;
4560 lphttpHdr->lpszValue[origlen] = ' ';
4561 origlen++;
4564 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4565 lphttpHdr->lpszValue[len] = '\0';
4566 bSuccess = TRUE;
4568 else
4570 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4571 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4574 TRACE("<-- %d\n",bSuccess);
4575 return bSuccess;
4579 /***********************************************************************
4580 * HTTP_FinishedReading (internal)
4582 * Called when all content from server has been read by client.
4585 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4587 WCHAR szVersion[10];
4588 WCHAR szConnectionResponse[20];
4589 DWORD dwBufferSize = sizeof(szVersion);
4590 BOOL keepalive = FALSE;
4592 TRACE("\n");
4594 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
4595 * the connection is keep-alive by default */
4596 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion,
4597 &dwBufferSize, NULL) &&
4598 !strcmpiW(szVersion, g_szHttp1_1))
4600 keepalive = TRUE;
4603 dwBufferSize = sizeof(szConnectionResponse);
4604 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) ||
4605 HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL))
4607 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
4610 if (!keepalive)
4612 HTTPREQ_CloseConnection(&lpwhr->hdr);
4615 /* FIXME: store data in the URL cache here */
4617 return TRUE;
4621 /***********************************************************************
4622 * HTTP_GetCustomHeaderIndex (internal)
4624 * Return index of custom header from header array
4627 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
4628 int requested_index, BOOL request_only)
4630 DWORD index;
4632 TRACE("%s\n", debugstr_w(lpszField));
4634 for (index = 0; index < lpwhr->nCustHeaders; index++)
4636 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
4637 continue;
4639 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4640 continue;
4642 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4643 continue;
4645 if (requested_index == 0)
4646 break;
4647 requested_index --;
4650 if (index >= lpwhr->nCustHeaders)
4651 index = -1;
4653 TRACE("Return: %d\n", index);
4654 return index;
4658 /***********************************************************************
4659 * HTTP_InsertCustomHeader (internal)
4661 * Insert header into array
4664 static BOOL HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
4666 INT count;
4667 LPHTTPHEADERW lph = NULL;
4668 BOOL r = FALSE;
4670 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
4671 count = lpwhr->nCustHeaders + 1;
4672 if (count > 1)
4673 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
4674 else
4675 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
4677 if (NULL != lph)
4679 lpwhr->pCustHeaders = lph;
4680 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
4681 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
4682 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
4683 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
4684 lpwhr->nCustHeaders++;
4685 r = TRUE;
4687 else
4689 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4692 return r;
4696 /***********************************************************************
4697 * HTTP_DeleteCustomHeader (internal)
4699 * Delete header from array
4700 * If this function is called, the indexs may change.
4702 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
4704 if( lpwhr->nCustHeaders <= 0 )
4705 return FALSE;
4706 if( index >= lpwhr->nCustHeaders )
4707 return FALSE;
4708 lpwhr->nCustHeaders--;
4710 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
4711 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
4713 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
4714 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
4715 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
4717 return TRUE;
4721 /***********************************************************************
4722 * HTTP_VerifyValidHeader (internal)
4724 * Verify the given header is not invalid for the given http request
4727 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
4729 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4730 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
4731 return FALSE;
4733 return TRUE;
4736 /***********************************************************************
4737 * IsHostInProxyBypassList (@)
4739 * Undocumented
4742 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
4744 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
4745 return FALSE;