ntdll: Use #ifdef instead of #if to check for _DARWIN_FEATURE_64_BIT_INODE.
[wine/testsucceed.git] / dlls / wininet / http.c
blob70f5f6a3161f998e25fb90665cae63f5226981ac
1 /*
2 * Wininet - Http Implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2002 CodeWeavers Inc.
6 * Copyright 2002 TransGaming Technologies Inc.
7 * Copyright 2004 Mike McCormack for CodeWeavers
8 * Copyright 2005 Aric Stewart for CodeWeavers
9 * Copyright 2006 Robert Shearman for CodeWeavers
11 * Ulrich Czekalla
12 * David Hammerton
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
32 #if defined(__MINGW32__) || defined (_MSC_VER)
33 #include <ws2tcpip.h>
34 #endif
36 #include <sys/types.h>
37 #ifdef HAVE_SYS_SOCKET_H
38 # include <sys/socket.h>
39 #endif
40 #ifdef HAVE_ARPA_INET_H
41 # include <arpa/inet.h>
42 #endif
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #ifdef HAVE_UNISTD_H
47 # include <unistd.h>
48 #endif
49 #include <time.h>
50 #include <assert.h>
51 #ifdef HAVE_ZLIB
52 # include <zlib.h>
53 #endif
55 #include "windef.h"
56 #include "winbase.h"
57 #include "wininet.h"
58 #include "winerror.h"
59 #define NO_SHLWAPI_STREAM
60 #define NO_SHLWAPI_REG
61 #define NO_SHLWAPI_STRFCNS
62 #define NO_SHLWAPI_GDI
63 #include "shlwapi.h"
64 #include "sspi.h"
65 #include "wincrypt.h"
67 #include "internet.h"
68 #include "wine/debug.h"
69 #include "wine/exception.h"
70 #include "wine/unicode.h"
72 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
74 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
75 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
76 static const WCHAR hostW[] = { 'H','o','s','t',0 };
77 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
78 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
79 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
80 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
81 static const WCHAR szGET[] = { 'G','E','T', 0 };
82 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
83 static const WCHAR szCrLf[] = {'\r','\n', 0};
85 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
86 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
87 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
88 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
89 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
90 static const WCHAR szAge[] = { 'A','g','e',0 };
91 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
92 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
93 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
94 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
95 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
96 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
97 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
98 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
99 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
100 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
101 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
102 static const WCHAR szContent_Transfer_Encoding[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
103 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
104 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
105 static const WCHAR szDate[] = { 'D','a','t','e',0 };
106 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
107 static const WCHAR szETag[] = { 'E','T','a','g',0 };
108 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
109 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
110 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
111 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
112 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
113 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
114 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
115 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
116 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
117 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
118 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
119 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
120 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
121 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
122 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
123 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
124 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
125 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
126 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
127 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
128 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
129 static const WCHAR szUnless_Modified_Since[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
130 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
131 static const WCHAR szURI[] = { 'U','R','I',0 };
132 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
133 static const WCHAR szVary[] = { 'V','a','r','y',0 };
134 static const WCHAR szVia[] = { 'V','i','a',0 };
135 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
136 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
138 #define MAXHOSTNAME 100
139 #define MAX_FIELD_VALUE_LEN 256
140 #define MAX_FIELD_LEN 256
142 #define HTTP_REFERER szReferer
143 #define HTTP_ACCEPT szAccept
144 #define HTTP_USERAGENT szUser_Agent
146 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
147 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
148 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
149 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
150 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
151 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
152 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
154 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
156 struct HttpAuthInfo
158 LPWSTR scheme;
159 CredHandle cred;
160 CtxtHandle ctx;
161 TimeStamp exp;
162 ULONG attr;
163 ULONG max_token;
164 void *auth_data;
165 unsigned int auth_data_len;
166 BOOL finished; /* finished authenticating */
170 struct gzip_stream_t {
171 #ifdef HAVE_ZLIB
172 z_stream zstream;
173 #endif
174 BYTE buf[8192];
175 DWORD buf_size;
176 DWORD buf_pos;
177 BOOL end_of_data;
180 typedef struct _authorizationData
182 struct list entry;
184 LPWSTR lpszwHost;
185 LPWSTR lpszwRealm;
186 LPSTR lpszAuthorization;
187 UINT AuthorizationLen;
188 } authorizationData;
190 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
192 static CRITICAL_SECTION authcache_cs;
193 static CRITICAL_SECTION_DEBUG critsect_debug =
195 0, 0, &authcache_cs,
196 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
197 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
199 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
201 static DWORD HTTP_OpenConnection(http_request_t *req);
202 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
203 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
204 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
205 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
206 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
207 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
208 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
209 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
210 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
211 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
212 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
213 static void HTTP_DrainContent(http_request_t *req);
214 static BOOL HTTP_FinishedReading(http_request_t *req);
216 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
218 int HeaderIndex = 0;
219 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
220 if (HeaderIndex == -1)
221 return NULL;
222 else
223 return &req->pCustHeaders[HeaderIndex];
226 #ifdef HAVE_ZLIB
228 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
230 return HeapAlloc(GetProcessHeap(), 0, items*size);
233 static void wininet_zfree(voidpf opaque, voidpf address)
235 HeapFree(GetProcessHeap(), 0, address);
238 static void init_gzip_stream(http_request_t *req)
240 gzip_stream_t *gzip_stream;
241 int index, zres;
243 gzip_stream = HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t));
244 gzip_stream->zstream.zalloc = wininet_zalloc;
245 gzip_stream->zstream.zfree = wininet_zfree;
246 gzip_stream->zstream.opaque = NULL;
247 gzip_stream->zstream.next_in = NULL;
248 gzip_stream->zstream.avail_in = 0;
249 gzip_stream->zstream.next_out = NULL;
250 gzip_stream->zstream.avail_out = 0;
251 gzip_stream->buf_pos = 0;
252 gzip_stream->buf_size = 0;
253 gzip_stream->end_of_data = FALSE;
255 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
256 if(zres != Z_OK) {
257 ERR("inflateInit failed: %d\n", zres);
258 HeapFree(GetProcessHeap(), 0, gzip_stream);
259 return;
262 req->gzip_stream = gzip_stream;
264 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
265 if(index != -1)
266 HTTP_DeleteCustomHeader(req, index);
269 #else
271 static void init_gzip_stream(http_request_t *req)
273 ERR("gzip stream not supported, missing zlib.\n");
276 #endif
278 /* set the request content length based on the headers */
279 static DWORD set_content_length( http_request_t *lpwhr )
281 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
282 WCHAR encoding[20];
283 DWORD size;
285 size = sizeof(lpwhr->dwContentLength);
286 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
287 &lpwhr->dwContentLength, &size, NULL) != ERROR_SUCCESS)
288 lpwhr->dwContentLength = ~0u;
290 size = sizeof(encoding);
291 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
292 !strcmpiW(encoding, szChunked))
294 lpwhr->dwContentLength = ~0u;
295 lpwhr->read_chunked = TRUE;
298 if(lpwhr->decoding) {
299 int encoding_idx;
301 static const WCHAR gzipW[] = {'g','z','i','p',0};
303 encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
304 if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
305 init_gzip_stream(lpwhr);
308 return lpwhr->dwContentLength;
311 /***********************************************************************
312 * HTTP_Tokenize (internal)
314 * Tokenize a string, allocating memory for the tokens.
316 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
318 LPWSTR * token_array;
319 int tokens = 0;
320 int i;
321 LPCWSTR next_token;
323 if (string)
325 /* empty string has no tokens */
326 if (*string)
327 tokens++;
328 /* count tokens */
329 for (i = 0; string[i]; i++)
331 if (!strncmpW(string+i, token_string, strlenW(token_string)))
333 DWORD j;
334 tokens++;
335 /* we want to skip over separators, but not the null terminator */
336 for (j = 0; j < strlenW(token_string) - 1; j++)
337 if (!string[i+j])
338 break;
339 i += j;
344 /* add 1 for terminating NULL */
345 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
346 token_array[tokens] = NULL;
347 if (!tokens)
348 return token_array;
349 for (i = 0; i < tokens; i++)
351 int len;
352 next_token = strstrW(string, token_string);
353 if (!next_token) next_token = string+strlenW(string);
354 len = next_token - string;
355 token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
356 memcpy(token_array[i], string, len*sizeof(WCHAR));
357 token_array[i][len] = '\0';
358 string = next_token+strlenW(token_string);
360 return token_array;
363 /***********************************************************************
364 * HTTP_FreeTokens (internal)
366 * Frees memory returned from HTTP_Tokenize.
368 static void HTTP_FreeTokens(LPWSTR * token_array)
370 int i;
371 for (i = 0; token_array[i]; i++)
372 HeapFree(GetProcessHeap(), 0, token_array[i]);
373 HeapFree(GetProcessHeap(), 0, token_array);
376 static void HTTP_FixURL(http_request_t *lpwhr)
378 static const WCHAR szSlash[] = { '/',0 };
379 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
381 /* If we don't have a path we set it to root */
382 if (NULL == lpwhr->lpszPath)
383 lpwhr->lpszPath = heap_strdupW(szSlash);
384 else /* remove \r and \n*/
386 int nLen = strlenW(lpwhr->lpszPath);
387 while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
389 nLen--;
390 lpwhr->lpszPath[nLen]='\0';
392 /* Replace '\' with '/' */
393 while (nLen>0) {
394 nLen--;
395 if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
399 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
400 lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
401 && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
403 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
404 (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
405 *fixurl = '/';
406 strcpyW(fixurl + 1, lpwhr->lpszPath);
407 HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
408 lpwhr->lpszPath = fixurl;
412 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
414 LPWSTR requestString;
415 DWORD len, n;
416 LPCWSTR *req;
417 UINT i;
418 LPWSTR p;
420 static const WCHAR szSpace[] = { ' ',0 };
421 static const WCHAR szColon[] = { ':',' ',0 };
422 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
424 /* allocate space for an array of all the string pointers to be added */
425 len = (lpwhr->nCustHeaders)*4 + 10;
426 req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
428 /* add the verb, path and HTTP version string */
429 n = 0;
430 req[n++] = verb;
431 req[n++] = szSpace;
432 req[n++] = path;
433 req[n++] = szSpace;
434 req[n++] = version;
436 /* Append custom request headers */
437 for (i = 0; i < lpwhr->nCustHeaders; i++)
439 if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
441 req[n++] = szCrLf;
442 req[n++] = lpwhr->pCustHeaders[i].lpszField;
443 req[n++] = szColon;
444 req[n++] = lpwhr->pCustHeaders[i].lpszValue;
446 TRACE("Adding custom header %s (%s)\n",
447 debugstr_w(lpwhr->pCustHeaders[i].lpszField),
448 debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
452 if( n >= len )
453 ERR("oops. buffer overrun\n");
455 req[n] = NULL;
456 requestString = HTTP_build_req( req, 4 );
457 HeapFree( GetProcessHeap(), 0, req );
460 * Set (header) termination string for request
461 * Make sure there's exactly two new lines at the end of the request
463 p = &requestString[strlenW(requestString)-1];
464 while ( (*p == '\n') || (*p == '\r') )
465 p--;
466 strcpyW( p+1, sztwocrlf );
468 return requestString;
471 static void HTTP_ProcessCookies( http_request_t *lpwhr )
473 int HeaderIndex;
474 int numCookies = 0;
475 LPHTTPHEADERW setCookieHeader;
477 while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
479 setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
481 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
483 int len;
484 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
485 LPWSTR buf_url;
486 LPHTTPHEADERW Host;
488 Host = HTTP_GetHeader(lpwhr, hostW);
489 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
490 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
491 sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
492 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
494 HeapFree(GetProcessHeap(), 0, buf_url);
496 numCookies++;
500 static void strip_spaces(LPWSTR start)
502 LPWSTR str = start;
503 LPWSTR end;
505 while (*str == ' ' && *str != '\0')
506 str++;
508 if (str != start)
509 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
511 end = start + strlenW(start) - 1;
512 while (end >= start && *end == ' ')
514 *end = '\0';
515 end--;
519 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
521 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
522 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
523 BOOL is_basic;
524 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
525 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
526 if (is_basic && pszRealm)
528 LPCWSTR token;
529 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
530 LPCWSTR realm;
531 ptr++;
532 *pszRealm=NULL;
533 token = strchrW(ptr,'=');
534 if (!token)
535 return TRUE;
536 realm = ptr;
537 while (*realm == ' ' && *realm != '\0')
538 realm++;
539 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
540 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
542 token++;
543 while (*token == ' ' && *token != '\0')
544 token++;
545 if (*token == '\0')
546 return TRUE;
547 *pszRealm = heap_strdupW(token);
548 strip_spaces(*pszRealm);
552 return is_basic;
555 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
557 if (!authinfo) return;
559 if (SecIsValidHandle(&authinfo->ctx))
560 DeleteSecurityContext(&authinfo->ctx);
561 if (SecIsValidHandle(&authinfo->cred))
562 FreeCredentialsHandle(&authinfo->cred);
564 HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
565 HeapFree(GetProcessHeap(), 0, authinfo->scheme);
566 HeapFree(GetProcessHeap(), 0, authinfo);
569 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
571 authorizationData *ad;
572 UINT rc = 0;
574 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
576 EnterCriticalSection(&authcache_cs);
577 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, authorizationData, entry)
579 if (!strcmpiW(host,ad->lpszwHost) && !strcmpW(realm,ad->lpszwRealm))
581 TRACE("Authorization found in cache\n");
582 *auth_data = HeapAlloc(GetProcessHeap(),0,ad->AuthorizationLen);
583 memcpy(*auth_data,ad->lpszAuthorization,ad->AuthorizationLen);
584 rc = ad->AuthorizationLen;
585 break;
588 LeaveCriticalSection(&authcache_cs);
589 return rc;
592 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
594 struct list *cursor;
595 authorizationData* ad = NULL;
597 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
599 EnterCriticalSection(&authcache_cs);
600 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
602 authorizationData *check = LIST_ENTRY(cursor,authorizationData,entry);
603 if (!strcmpiW(host,check->lpszwHost) && !strcmpW(realm,check->lpszwRealm))
605 ad = check;
606 break;
610 if (ad)
612 TRACE("Found match in cache, replacing\n");
613 HeapFree(GetProcessHeap(),0,ad->lpszAuthorization);
614 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
615 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
616 ad->AuthorizationLen = auth_data_len;
618 else
620 ad = HeapAlloc(GetProcessHeap(),0,sizeof(authorizationData));
621 ad->lpszwHost = heap_strdupW(host);
622 ad->lpszwRealm = heap_strdupW(realm);
623 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
624 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
625 ad->AuthorizationLen = auth_data_len;
626 list_add_head(&basicAuthorizationCache,&ad->entry);
627 TRACE("authorization cached\n");
629 LeaveCriticalSection(&authcache_cs);
632 static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
633 struct HttpAuthInfo **ppAuthInfo,
634 LPWSTR domain_and_username, LPWSTR password,
635 LPWSTR host )
637 SECURITY_STATUS sec_status;
638 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
639 BOOL first = FALSE;
640 LPWSTR szRealm = NULL;
642 TRACE("%s\n", debugstr_w(pszAuthValue));
644 if (!pAuthInfo)
646 TimeStamp exp;
648 first = TRUE;
649 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
650 if (!pAuthInfo)
651 return FALSE;
653 SecInvalidateHandle(&pAuthInfo->cred);
654 SecInvalidateHandle(&pAuthInfo->ctx);
655 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
656 pAuthInfo->attr = 0;
657 pAuthInfo->auth_data = NULL;
658 pAuthInfo->auth_data_len = 0;
659 pAuthInfo->finished = FALSE;
661 if (is_basic_auth_value(pszAuthValue,NULL))
663 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
664 pAuthInfo->scheme = heap_strdupW(szBasic);
665 if (!pAuthInfo->scheme)
667 HeapFree(GetProcessHeap(), 0, pAuthInfo);
668 return FALSE;
671 else
673 PVOID pAuthData;
674 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
676 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
677 if (!pAuthInfo->scheme)
679 HeapFree(GetProcessHeap(), 0, pAuthInfo);
680 return FALSE;
683 if (domain_and_username)
685 WCHAR *user = strchrW(domain_and_username, '\\');
686 WCHAR *domain = domain_and_username;
688 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
690 pAuthData = &nt_auth_identity;
692 if (user) user++;
693 else
695 user = domain_and_username;
696 domain = NULL;
699 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
700 nt_auth_identity.User = user;
701 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
702 nt_auth_identity.Domain = domain;
703 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
704 nt_auth_identity.Password = password;
705 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
707 else
708 /* use default credentials */
709 pAuthData = NULL;
711 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
712 SECPKG_CRED_OUTBOUND, NULL,
713 pAuthData, NULL,
714 NULL, &pAuthInfo->cred,
715 &exp);
716 if (sec_status == SEC_E_OK)
718 PSecPkgInfoW sec_pkg_info;
719 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
720 if (sec_status == SEC_E_OK)
722 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
723 FreeContextBuffer(sec_pkg_info);
726 if (sec_status != SEC_E_OK)
728 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
729 debugstr_w(pAuthInfo->scheme), sec_status);
730 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
731 HeapFree(GetProcessHeap(), 0, pAuthInfo);
732 return FALSE;
735 *ppAuthInfo = pAuthInfo;
737 else if (pAuthInfo->finished)
738 return FALSE;
740 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
741 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
743 ERR("authentication scheme changed from %s to %s\n",
744 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
745 return FALSE;
748 if (is_basic_auth_value(pszAuthValue,&szRealm))
750 int userlen;
751 int passlen;
752 char *auth_data = NULL;
753 UINT auth_data_len = 0;
755 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
757 if (!domain_and_username)
759 if (host && szRealm)
760 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
761 if (auth_data_len == 0)
763 HeapFree(GetProcessHeap(),0,szRealm);
764 return FALSE;
767 else
769 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
770 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
772 /* length includes a nul terminator, which will be re-used for the ':' */
773 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
774 if (!auth_data)
776 HeapFree(GetProcessHeap(),0,szRealm);
777 return FALSE;
780 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
781 auth_data[userlen] = ':';
782 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
783 auth_data_len = userlen + 1 + passlen;
784 if (host && szRealm)
785 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
788 pAuthInfo->auth_data = auth_data;
789 pAuthInfo->auth_data_len = auth_data_len;
790 pAuthInfo->finished = TRUE;
791 HeapFree(GetProcessHeap(),0,szRealm);
793 return TRUE;
795 else
797 LPCWSTR pszAuthData;
798 SecBufferDesc out_desc, in_desc;
799 SecBuffer out, in;
800 unsigned char *buffer;
801 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
802 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
804 in.BufferType = SECBUFFER_TOKEN;
805 in.cbBuffer = 0;
806 in.pvBuffer = NULL;
808 in_desc.ulVersion = 0;
809 in_desc.cBuffers = 1;
810 in_desc.pBuffers = &in;
812 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
813 if (*pszAuthData == ' ')
815 pszAuthData++;
816 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
817 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
818 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
821 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
823 out.BufferType = SECBUFFER_TOKEN;
824 out.cbBuffer = pAuthInfo->max_token;
825 out.pvBuffer = buffer;
827 out_desc.ulVersion = 0;
828 out_desc.cBuffers = 1;
829 out_desc.pBuffers = &out;
831 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
832 first ? NULL : &pAuthInfo->ctx,
833 first ? lpwhr->lpHttpSession->lpszServerName : NULL,
834 context_req, 0, SECURITY_NETWORK_DREP,
835 in.pvBuffer ? &in_desc : NULL,
836 0, &pAuthInfo->ctx, &out_desc,
837 &pAuthInfo->attr, &pAuthInfo->exp);
838 if (sec_status == SEC_E_OK)
840 pAuthInfo->finished = TRUE;
841 pAuthInfo->auth_data = out.pvBuffer;
842 pAuthInfo->auth_data_len = out.cbBuffer;
843 TRACE("sending last auth packet\n");
845 else if (sec_status == SEC_I_CONTINUE_NEEDED)
847 pAuthInfo->auth_data = out.pvBuffer;
848 pAuthInfo->auth_data_len = out.cbBuffer;
849 TRACE("sending next auth packet\n");
851 else
853 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
854 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
855 destroy_authinfo(pAuthInfo);
856 *ppAuthInfo = NULL;
857 return FALSE;
861 return TRUE;
864 /***********************************************************************
865 * HTTP_HttpAddRequestHeadersW (internal)
867 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
868 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
870 LPWSTR lpszStart;
871 LPWSTR lpszEnd;
872 LPWSTR buffer;
873 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
875 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
877 if( dwHeaderLength == ~0U )
878 len = strlenW(lpszHeader);
879 else
880 len = dwHeaderLength;
881 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
882 lstrcpynW( buffer, lpszHeader, len + 1);
884 lpszStart = buffer;
888 LPWSTR * pFieldAndValue;
890 lpszEnd = lpszStart;
892 while (*lpszEnd != '\0')
894 if (*lpszEnd == '\r' || *lpszEnd == '\n')
895 break;
896 lpszEnd++;
899 if (*lpszStart == '\0')
900 break;
902 if (*lpszEnd == '\r' || *lpszEnd == '\n')
904 *lpszEnd = '\0';
905 lpszEnd++; /* Jump over newline */
907 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
908 if (*lpszStart == '\0')
910 /* Skip 0-length headers */
911 lpszStart = lpszEnd;
912 res = ERROR_SUCCESS;
913 continue;
915 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
916 if (pFieldAndValue)
918 res = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
919 if (res == ERROR_SUCCESS)
920 res = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
921 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
922 HTTP_FreeTokens(pFieldAndValue);
925 lpszStart = lpszEnd;
926 } while (res == ERROR_SUCCESS);
928 HeapFree(GetProcessHeap(), 0, buffer);
930 return res;
933 /***********************************************************************
934 * HttpAddRequestHeadersW (WININET.@)
936 * Adds one or more HTTP header to the request handler
938 * NOTE
939 * On Windows if dwHeaderLength includes the trailing '\0', then
940 * HttpAddRequestHeadersW() adds it too. However this results in an
941 * invalid Http header which is rejected by some servers so we probably
942 * don't need to match Windows on that point.
944 * RETURNS
945 * TRUE on success
946 * FALSE on failure
949 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
950 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
952 http_request_t *lpwhr;
953 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
955 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
957 if (!lpszHeader)
958 return TRUE;
960 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
961 if (lpwhr && lpwhr->hdr.htype == WH_HHTTPREQ)
962 res = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
963 if( lpwhr )
964 WININET_Release( &lpwhr->hdr );
966 if(res != ERROR_SUCCESS)
967 SetLastError(res);
968 return res == ERROR_SUCCESS;
971 /***********************************************************************
972 * HttpAddRequestHeadersA (WININET.@)
974 * Adds one or more HTTP header to the request handler
976 * RETURNS
977 * TRUE on success
978 * FALSE on failure
981 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
982 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
984 DWORD len;
985 LPWSTR hdr;
986 BOOL r;
988 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
990 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
991 hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
992 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
993 if( dwHeaderLength != ~0U )
994 dwHeaderLength = len;
996 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
998 HeapFree( GetProcessHeap(), 0, hdr );
1000 return r;
1003 /***********************************************************************
1004 * HttpOpenRequestA (WININET.@)
1006 * Open a HTTP request handle
1008 * RETURNS
1009 * HINTERNET a HTTP request handle on success
1010 * NULL on failure
1013 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1014 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1015 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1016 DWORD dwFlags, DWORD_PTR dwContext)
1018 LPWSTR szVerb = NULL, szObjectName = NULL;
1019 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1020 INT acceptTypesCount;
1021 HINTERNET rc = FALSE;
1022 LPCSTR *types;
1024 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1025 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1026 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1027 dwFlags, dwContext);
1029 if (lpszVerb)
1031 szVerb = heap_strdupAtoW(lpszVerb);
1032 if ( !szVerb )
1033 goto end;
1036 if (lpszObjectName)
1038 szObjectName = heap_strdupAtoW(lpszObjectName);
1039 if ( !szObjectName )
1040 goto end;
1043 if (lpszVersion)
1045 szVersion = heap_strdupAtoW(lpszVersion);
1046 if ( !szVersion )
1047 goto end;
1050 if (lpszReferrer)
1052 szReferrer = heap_strdupAtoW(lpszReferrer);
1053 if ( !szReferrer )
1054 goto end;
1057 if (lpszAcceptTypes)
1059 acceptTypesCount = 0;
1060 types = lpszAcceptTypes;
1061 while (*types)
1063 __TRY
1065 /* find out how many there are */
1066 if (*types && **types)
1068 TRACE("accept type: %s\n", debugstr_a(*types));
1069 acceptTypesCount++;
1072 __EXCEPT_PAGE_FAULT
1074 WARN("invalid accept type pointer\n");
1076 __ENDTRY;
1077 types++;
1079 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1080 if (!szAcceptTypes) goto end;
1082 acceptTypesCount = 0;
1083 types = lpszAcceptTypes;
1084 while (*types)
1086 __TRY
1088 if (*types && **types)
1089 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1091 __EXCEPT_PAGE_FAULT
1093 /* ignore invalid pointer */
1095 __ENDTRY;
1096 types++;
1098 szAcceptTypes[acceptTypesCount] = NULL;
1101 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1102 szVersion, szReferrer,
1103 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1105 end:
1106 if (szAcceptTypes)
1108 acceptTypesCount = 0;
1109 while (szAcceptTypes[acceptTypesCount])
1111 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1112 acceptTypesCount++;
1114 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1116 HeapFree(GetProcessHeap(), 0, szReferrer);
1117 HeapFree(GetProcessHeap(), 0, szVersion);
1118 HeapFree(GetProcessHeap(), 0, szObjectName);
1119 HeapFree(GetProcessHeap(), 0, szVerb);
1121 return rc;
1124 /***********************************************************************
1125 * HTTP_EncodeBase64
1127 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1129 UINT n = 0, x;
1130 static const CHAR HTTP_Base64Enc[] =
1131 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1133 while( len > 0 )
1135 /* first 6 bits, all from bin[0] */
1136 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1137 x = (bin[0] & 3) << 4;
1139 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1140 if( len == 1 )
1142 base64[n++] = HTTP_Base64Enc[x];
1143 base64[n++] = '=';
1144 base64[n++] = '=';
1145 break;
1147 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1148 x = ( bin[1] & 0x0f ) << 2;
1150 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1151 if( len == 2 )
1153 base64[n++] = HTTP_Base64Enc[x];
1154 base64[n++] = '=';
1155 break;
1157 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1159 /* last 6 bits, all from bin [2] */
1160 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1161 bin += 3;
1162 len -= 3;
1164 base64[n] = 0;
1165 return n;
1168 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1169 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1170 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1171 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1172 static const signed char HTTP_Base64Dec[256] =
1174 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1175 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1176 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1177 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1178 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1179 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1180 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1181 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1182 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1183 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1184 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1185 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1186 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1187 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1188 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1189 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1190 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1191 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1192 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1193 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1194 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1195 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1196 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1197 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1198 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1199 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1201 #undef CH
1203 /***********************************************************************
1204 * HTTP_DecodeBase64
1206 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1208 unsigned int n = 0;
1210 while(*base64)
1212 signed char in[4];
1214 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1215 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1216 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1217 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1219 WARN("invalid base64: %s\n", debugstr_w(base64));
1220 return 0;
1222 if (bin)
1223 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1224 n++;
1226 if ((base64[2] == '=') && (base64[3] == '='))
1227 break;
1228 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1229 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1231 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1232 return 0;
1234 if (bin)
1235 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1236 n++;
1238 if (base64[3] == '=')
1239 break;
1240 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1241 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1243 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1244 return 0;
1246 if (bin)
1247 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1248 n++;
1250 base64 += 4;
1253 return n;
1256 /***********************************************************************
1257 * HTTP_InsertAuthorization
1259 * Insert or delete the authorization field in the request header.
1261 static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1263 if (pAuthInfo)
1265 static const WCHAR wszSpace[] = {' ',0};
1266 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1267 unsigned int len;
1268 WCHAR *authorization = NULL;
1270 if (pAuthInfo->auth_data_len)
1272 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1273 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1274 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1275 if (!authorization)
1276 return FALSE;
1278 strcpyW(authorization, pAuthInfo->scheme);
1279 strcatW(authorization, wszSpace);
1280 HTTP_EncodeBase64(pAuthInfo->auth_data,
1281 pAuthInfo->auth_data_len,
1282 authorization+strlenW(authorization));
1284 /* clear the data as it isn't valid now that it has been sent to the
1285 * server, unless it's Basic authentication which doesn't do
1286 * connection tracking */
1287 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1289 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1290 pAuthInfo->auth_data = NULL;
1291 pAuthInfo->auth_data_len = 0;
1295 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1297 HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1299 HeapFree(GetProcessHeap(), 0, authorization);
1301 return TRUE;
1304 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1306 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1307 DWORD size;
1309 size = sizeof(new_location);
1310 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1312 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1313 strcpyW( url, new_location );
1315 else
1317 static const WCHAR slash[] = { '/',0 };
1318 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1319 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1320 http_session_t *session = req->lpHttpSession;
1322 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1323 size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
1325 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1327 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1328 sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
1329 else
1330 sprintfW( url, format, session->lpszHostName, session->nHostPort );
1331 if (req->lpszPath[0] != '/') strcatW( url, slash );
1332 strcatW( url, req->lpszPath );
1334 TRACE("url=%s\n", debugstr_w(url));
1335 return url;
1338 /***********************************************************************
1339 * HTTP_DealWithProxy
1341 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
1343 WCHAR buf[MAXHOSTNAME];
1344 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1345 static WCHAR szNul[] = { 0 };
1346 URL_COMPONENTSW UrlComponents;
1347 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1348 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1350 memset( &UrlComponents, 0, sizeof UrlComponents );
1351 UrlComponents.dwStructSize = sizeof UrlComponents;
1352 UrlComponents.lpszHostName = buf;
1353 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1355 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1356 hIC->lpszProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1357 sprintfW(proxy, szFormat, hIC->lpszProxy);
1358 else
1359 strcpyW(proxy, hIC->lpszProxy);
1360 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1361 return FALSE;
1362 if( UrlComponents.dwHostNameLength == 0 )
1363 return FALSE;
1365 if( !lpwhr->lpszPath )
1366 lpwhr->lpszPath = szNul;
1368 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1369 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1371 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1372 lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1373 lpwhs->nServerPort = UrlComponents.nPort;
1375 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1376 return TRUE;
1379 #ifndef INET6_ADDRSTRLEN
1380 #define INET6_ADDRSTRLEN 46
1381 #endif
1383 static DWORD HTTP_ResolveName(http_request_t *lpwhr)
1385 char szaddr[INET6_ADDRSTRLEN];
1386 http_session_t *lpwhs = lpwhr->lpHttpSession;
1387 const void *addr;
1389 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1390 INTERNET_STATUS_RESOLVING_NAME,
1391 lpwhs->lpszServerName,
1392 (strlenW(lpwhs->lpszServerName)+1) * sizeof(WCHAR));
1394 lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1395 if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1396 (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1397 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1399 switch (lpwhs->socketAddress.ss_family)
1401 case AF_INET:
1402 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1403 break;
1404 case AF_INET6:
1405 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1406 break;
1407 default:
1408 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1409 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1411 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1412 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1413 INTERNET_STATUS_NAME_RESOLVED,
1414 szaddr, strlen(szaddr)+1);
1416 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1417 return ERROR_SUCCESS;
1421 /***********************************************************************
1422 * HTTPREQ_Destroy (internal)
1424 * Deallocate request handle
1427 static void HTTPREQ_Destroy(object_header_t *hdr)
1429 http_request_t *lpwhr = (http_request_t*) hdr;
1430 DWORD i;
1432 TRACE("\n");
1434 if(lpwhr->hCacheFile)
1435 CloseHandle(lpwhr->hCacheFile);
1437 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1439 DeleteCriticalSection( &lpwhr->read_section );
1440 WININET_Release(&lpwhr->lpHttpSession->hdr);
1442 destroy_authinfo(lpwhr->pAuthInfo);
1443 destroy_authinfo(lpwhr->pProxyAuthInfo);
1445 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1446 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1447 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1448 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1449 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1451 for (i = 0; i < lpwhr->nCustHeaders; i++)
1453 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1454 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1457 #ifdef HAVE_ZLIB
1458 if(lpwhr->gzip_stream) {
1459 if(!lpwhr->gzip_stream->end_of_data)
1460 inflateEnd(&lpwhr->gzip_stream->zstream);
1461 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1463 #endif
1465 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1466 HeapFree(GetProcessHeap(), 0, lpwhr);
1469 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1471 http_request_t *lpwhr = (http_request_t*) hdr;
1473 TRACE("%p\n",lpwhr);
1475 if (!NETCON_connected(&lpwhr->netConnection))
1476 return;
1478 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1479 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1481 NETCON_close(&lpwhr->netConnection);
1483 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1484 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1487 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1489 LPHTTPHEADERW host_header;
1491 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1493 host_header = HTTP_GetHeader(req, hostW);
1494 if(!host_header)
1495 return FALSE;
1497 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1498 return TRUE;
1501 static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
1503 WCHAR szVersion[10];
1504 WCHAR szConnectionResponse[20];
1505 DWORD dwBufferSize = sizeof(szVersion);
1506 BOOL keepalive = FALSE;
1508 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1509 * the connection is keep-alive by default */
1510 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1511 && !strcmpiW(szVersion, g_szHttp1_1))
1513 keepalive = TRUE;
1516 dwBufferSize = sizeof(szConnectionResponse);
1517 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1518 || HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1520 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1523 return keepalive;
1526 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1528 http_request_t *req = (http_request_t*)hdr;
1530 switch(option) {
1531 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1533 http_session_t *lpwhs = req->lpHttpSession;
1534 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1536 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1538 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1539 return ERROR_INSUFFICIENT_BUFFER;
1540 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1541 /* FIXME: can't get a SOCKET from our connection since we don't use
1542 * winsock
1544 info->Socket = 0;
1545 /* FIXME: get source port from req->netConnection */
1546 info->SourcePort = 0;
1547 info->DestPort = lpwhs->nHostPort;
1548 info->Flags = 0;
1549 if (HTTP_KeepAlive(req))
1550 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1551 if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
1552 info->Flags |= IDSI_FLAG_PROXY;
1553 if (req->netConnection.useSSL)
1554 info->Flags |= IDSI_FLAG_SECURE;
1556 return ERROR_SUCCESS;
1559 case INTERNET_OPTION_SECURITY_FLAGS:
1561 http_session_t *lpwhs;
1562 lpwhs = req->lpHttpSession;
1564 if (*size < sizeof(ULONG))
1565 return ERROR_INSUFFICIENT_BUFFER;
1567 *size = sizeof(DWORD);
1568 if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1569 *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1570 else
1571 *(DWORD*)buffer = 0;
1572 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1573 return ERROR_SUCCESS;
1576 case INTERNET_OPTION_HANDLE_TYPE:
1577 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1579 if (*size < sizeof(ULONG))
1580 return ERROR_INSUFFICIENT_BUFFER;
1582 *size = sizeof(DWORD);
1583 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1584 return ERROR_SUCCESS;
1586 case INTERNET_OPTION_URL: {
1587 WCHAR url[INTERNET_MAX_URL_LENGTH];
1588 HTTPHEADERW *host;
1589 DWORD len;
1590 WCHAR *pch;
1592 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1594 TRACE("INTERNET_OPTION_URL\n");
1596 host = HTTP_GetHeader(req, hostW);
1597 strcpyW(url, httpW);
1598 strcatW(url, host->lpszValue);
1599 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1600 *pch = 0;
1601 strcatW(url, req->lpszPath);
1603 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1605 if(unicode) {
1606 len = (strlenW(url)+1) * sizeof(WCHAR);
1607 if(*size < len)
1608 return ERROR_INSUFFICIENT_BUFFER;
1610 *size = len;
1611 strcpyW(buffer, url);
1612 return ERROR_SUCCESS;
1613 }else {
1614 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1615 if(len > *size)
1616 return ERROR_INSUFFICIENT_BUFFER;
1618 *size = len;
1619 return ERROR_SUCCESS;
1623 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1624 INTERNET_CACHE_ENTRY_INFOW *info;
1625 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1626 WCHAR url[INTERNET_MAX_URL_LENGTH];
1627 DWORD nbytes, error;
1628 BOOL ret;
1630 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1632 if (*size < sizeof(*ts))
1634 *size = sizeof(*ts);
1635 return ERROR_INSUFFICIENT_BUFFER;
1637 nbytes = 0;
1638 HTTP_GetRequestURL(req, url);
1639 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1640 error = GetLastError();
1641 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1643 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1644 return ERROR_OUTOFMEMORY;
1646 GetUrlCacheEntryInfoW(url, info, &nbytes);
1648 ts->ftExpires = info->ExpireTime;
1649 ts->ftLastModified = info->LastModifiedTime;
1651 HeapFree(GetProcessHeap(), 0, info);
1652 *size = sizeof(*ts);
1653 return ERROR_SUCCESS;
1655 return error;
1658 case INTERNET_OPTION_DATAFILE_NAME: {
1659 DWORD req_size;
1661 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1663 if(!req->lpszCacheFile) {
1664 *size = 0;
1665 return ERROR_INTERNET_ITEM_NOT_FOUND;
1668 if(unicode) {
1669 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1670 if(*size < req_size)
1671 return ERROR_INSUFFICIENT_BUFFER;
1673 *size = req_size;
1674 memcpy(buffer, req->lpszCacheFile, *size);
1675 return ERROR_SUCCESS;
1676 }else {
1677 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1678 if (req_size > *size)
1679 return ERROR_INSUFFICIENT_BUFFER;
1681 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1682 -1, buffer, *size, NULL, NULL);
1683 return ERROR_SUCCESS;
1687 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1688 PCCERT_CONTEXT context;
1690 if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1691 *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1692 return ERROR_INSUFFICIENT_BUFFER;
1695 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1696 if(context) {
1697 INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1698 DWORD len;
1700 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1701 info->ftExpiry = context->pCertInfo->NotAfter;
1702 info->ftStart = context->pCertInfo->NotBefore;
1703 if(unicode) {
1704 len = CertNameToStrW(context->dwCertEncodingType,
1705 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1706 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1707 if(info->lpszSubjectInfo)
1708 CertNameToStrW(context->dwCertEncodingType,
1709 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1710 info->lpszSubjectInfo, len);
1711 len = CertNameToStrW(context->dwCertEncodingType,
1712 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1713 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1714 if (info->lpszIssuerInfo)
1715 CertNameToStrW(context->dwCertEncodingType,
1716 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1717 info->lpszIssuerInfo, len);
1718 }else {
1719 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1721 len = CertNameToStrA(context->dwCertEncodingType,
1722 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1723 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1724 if(infoA->lpszSubjectInfo)
1725 CertNameToStrA(context->dwCertEncodingType,
1726 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1727 infoA->lpszSubjectInfo, len);
1728 len = CertNameToStrA(context->dwCertEncodingType,
1729 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1730 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1731 if(infoA->lpszIssuerInfo)
1732 CertNameToStrA(context->dwCertEncodingType,
1733 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1734 infoA->lpszIssuerInfo, len);
1738 * Contrary to MSDN, these do not appear to be set.
1739 * lpszProtocolName
1740 * lpszSignatureAlgName
1741 * lpszEncryptionAlgName
1742 * dwKeySize
1744 CertFreeCertificateContext(context);
1745 return ERROR_SUCCESS;
1750 return INET_QueryOption(option, buffer, size, unicode);
1753 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1755 http_request_t *req = (http_request_t*)hdr;
1757 switch(option) {
1758 case INTERNET_OPTION_SEND_TIMEOUT:
1759 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1760 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1762 if (size != sizeof(DWORD))
1763 return ERROR_INVALID_PARAMETER;
1765 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1766 *(DWORD*)buffer);
1768 case INTERNET_OPTION_USERNAME:
1769 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1770 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1771 return ERROR_SUCCESS;
1773 case INTERNET_OPTION_PASSWORD:
1774 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1775 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1776 return ERROR_SUCCESS;
1777 case INTERNET_OPTION_HTTP_DECODING:
1778 if(size != sizeof(BOOL))
1779 return ERROR_INVALID_PARAMETER;
1780 req->decoding = *(BOOL*)buffer;
1781 return ERROR_SUCCESS;
1784 return ERROR_INTERNET_INVALID_OPTION;
1787 /* read some more data into the read buffer (the read section must be held) */
1788 static DWORD read_more_data( http_request_t *req, int maxlen )
1790 DWORD res;
1791 int len;
1793 if (req->read_pos)
1795 /* move existing data to the start of the buffer */
1796 if(req->read_size)
1797 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1798 req->read_pos = 0;
1801 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1803 res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1804 maxlen - req->read_size, 0, &len );
1805 if(res == ERROR_SUCCESS)
1806 req->read_size += len;
1808 return res;
1811 /* remove some amount of data from the read buffer (the read section must be held) */
1812 static void remove_data( http_request_t *req, int count )
1814 if (!(req->read_size -= count)) req->read_pos = 0;
1815 else req->read_pos += count;
1818 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1820 int count, bytes_read, pos = 0;
1821 DWORD res;
1823 EnterCriticalSection( &req->read_section );
1824 for (;;)
1826 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1828 if (eol)
1830 count = eol - (req->read_buf + req->read_pos);
1831 bytes_read = count + 1;
1833 else count = bytes_read = req->read_size;
1835 count = min( count, *len - pos );
1836 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1837 pos += count;
1838 remove_data( req, bytes_read );
1839 if (eol) break;
1841 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
1843 *len = 0;
1844 TRACE( "returning empty string\n" );
1845 LeaveCriticalSection( &req->read_section );
1846 INTERNET_SetLastError(res);
1847 return FALSE;
1850 LeaveCriticalSection( &req->read_section );
1852 if (pos < *len)
1854 if (pos && buffer[pos - 1] == '\r') pos--;
1855 *len = pos + 1;
1857 buffer[*len - 1] = 0;
1858 TRACE( "returning %s\n", debugstr_a(buffer));
1859 return TRUE;
1862 /* discard data contents until we reach end of line (the read section must be held) */
1863 static DWORD discard_eol( http_request_t *req )
1865 DWORD res;
1869 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1870 if (eol)
1872 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
1873 break;
1875 req->read_pos = req->read_size = 0; /* discard everything */
1876 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1877 } while (req->read_size);
1878 return ERROR_SUCCESS;
1881 /* read the size of the next chunk (the read section must be held) */
1882 static DWORD start_next_chunk( http_request_t *req )
1884 DWORD chunk_size = 0, res;
1886 if (!req->dwContentLength) return ERROR_SUCCESS;
1887 if (req->dwContentLength == req->dwContentRead)
1889 /* read terminator for the previous chunk */
1890 if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
1891 req->dwContentLength = ~0u;
1892 req->dwContentRead = 0;
1894 for (;;)
1896 while (req->read_size)
1898 char ch = req->read_buf[req->read_pos];
1899 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
1900 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
1901 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
1902 else if (ch == ';' || ch == '\r' || ch == '\n')
1904 TRACE( "reading %u byte chunk\n", chunk_size );
1905 req->dwContentLength = chunk_size;
1906 req->dwContentRead = 0;
1907 return discard_eol( req );
1909 remove_data( req, 1 );
1911 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1912 if (!req->read_size)
1914 req->dwContentLength = req->dwContentRead = 0;
1915 return ERROR_SUCCESS;
1920 /* check if we have reached the end of the data to read (the read section must be held) */
1921 static BOOL end_of_read_data( http_request_t *req )
1923 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
1924 if (req->read_chunked) return (req->dwContentLength == 0);
1925 if (req->dwContentLength == ~0u) return FALSE;
1926 return (req->dwContentLength == req->dwContentRead);
1929 /* fetch some more data into the read buffer (the read section must be held) */
1930 static DWORD refill_buffer( http_request_t *req )
1932 int len = sizeof(req->read_buf);
1933 DWORD res;
1935 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
1937 if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
1940 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
1941 if (len <= req->read_size) return ERROR_SUCCESS;
1943 if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
1944 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
1945 return ERROR_SUCCESS;
1948 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
1950 DWORD ret = ERROR_SUCCESS;
1951 int read = 0;
1953 #ifdef HAVE_ZLIB
1954 z_stream *zstream = &req->gzip_stream->zstream;
1955 DWORD buf_avail;
1956 int zres;
1958 while(read < size && !req->gzip_stream->end_of_data) {
1959 if(!req->read_size) {
1960 if(!sync || refill_buffer(req) != ERROR_SUCCESS)
1961 break;
1964 buf_avail = req->dwContentLength == ~0 ? req->read_size : min(req->read_size, req->dwContentLength-req->dwContentRead);
1966 zstream->next_in = req->read_buf+req->read_pos;
1967 zstream->avail_in = buf_avail;
1968 zstream->next_out = buf+read;
1969 zstream->avail_out = size-read;
1970 zres = inflate(zstream, Z_FULL_FLUSH);
1971 read = size - zstream->avail_out;
1972 req->dwContentRead += buf_avail-zstream->avail_in;
1973 remove_data(req, buf_avail-zstream->avail_in);
1974 if(zres == Z_STREAM_END) {
1975 TRACE("end of data\n");
1976 req->gzip_stream->end_of_data = TRUE;
1977 inflateEnd(&req->gzip_stream->zstream);
1978 }else if(zres != Z_OK) {
1979 WARN("inflate failed %d\n", zres);
1980 if(!read)
1981 ret = ERROR_INTERNET_DECODING_FAILED;
1982 break;
1985 #endif
1987 *read_ret = read;
1988 return ret;
1991 static void refill_gzip_buffer(http_request_t *req)
1993 DWORD res;
1994 int len;
1996 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
1997 return;
1999 if(req->gzip_stream->buf_pos) {
2000 if(req->gzip_stream->buf_size)
2001 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2002 req->gzip_stream->buf_pos = 0;
2005 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2006 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2007 if(res == ERROR_SUCCESS)
2008 req->gzip_stream->buf_size += len;
2011 /* return the size of data available to be read immediately (the read section must be held) */
2012 static DWORD get_avail_data( http_request_t *req )
2014 if (req->gzip_stream) {
2015 refill_gzip_buffer(req);
2016 return req->gzip_stream->buf_size;
2018 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2019 return 0;
2020 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2023 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2025 INTERNET_ASYNC_RESULT iar;
2026 DWORD res;
2028 TRACE("%p\n", req);
2030 EnterCriticalSection( &req->read_section );
2031 if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
2032 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2033 iar.dwError = first_notif ? 0 : get_avail_data(req);
2034 }else {
2035 iar.dwResult = 0;
2036 iar.dwError = res;
2038 LeaveCriticalSection( &req->read_section );
2040 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2041 sizeof(INTERNET_ASYNC_RESULT));
2044 /* read data from the http connection (the read section must be held) */
2045 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2047 BOOL finished_reading = FALSE;
2048 int len, bytes_read = 0;
2049 DWORD ret = ERROR_SUCCESS;
2051 EnterCriticalSection( &req->read_section );
2053 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2055 if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
2058 if(req->gzip_stream) {
2059 if(req->gzip_stream->buf_size) {
2060 bytes_read = min(req->gzip_stream->buf_size, size);
2061 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2062 req->gzip_stream->buf_pos += bytes_read;
2063 req->gzip_stream->buf_size -= bytes_read;
2064 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2065 refill_buffer(req);
2068 if(size > bytes_read) {
2069 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2070 if(ret == ERROR_SUCCESS)
2071 bytes_read += len;
2074 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2075 }else {
2076 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2078 if (req->read_size) {
2079 bytes_read = min( req->read_size, size );
2080 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2081 remove_data( req, bytes_read );
2084 if (size > bytes_read && (!bytes_read || sync)) {
2085 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2086 sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2087 bytes_read += len;
2088 /* always return success, even if the network layer returns an error */
2091 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2092 req->dwContentRead += bytes_read;
2094 done:
2095 *read = bytes_read;
2097 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2098 LeaveCriticalSection( &req->read_section );
2100 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2101 BOOL res;
2102 DWORD dwBytesWritten;
2104 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2105 if(!res)
2106 WARN("WriteFile failed: %u\n", GetLastError());
2109 if(finished_reading)
2110 HTTP_FinishedReading(req);
2112 return ret;
2116 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2118 http_request_t *req = (http_request_t*)hdr;
2119 return HTTPREQ_Read(req, buffer, size, read, TRUE);
2122 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2124 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2125 http_request_t *req = (http_request_t*)workRequest->hdr;
2126 INTERNET_ASYNC_RESULT iar;
2127 DWORD res;
2129 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2131 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2132 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2134 iar.dwResult = res == ERROR_SUCCESS;
2135 iar.dwError = res;
2137 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2138 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2139 sizeof(INTERNET_ASYNC_RESULT));
2142 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2143 DWORD flags, DWORD_PTR context)
2145 http_request_t *req = (http_request_t*)hdr;
2146 DWORD res;
2148 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2149 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2151 if (buffers->dwStructSize != sizeof(*buffers))
2152 return ERROR_INVALID_PARAMETER;
2154 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2156 if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
2158 WORKREQUEST workRequest;
2160 if (TryEnterCriticalSection( &req->read_section ))
2162 if (get_avail_data(req))
2164 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2165 &buffers->dwBufferLength, FALSE);
2166 LeaveCriticalSection( &req->read_section );
2167 goto done;
2169 LeaveCriticalSection( &req->read_section );
2172 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2173 workRequest.hdr = WININET_AddRef(&req->hdr);
2174 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2176 INTERNET_AsyncCall(&workRequest);
2178 return ERROR_IO_PENDING;
2181 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2182 !(flags & IRF_NO_WAIT));
2184 done:
2185 if (res == ERROR_SUCCESS) {
2186 DWORD size = buffers->dwBufferLength;
2187 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2188 &size, sizeof(size));
2191 return res;
2194 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2196 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2197 http_request_t *req = (http_request_t*)workRequest->hdr;
2198 INTERNET_ASYNC_RESULT iar;
2199 DWORD res;
2201 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2203 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2204 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2206 iar.dwResult = res == ERROR_SUCCESS;
2207 iar.dwError = res;
2209 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2210 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2211 sizeof(INTERNET_ASYNC_RESULT));
2214 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2215 DWORD flags, DWORD_PTR context)
2218 http_request_t *req = (http_request_t*)hdr;
2219 DWORD res;
2221 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2222 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2224 if (buffers->dwStructSize != sizeof(*buffers))
2225 return ERROR_INVALID_PARAMETER;
2227 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2229 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2231 WORKREQUEST workRequest;
2233 if (TryEnterCriticalSection( &req->read_section ))
2235 if (get_avail_data(req))
2237 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2238 &buffers->dwBufferLength, FALSE);
2239 LeaveCriticalSection( &req->read_section );
2240 goto done;
2242 LeaveCriticalSection( &req->read_section );
2245 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2246 workRequest.hdr = WININET_AddRef(&req->hdr);
2247 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2249 INTERNET_AsyncCall(&workRequest);
2251 return ERROR_IO_PENDING;
2254 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2255 !(flags & IRF_NO_WAIT));
2257 done:
2258 if (res == ERROR_SUCCESS) {
2259 DWORD size = buffers->dwBufferLength;
2260 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2261 &size, sizeof(size));
2264 return res;
2267 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2269 DWORD res;
2270 http_request_t *lpwhr = (http_request_t*)hdr;
2272 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2274 *written = 0;
2275 res = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written);
2276 if (res == ERROR_SUCCESS)
2277 lpwhr->dwBytesWritten += *written;
2279 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2280 return res;
2283 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2285 http_request_t *req = (http_request_t*)workRequest->hdr;
2287 HTTP_ReceiveRequestData(req, FALSE);
2290 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2292 http_request_t *req = (http_request_t*)hdr;
2294 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2296 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2298 WORKREQUEST workRequest;
2300 /* never wait, if we can't enter the section we queue an async request right away */
2301 if (TryEnterCriticalSection( &req->read_section ))
2303 if ((*available = get_avail_data( req ))) goto done;
2304 if (end_of_read_data( req )) goto done;
2305 LeaveCriticalSection( &req->read_section );
2308 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2309 workRequest.hdr = WININET_AddRef( &req->hdr );
2311 INTERNET_AsyncCall(&workRequest);
2313 return ERROR_IO_PENDING;
2316 EnterCriticalSection( &req->read_section );
2318 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2320 refill_buffer( req );
2321 *available = get_avail_data( req );
2324 done:
2325 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2327 DWORD extra;
2328 if (NETCON_query_data_available(&req->netConnection, &extra))
2329 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2331 LeaveCriticalSection( &req->read_section );
2333 TRACE( "returning %u\n", *available );
2334 return ERROR_SUCCESS;
2337 static const object_vtbl_t HTTPREQVtbl = {
2338 HTTPREQ_Destroy,
2339 HTTPREQ_CloseConnection,
2340 HTTPREQ_QueryOption,
2341 HTTPREQ_SetOption,
2342 HTTPREQ_ReadFile,
2343 HTTPREQ_ReadFileExA,
2344 HTTPREQ_ReadFileExW,
2345 HTTPREQ_WriteFile,
2346 HTTPREQ_QueryDataAvailable,
2347 NULL
2350 /***********************************************************************
2351 * HTTP_HttpOpenRequestW (internal)
2353 * Open a HTTP request handle
2355 * RETURNS
2356 * HINTERNET a HTTP request handle on success
2357 * NULL on failure
2360 static DWORD HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2361 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2362 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2363 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2365 appinfo_t *hIC = NULL;
2366 http_request_t *lpwhr;
2367 LPWSTR lpszHostName = NULL;
2368 HINTERNET handle = NULL;
2369 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2370 DWORD len, res;
2372 TRACE("-->\n");
2374 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2375 hIC = lpwhs->lpAppInfo;
2377 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2378 if (NULL == lpwhr)
2380 res = ERROR_OUTOFMEMORY;
2381 goto lend;
2383 lpwhr->hdr.htype = WH_HHTTPREQ;
2384 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2385 lpwhr->hdr.dwFlags = dwFlags;
2386 lpwhr->hdr.dwContext = dwContext;
2387 lpwhr->hdr.refs = 1;
2388 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2389 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2390 lpwhr->dwContentLength = ~0u;
2391 InitializeCriticalSection( &lpwhr->read_section );
2393 WININET_AddRef( &lpwhs->hdr );
2394 lpwhr->lpHttpSession = lpwhs;
2395 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2397 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2398 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2399 if (NULL == lpszHostName)
2401 res = ERROR_OUTOFMEMORY;
2402 goto lend;
2405 handle = WININET_AllocHandle( &lpwhr->hdr );
2406 if (NULL == handle)
2408 res = ERROR_OUTOFMEMORY;
2409 goto lend;
2412 if ((res = NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2414 InternetCloseHandle( handle );
2415 handle = NULL;
2416 goto lend;
2419 if (lpszObjectName && *lpszObjectName) {
2420 HRESULT rc;
2422 len = 0;
2423 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2424 if (rc != E_POINTER)
2425 len = strlenW(lpszObjectName)+1;
2426 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2427 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2428 URL_ESCAPE_SPACES_ONLY);
2429 if (rc != S_OK)
2431 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2432 strcpyW(lpwhr->lpszPath,lpszObjectName);
2434 }else {
2435 static const WCHAR slashW[] = {'/',0};
2437 lpwhr->lpszPath = heap_strdupW(slashW);
2440 if (lpszReferrer && *lpszReferrer)
2441 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2443 if (lpszAcceptTypes)
2445 int i;
2446 for (i = 0; lpszAcceptTypes[i]; i++)
2448 if (!*lpszAcceptTypes[i]) continue;
2449 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2450 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2451 HTTP_ADDHDR_FLAG_REQ |
2452 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2456 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2457 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2459 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2460 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2461 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2463 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2464 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2465 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2467 else
2468 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2469 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2471 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2472 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2473 INTERNET_DEFAULT_HTTPS_PORT :
2474 INTERNET_DEFAULT_HTTP_PORT);
2476 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2477 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2478 INTERNET_DEFAULT_HTTPS_PORT :
2479 INTERNET_DEFAULT_HTTP_PORT);
2481 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2482 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2484 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2485 INTERNET_STATUS_HANDLE_CREATED, &handle,
2486 sizeof(handle));
2488 lend:
2489 HeapFree(GetProcessHeap(), 0, lpszHostName);
2490 if( lpwhr )
2491 WININET_Release( &lpwhr->hdr );
2493 TRACE("<-- %p (%p)\n", handle, lpwhr);
2494 *ret = handle;
2495 return res;
2498 /***********************************************************************
2499 * HttpOpenRequestW (WININET.@)
2501 * Open a HTTP request handle
2503 * RETURNS
2504 * HINTERNET a HTTP request handle on success
2505 * NULL on failure
2508 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2509 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2510 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2511 DWORD dwFlags, DWORD_PTR dwContext)
2513 http_session_t *lpwhs;
2514 HINTERNET handle = NULL;
2515 DWORD res;
2517 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2518 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2519 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2520 dwFlags, dwContext);
2521 if(lpszAcceptTypes!=NULL)
2523 int i;
2524 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2525 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2528 lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
2529 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
2531 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2532 goto lend;
2536 * My tests seem to show that the windows version does not
2537 * become asynchronous until after this point. And anyhow
2538 * if this call was asynchronous then how would you get the
2539 * necessary HINTERNET pointer returned by this function.
2542 res = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
2543 lpszVersion, lpszReferrer, lpszAcceptTypes,
2544 dwFlags, dwContext, &handle);
2545 lend:
2546 if( lpwhs )
2547 WININET_Release( &lpwhs->hdr );
2548 TRACE("returning %p\n", handle);
2549 if(res != ERROR_SUCCESS)
2550 SetLastError(res);
2551 return handle;
2554 /* read any content returned by the server so that the connection can be
2555 * reused */
2556 static void HTTP_DrainContent(http_request_t *req)
2558 DWORD bytes_read;
2560 if (!NETCON_connected(&req->netConnection)) return;
2562 if (req->dwContentLength == -1)
2564 NETCON_close(&req->netConnection);
2565 return;
2567 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2571 char buffer[2048];
2572 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2573 return;
2574 } while (bytes_read);
2577 static const LPCWSTR header_lookup[] = {
2578 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2579 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2580 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2581 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2582 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2583 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2584 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2585 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2586 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2587 szDate, /* HTTP_QUERY_DATE = 9 */
2588 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2589 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2590 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2591 szURI, /* HTTP_QUERY_URI = 13 */
2592 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2593 NULL, /* HTTP_QUERY_COST = 15 */
2594 NULL, /* HTTP_QUERY_LINK = 16 */
2595 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2596 NULL, /* HTTP_QUERY_VERSION = 18 */
2597 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2598 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2599 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2600 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2601 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2602 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2603 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2604 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2605 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2606 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2607 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2608 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2609 NULL, /* HTTP_QUERY_FROM = 31 */
2610 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2611 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2612 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2613 szReferer, /* HTTP_QUERY_REFERER = 35 */
2614 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2615 szServer, /* HTTP_QUERY_SERVER = 37 */
2616 NULL, /* HTTP_TITLE = 38 */
2617 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2618 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2619 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2620 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2621 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2622 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2623 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2624 NULL, /* HTTP_QUERY_REFRESH = 46 */
2625 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2626 szAge, /* HTTP_QUERY_AGE = 48 */
2627 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2628 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2629 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2630 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2631 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2632 szETag, /* HTTP_QUERY_ETAG = 54 */
2633 hostW, /* HTTP_QUERY_HOST = 55 */
2634 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2635 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2636 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2637 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2638 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2639 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2640 szRange, /* HTTP_QUERY_RANGE = 62 */
2641 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2642 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2643 szVary, /* HTTP_QUERY_VARY = 65 */
2644 szVia, /* HTTP_QUERY_VIA = 66 */
2645 szWarning, /* HTTP_QUERY_WARNING = 67 */
2646 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2647 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2648 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2651 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2653 /***********************************************************************
2654 * HTTP_HttpQueryInfoW (internal)
2656 static DWORD HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2657 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2659 LPHTTPHEADERW lphttpHdr = NULL;
2660 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2661 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2662 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2663 INT index = -1;
2665 /* Find requested header structure */
2666 switch (level)
2668 case HTTP_QUERY_CUSTOM:
2669 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
2670 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2671 break;
2672 case HTTP_QUERY_RAW_HEADERS_CRLF:
2674 LPWSTR headers;
2675 DWORD len = 0;
2676 DWORD res = ERROR_INVALID_PARAMETER;
2678 if (request_only)
2679 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2680 else
2681 headers = lpwhr->lpszRawHeaders;
2683 if (headers)
2684 len = strlenW(headers) * sizeof(WCHAR);
2686 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2688 len += sizeof(WCHAR);
2689 res = ERROR_INSUFFICIENT_BUFFER;
2691 else if (lpBuffer)
2693 if (headers)
2694 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2695 else
2697 len = strlenW(szCrLf) * sizeof(WCHAR);
2698 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2700 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2701 res = ERROR_SUCCESS;
2703 *lpdwBufferLength = len;
2705 if (request_only)
2706 HeapFree(GetProcessHeap(), 0, headers);
2707 return res;
2709 case HTTP_QUERY_RAW_HEADERS:
2711 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2712 DWORD i, size = 0;
2713 LPWSTR pszString = lpBuffer;
2715 for (i = 0; ppszRawHeaderLines[i]; i++)
2716 size += strlenW(ppszRawHeaderLines[i]) + 1;
2718 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2720 HTTP_FreeTokens(ppszRawHeaderLines);
2721 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2722 return ERROR_INSUFFICIENT_BUFFER;
2724 if (pszString)
2726 for (i = 0; ppszRawHeaderLines[i]; i++)
2728 DWORD len = strlenW(ppszRawHeaderLines[i]);
2729 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2730 pszString += len+1;
2732 *pszString = '\0';
2733 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2735 *lpdwBufferLength = size * sizeof(WCHAR);
2736 HTTP_FreeTokens(ppszRawHeaderLines);
2738 return ERROR_SUCCESS;
2740 case HTTP_QUERY_STATUS_TEXT:
2741 if (lpwhr->lpszStatusText)
2743 DWORD len = strlenW(lpwhr->lpszStatusText);
2744 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2746 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2747 return ERROR_INSUFFICIENT_BUFFER;
2749 if (lpBuffer)
2751 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2752 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2754 *lpdwBufferLength = len * sizeof(WCHAR);
2755 return ERROR_SUCCESS;
2757 break;
2758 case HTTP_QUERY_VERSION:
2759 if (lpwhr->lpszVersion)
2761 DWORD len = strlenW(lpwhr->lpszVersion);
2762 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2764 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2765 return ERROR_INSUFFICIENT_BUFFER;
2767 if (lpBuffer)
2769 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2770 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2772 *lpdwBufferLength = len * sizeof(WCHAR);
2773 return ERROR_SUCCESS;
2775 break;
2776 case HTTP_QUERY_CONTENT_ENCODING:
2777 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2778 requested_index,request_only);
2779 break;
2780 default:
2781 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2783 if (level < LAST_TABLE_HEADER && header_lookup[level])
2784 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2785 requested_index,request_only);
2788 if (index >= 0)
2789 lphttpHdr = &lpwhr->pCustHeaders[index];
2791 /* Ensure header satisfies requested attributes */
2792 if (!lphttpHdr ||
2793 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2794 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2796 return ERROR_HTTP_HEADER_NOT_FOUND;
2799 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2801 /* coalesce value to requested type */
2802 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2804 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2805 TRACE(" returning number: %d\n", *(int *)lpBuffer);
2807 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2809 time_t tmpTime;
2810 struct tm tmpTM;
2811 SYSTEMTIME *STHook;
2813 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2815 tmpTM = *gmtime(&tmpTime);
2816 STHook = (SYSTEMTIME *)lpBuffer;
2817 STHook->wDay = tmpTM.tm_mday;
2818 STHook->wHour = tmpTM.tm_hour;
2819 STHook->wMilliseconds = 0;
2820 STHook->wMinute = tmpTM.tm_min;
2821 STHook->wDayOfWeek = tmpTM.tm_wday;
2822 STHook->wMonth = tmpTM.tm_mon + 1;
2823 STHook->wSecond = tmpTM.tm_sec;
2824 STHook->wYear = tmpTM.tm_year;
2826 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
2827 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
2828 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
2830 else if (lphttpHdr->lpszValue)
2832 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
2834 if (len > *lpdwBufferLength)
2836 *lpdwBufferLength = len;
2837 return ERROR_INSUFFICIENT_BUFFER;
2839 if (lpBuffer)
2841 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
2842 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
2844 *lpdwBufferLength = len - sizeof(WCHAR);
2846 return ERROR_SUCCESS;
2849 /***********************************************************************
2850 * HttpQueryInfoW (WININET.@)
2852 * Queries for information about an HTTP request
2854 * RETURNS
2855 * TRUE on success
2856 * FALSE on failure
2859 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
2860 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2862 http_request_t *lpwhr;
2863 DWORD res;
2865 if (TRACE_ON(wininet)) {
2866 #define FE(x) { x, #x }
2867 static const wininet_flag_info query_flags[] = {
2868 FE(HTTP_QUERY_MIME_VERSION),
2869 FE(HTTP_QUERY_CONTENT_TYPE),
2870 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
2871 FE(HTTP_QUERY_CONTENT_ID),
2872 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
2873 FE(HTTP_QUERY_CONTENT_LENGTH),
2874 FE(HTTP_QUERY_CONTENT_LANGUAGE),
2875 FE(HTTP_QUERY_ALLOW),
2876 FE(HTTP_QUERY_PUBLIC),
2877 FE(HTTP_QUERY_DATE),
2878 FE(HTTP_QUERY_EXPIRES),
2879 FE(HTTP_QUERY_LAST_MODIFIED),
2880 FE(HTTP_QUERY_MESSAGE_ID),
2881 FE(HTTP_QUERY_URI),
2882 FE(HTTP_QUERY_DERIVED_FROM),
2883 FE(HTTP_QUERY_COST),
2884 FE(HTTP_QUERY_LINK),
2885 FE(HTTP_QUERY_PRAGMA),
2886 FE(HTTP_QUERY_VERSION),
2887 FE(HTTP_QUERY_STATUS_CODE),
2888 FE(HTTP_QUERY_STATUS_TEXT),
2889 FE(HTTP_QUERY_RAW_HEADERS),
2890 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
2891 FE(HTTP_QUERY_CONNECTION),
2892 FE(HTTP_QUERY_ACCEPT),
2893 FE(HTTP_QUERY_ACCEPT_CHARSET),
2894 FE(HTTP_QUERY_ACCEPT_ENCODING),
2895 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
2896 FE(HTTP_QUERY_AUTHORIZATION),
2897 FE(HTTP_QUERY_CONTENT_ENCODING),
2898 FE(HTTP_QUERY_FORWARDED),
2899 FE(HTTP_QUERY_FROM),
2900 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
2901 FE(HTTP_QUERY_LOCATION),
2902 FE(HTTP_QUERY_ORIG_URI),
2903 FE(HTTP_QUERY_REFERER),
2904 FE(HTTP_QUERY_RETRY_AFTER),
2905 FE(HTTP_QUERY_SERVER),
2906 FE(HTTP_QUERY_TITLE),
2907 FE(HTTP_QUERY_USER_AGENT),
2908 FE(HTTP_QUERY_WWW_AUTHENTICATE),
2909 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
2910 FE(HTTP_QUERY_ACCEPT_RANGES),
2911 FE(HTTP_QUERY_SET_COOKIE),
2912 FE(HTTP_QUERY_COOKIE),
2913 FE(HTTP_QUERY_REQUEST_METHOD),
2914 FE(HTTP_QUERY_REFRESH),
2915 FE(HTTP_QUERY_CONTENT_DISPOSITION),
2916 FE(HTTP_QUERY_AGE),
2917 FE(HTTP_QUERY_CACHE_CONTROL),
2918 FE(HTTP_QUERY_CONTENT_BASE),
2919 FE(HTTP_QUERY_CONTENT_LOCATION),
2920 FE(HTTP_QUERY_CONTENT_MD5),
2921 FE(HTTP_QUERY_CONTENT_RANGE),
2922 FE(HTTP_QUERY_ETAG),
2923 FE(HTTP_QUERY_HOST),
2924 FE(HTTP_QUERY_IF_MATCH),
2925 FE(HTTP_QUERY_IF_NONE_MATCH),
2926 FE(HTTP_QUERY_IF_RANGE),
2927 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
2928 FE(HTTP_QUERY_MAX_FORWARDS),
2929 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
2930 FE(HTTP_QUERY_RANGE),
2931 FE(HTTP_QUERY_TRANSFER_ENCODING),
2932 FE(HTTP_QUERY_UPGRADE),
2933 FE(HTTP_QUERY_VARY),
2934 FE(HTTP_QUERY_VIA),
2935 FE(HTTP_QUERY_WARNING),
2936 FE(HTTP_QUERY_CUSTOM)
2938 static const wininet_flag_info modifier_flags[] = {
2939 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
2940 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
2941 FE(HTTP_QUERY_FLAG_NUMBER),
2942 FE(HTTP_QUERY_FLAG_COALESCE)
2944 #undef FE
2945 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
2946 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
2947 DWORD i;
2949 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
2950 TRACE(" Attribute:");
2951 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
2952 if (query_flags[i].val == info) {
2953 TRACE(" %s", query_flags[i].name);
2954 break;
2957 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
2958 TRACE(" Unknown (%08x)", info);
2961 TRACE(" Modifier:");
2962 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
2963 if (modifier_flags[i].val & info_mod) {
2964 TRACE(" %s", modifier_flags[i].name);
2965 info_mod &= ~ modifier_flags[i].val;
2969 if (info_mod) {
2970 TRACE(" Unknown (%08x)", info_mod);
2972 TRACE("\n");
2975 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
2976 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
2978 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2979 goto lend;
2982 if (lpBuffer == NULL)
2983 *lpdwBufferLength = 0;
2984 res = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
2985 lpBuffer, lpdwBufferLength, lpdwIndex);
2987 lend:
2988 if( lpwhr )
2989 WININET_Release( &lpwhr->hdr );
2991 TRACE("%u <--\n", res);
2992 if(res != ERROR_SUCCESS)
2993 SetLastError(res);
2994 return res == ERROR_SUCCESS;
2997 /***********************************************************************
2998 * HttpQueryInfoA (WININET.@)
3000 * Queries for information about an HTTP request
3002 * RETURNS
3003 * TRUE on success
3004 * FALSE on failure
3007 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3008 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3010 BOOL result;
3011 DWORD len;
3012 WCHAR* bufferW;
3014 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3015 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3017 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3018 lpdwBufferLength, lpdwIndex );
3021 if (lpBuffer)
3023 DWORD alloclen;
3024 len = (*lpdwBufferLength)*sizeof(WCHAR);
3025 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3027 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3028 if (alloclen < len)
3029 alloclen = len;
3031 else
3032 alloclen = len;
3033 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3034 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3035 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3036 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3037 } else
3039 bufferW = NULL;
3040 len = 0;
3043 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3044 &len, lpdwIndex );
3045 if( result )
3047 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3048 lpBuffer, *lpdwBufferLength, NULL, NULL );
3049 *lpdwBufferLength = len - 1;
3051 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3053 else
3054 /* since the strings being returned from HttpQueryInfoW should be
3055 * only ASCII characters, it is reasonable to assume that all of
3056 * the Unicode characters can be reduced to a single byte */
3057 *lpdwBufferLength = len / sizeof(WCHAR);
3059 HeapFree(GetProcessHeap(), 0, bufferW );
3061 return result;
3064 /***********************************************************************
3065 * HTTP_GetRedirectURL (internal)
3067 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3069 static WCHAR szHttp[] = {'h','t','t','p',0};
3070 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3071 http_session_t *lpwhs = lpwhr->lpHttpSession;
3072 URL_COMPONENTSW urlComponents;
3073 DWORD url_length = 0;
3074 LPWSTR orig_url;
3075 LPWSTR combined_url;
3077 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3078 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3079 urlComponents.dwSchemeLength = 0;
3080 urlComponents.lpszHostName = lpwhs->lpszHostName;
3081 urlComponents.dwHostNameLength = 0;
3082 urlComponents.nPort = lpwhs->nHostPort;
3083 urlComponents.lpszUserName = lpwhs->lpszUserName;
3084 urlComponents.dwUserNameLength = 0;
3085 urlComponents.lpszPassword = NULL;
3086 urlComponents.dwPasswordLength = 0;
3087 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3088 urlComponents.dwUrlPathLength = 0;
3089 urlComponents.lpszExtraInfo = NULL;
3090 urlComponents.dwExtraInfoLength = 0;
3092 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3093 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3094 return NULL;
3096 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3098 /* convert from bytes to characters */
3099 url_length = url_length / sizeof(WCHAR) - 1;
3100 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3102 HeapFree(GetProcessHeap(), 0, orig_url);
3103 return NULL;
3106 url_length = 0;
3107 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3108 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3110 HeapFree(GetProcessHeap(), 0, orig_url);
3111 return NULL;
3113 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3115 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3117 HeapFree(GetProcessHeap(), 0, orig_url);
3118 HeapFree(GetProcessHeap(), 0, combined_url);
3119 return NULL;
3121 HeapFree(GetProcessHeap(), 0, orig_url);
3122 return combined_url;
3126 /***********************************************************************
3127 * HTTP_HandleRedirect (internal)
3129 static DWORD HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3131 http_session_t *lpwhs = lpwhr->lpHttpSession;
3132 appinfo_t *hIC = lpwhs->lpAppInfo;
3133 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3134 WCHAR path[INTERNET_MAX_URL_LENGTH];
3135 int index;
3137 if(lpszUrl[0]=='/')
3139 /* if it's an absolute path, keep the same session info */
3140 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3142 else
3144 URL_COMPONENTSW urlComponents;
3145 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3146 static WCHAR szHttp[] = {'h','t','t','p',0};
3147 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3149 userName[0] = 0;
3150 hostName[0] = 0;
3151 protocol[0] = 0;
3153 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3154 urlComponents.lpszScheme = protocol;
3155 urlComponents.dwSchemeLength = 32;
3156 urlComponents.lpszHostName = hostName;
3157 urlComponents.dwHostNameLength = MAXHOSTNAME;
3158 urlComponents.lpszUserName = userName;
3159 urlComponents.dwUserNameLength = 1024;
3160 urlComponents.lpszPassword = NULL;
3161 urlComponents.dwPasswordLength = 0;
3162 urlComponents.lpszUrlPath = path;
3163 urlComponents.dwUrlPathLength = 2048;
3164 urlComponents.lpszExtraInfo = NULL;
3165 urlComponents.dwExtraInfoLength = 0;
3166 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3167 return INTERNET_GetLastError();
3169 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3170 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3172 TRACE("redirect from secure page to non-secure page\n");
3173 /* FIXME: warn about from secure redirect to non-secure page */
3174 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3176 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3177 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3179 TRACE("redirect from non-secure page to secure page\n");
3180 /* FIXME: notify about redirect to secure page */
3181 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3184 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3186 if (lstrlenW(protocol)>4) /*https*/
3187 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3188 else /*http*/
3189 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3192 #if 0
3194 * This upsets redirects to binary files on sourceforge.net
3195 * and gives an html page instead of the target file
3196 * Examination of the HTTP request sent by native wininet.dll
3197 * reveals that it doesn't send a referrer in that case.
3198 * Maybe there's a flag that enables this, or maybe a referrer
3199 * shouldn't be added in case of a redirect.
3202 /* consider the current host as the referrer */
3203 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3204 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3205 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3206 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3207 #endif
3209 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3210 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3211 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3213 int len;
3214 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3215 len = lstrlenW(hostName);
3216 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3217 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3218 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3220 else
3221 lpwhs->lpszHostName = heap_strdupW(hostName);
3223 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3225 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3226 lpwhs->lpszUserName = NULL;
3227 if (userName[0])
3228 lpwhs->lpszUserName = heap_strdupW(userName);
3230 if (!using_proxy)
3232 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3234 DWORD res;
3236 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3237 lpwhs->lpszServerName = heap_strdupW(hostName);
3238 lpwhs->nServerPort = urlComponents.nPort;
3240 NETCON_close(&lpwhr->netConnection);
3241 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS)
3242 return res;
3244 res = NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE);
3245 if (res != ERROR_SUCCESS)
3246 return res;
3248 lpwhr->read_pos = lpwhr->read_size = 0;
3249 lpwhr->read_chunked = FALSE;
3252 else
3253 TRACE("Redirect through proxy\n");
3256 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3257 lpwhr->lpszPath=NULL;
3258 if (*path)
3260 DWORD needed = 0;
3261 HRESULT rc;
3263 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3264 if (rc != E_POINTER)
3265 needed = strlenW(path)+1;
3266 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3267 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3268 URL_ESCAPE_SPACES_ONLY);
3269 if (rc != S_OK)
3271 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3272 strcpyW(lpwhr->lpszPath,path);
3276 /* Remove custom content-type/length headers on redirects. */
3277 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3278 if (0 <= index)
3279 HTTP_DeleteCustomHeader(lpwhr, index);
3280 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3281 if (0 <= index)
3282 HTTP_DeleteCustomHeader(lpwhr, index);
3284 return ERROR_SUCCESS;
3287 /***********************************************************************
3288 * HTTP_build_req (internal)
3290 * concatenate all the strings in the request together
3292 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3294 LPCWSTR *t;
3295 LPWSTR str;
3297 for( t = list; *t ; t++ )
3298 len += strlenW( *t );
3299 len++;
3301 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3302 *str = 0;
3304 for( t = list; *t ; t++ )
3305 strcatW( str, *t );
3307 return str;
3310 static DWORD HTTP_SecureProxyConnect(http_request_t *lpwhr)
3312 LPWSTR lpszPath;
3313 LPWSTR requestString;
3314 INT len;
3315 INT cnt;
3316 INT responseLen;
3317 char *ascii_req;
3318 DWORD res;
3319 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3320 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3321 http_session_t *lpwhs = lpwhr->lpHttpSession;
3323 TRACE("\n");
3325 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3326 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3327 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3328 HeapFree( GetProcessHeap(), 0, lpszPath );
3330 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3331 NULL, 0, NULL, NULL );
3332 len--; /* the nul terminator isn't needed */
3333 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3334 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3335 ascii_req, len, NULL, NULL );
3336 HeapFree( GetProcessHeap(), 0, requestString );
3338 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3340 res = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3341 HeapFree( GetProcessHeap(), 0, ascii_req );
3342 if (res != ERROR_SUCCESS)
3343 return res;
3345 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3346 if (!responseLen)
3347 return ERROR_HTTP_INVALID_HEADER;
3349 return ERROR_SUCCESS;
3352 static void HTTP_InsertCookies(http_request_t *lpwhr)
3354 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3355 LPWSTR lpszCookies, lpszUrl = NULL;
3356 DWORD nCookieSize, size;
3357 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3359 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3360 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3361 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3363 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3365 int cnt = 0;
3366 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3368 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3369 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3371 cnt += sprintfW(lpszCookies, szCookie);
3372 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3373 strcatW(lpszCookies, szCrLf);
3375 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3376 HeapFree(GetProcessHeap(), 0, lpszCookies);
3379 HeapFree(GetProcessHeap(), 0, lpszUrl);
3382 /***********************************************************************
3383 * HTTP_HttpSendRequestW (internal)
3385 * Sends the specified request to the HTTP server
3387 * RETURNS
3388 * TRUE on success
3389 * FALSE on failure
3392 static DWORD HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3393 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3394 DWORD dwContentLength, BOOL bEndRequest)
3396 INT cnt;
3397 BOOL redirected = FALSE;
3398 LPWSTR requestString = NULL;
3399 INT responseLen;
3400 BOOL loop_next;
3401 INTERNET_ASYNC_RESULT iar;
3402 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3403 static const WCHAR szContentLength[] =
3404 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3405 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3406 DWORD res;
3408 TRACE("--> %p\n", lpwhr);
3410 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3412 /* if the verb is NULL default to GET */
3413 if (!lpwhr->lpszVerb)
3414 lpwhr->lpszVerb = heap_strdupW(szGET);
3416 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3418 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3419 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3420 lpwhr->dwBytesToWrite = dwContentLength;
3422 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3424 WCHAR *agent_header;
3425 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3426 int len;
3428 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3429 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3430 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3432 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3433 HeapFree(GetProcessHeap(), 0, agent_header);
3435 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3437 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3438 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3440 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3442 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3443 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3444 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3449 DWORD len;
3450 char *ascii_req;
3452 loop_next = FALSE;
3454 /* like native, just in case the caller forgot to call InternetReadFile
3455 * for all the data */
3456 HTTP_DrainContent(lpwhr);
3457 lpwhr->dwContentRead = 0;
3459 if (TRACE_ON(wininet))
3461 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3462 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3465 HTTP_FixURL(lpwhr);
3466 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3468 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3470 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3471 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3473 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3474 HTTP_InsertCookies(lpwhr);
3476 /* add the headers the caller supplied */
3477 if( lpszHeaders && dwHeaderLength )
3479 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3480 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3483 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3485 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3486 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3487 HeapFree(GetProcessHeap(), 0, url);
3489 else
3490 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3493 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3495 /* Send the request and store the results */
3496 if ((res = HTTP_OpenConnection(lpwhr)) != ERROR_SUCCESS)
3497 goto lend;
3499 /* send the request as ASCII, tack on the optional data */
3500 if (!lpOptional || redirected)
3501 dwOptionalLength = 0;
3502 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3503 NULL, 0, NULL, NULL );
3504 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3505 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3506 ascii_req, len, NULL, NULL );
3507 if( lpOptional )
3508 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3509 len = (len + dwOptionalLength - 1);
3510 ascii_req[len] = 0;
3511 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3513 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3514 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3516 res = NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3517 HeapFree( GetProcessHeap(), 0, ascii_req );
3519 lpwhr->dwBytesWritten = dwOptionalLength;
3521 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3522 INTERNET_STATUS_REQUEST_SENT,
3523 &len, sizeof(DWORD));
3525 if (bEndRequest)
3527 DWORD dwBufferSize;
3528 DWORD dwStatusCode;
3530 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3531 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3533 if (res != ERROR_SUCCESS)
3534 goto lend;
3536 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3538 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3539 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3540 sizeof(DWORD));
3542 HTTP_ProcessCookies(lpwhr);
3544 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3546 dwBufferSize = sizeof(dwStatusCode);
3547 if (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3548 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
3549 dwStatusCode = 0;
3551 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
3553 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3554 dwBufferSize=sizeof(szNewLocation);
3555 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
3556 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
3558 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3560 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3561 lpwhr->lpszVerb = heap_strdupW(szGET);
3563 HTTP_DrainContent(lpwhr);
3564 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3566 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3567 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3568 res = HTTP_HandleRedirect(lpwhr, new_url);
3569 if (res == ERROR_SUCCESS)
3571 HeapFree(GetProcessHeap(), 0, requestString);
3572 loop_next = TRUE;
3574 HeapFree( GetProcessHeap(), 0, new_url );
3576 redirected = TRUE;
3579 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
3581 WCHAR szAuthValue[2048];
3582 dwBufferSize=2048;
3583 if (dwStatusCode == HTTP_STATUS_DENIED)
3585 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3586 DWORD dwIndex = 0;
3587 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3589 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3590 &lpwhr->pAuthInfo,
3591 lpwhr->lpHttpSession->lpszUserName,
3592 lpwhr->lpHttpSession->lpszPassword,
3593 Host->lpszValue))
3595 loop_next = TRUE;
3596 break;
3600 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3602 DWORD dwIndex = 0;
3603 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3605 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3606 &lpwhr->pProxyAuthInfo,
3607 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3608 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
3609 NULL))
3611 loop_next = TRUE;
3612 break;
3618 else
3619 res = ERROR_SUCCESS;
3621 while (loop_next);
3623 if(res == ERROR_SUCCESS) {
3624 WCHAR url[INTERNET_MAX_URL_LENGTH];
3625 WCHAR cacheFileName[MAX_PATH+1];
3626 BOOL b;
3628 b = HTTP_GetRequestURL(lpwhr, url);
3629 if(!b) {
3630 WARN("Could not get URL\n");
3631 goto lend;
3634 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3635 if(b) {
3636 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
3637 CloseHandle(lpwhr->hCacheFile);
3639 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3640 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3641 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3642 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3643 WARN("Could not create file: %u\n", GetLastError());
3644 lpwhr->hCacheFile = NULL;
3646 }else {
3647 WARN("Could not create cache entry: %08x\n", GetLastError());
3651 lend:
3653 HeapFree(GetProcessHeap(), 0, requestString);
3655 /* TODO: send notification for P3P header */
3657 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3659 if (res == ERROR_SUCCESS && lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite)
3660 HTTP_ReceiveRequestData(lpwhr, TRUE);
3661 else
3663 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3664 iar.dwError = res;
3666 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3667 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3668 sizeof(INTERNET_ASYNC_RESULT));
3672 TRACE("<--\n");
3673 return res;
3676 /***********************************************************************
3678 * Helper functions for the HttpSendRequest(Ex) functions
3681 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
3683 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
3684 http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
3686 TRACE("%p\n", lpwhr);
3688 HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
3689 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
3690 req->dwContentLength, req->bEndRequest);
3692 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
3696 static DWORD HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
3698 INT responseLen;
3699 DWORD dwBufferSize;
3700 INTERNET_ASYNC_RESULT iar;
3701 DWORD res = ERROR_SUCCESS;
3703 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3704 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3706 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3707 if (!responseLen)
3708 res = ERROR_HTTP_HEADER_NOT_FOUND;
3710 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3711 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
3713 /* process cookies here. Is this right? */
3714 HTTP_ProcessCookies(lpwhr);
3716 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3718 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
3720 DWORD dwCode,dwCodeLength = sizeof(DWORD);
3721 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
3722 && (dwCode == 302 || dwCode == 301 || dwCode == 303))
3724 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3725 dwBufferSize=sizeof(szNewLocation);
3726 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
3728 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3730 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3731 lpwhr->lpszVerb = heap_strdupW(szGET);
3733 HTTP_DrainContent(lpwhr);
3734 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3736 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3737 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3738 res = HTTP_HandleRedirect(lpwhr, new_url);
3739 if (res == ERROR_SUCCESS)
3740 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
3741 HeapFree( GetProcessHeap(), 0, new_url );
3747 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3748 iar.dwError = res;
3750 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3751 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3752 sizeof(INTERNET_ASYNC_RESULT));
3753 return res;
3756 /***********************************************************************
3757 * HttpEndRequestA (WININET.@)
3759 * Ends an HTTP request that was started by HttpSendRequestEx
3761 * RETURNS
3762 * TRUE if successful
3763 * FALSE on failure
3766 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
3767 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3769 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
3771 if (lpBuffersOut)
3773 SetLastError(ERROR_INVALID_PARAMETER);
3774 return FALSE;
3777 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
3780 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
3782 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
3783 http_request_t *lpwhr = (http_request_t*)work->hdr;
3785 TRACE("%p\n", lpwhr);
3787 HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
3790 /***********************************************************************
3791 * HttpEndRequestW (WININET.@)
3793 * Ends an HTTP request that was started by HttpSendRequestEx
3795 * RETURNS
3796 * TRUE if successful
3797 * FALSE on failure
3800 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
3801 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3803 http_request_t *lpwhr;
3804 DWORD res;
3806 TRACE("-->\n");
3808 if (lpBuffersOut)
3810 SetLastError(ERROR_INVALID_PARAMETER);
3811 return FALSE;
3814 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3816 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3818 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3819 if (lpwhr)
3820 WININET_Release( &lpwhr->hdr );
3821 return FALSE;
3823 lpwhr->hdr.dwFlags |= dwFlags;
3825 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3827 WORKREQUEST work;
3828 struct WORKREQ_HTTPENDREQUESTW *request;
3830 work.asyncproc = AsyncHttpEndRequestProc;
3831 work.hdr = WININET_AddRef( &lpwhr->hdr );
3833 request = &work.u.HttpEndRequestW;
3834 request->dwFlags = dwFlags;
3835 request->dwContext = dwContext;
3837 INTERNET_AsyncCall(&work);
3838 res = ERROR_IO_PENDING;
3840 else
3841 res = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
3843 WININET_Release( &lpwhr->hdr );
3844 TRACE("%u <--\n", res);
3845 if(res != ERROR_SUCCESS)
3846 SetLastError(res);
3847 return res == ERROR_SUCCESS;
3850 /***********************************************************************
3851 * HttpSendRequestExA (WININET.@)
3853 * Sends the specified request to the HTTP server and allows chunked
3854 * transfers.
3856 * RETURNS
3857 * Success: TRUE
3858 * Failure: FALSE, call GetLastError() for more information.
3860 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3861 LPINTERNET_BUFFERSA lpBuffersIn,
3862 LPINTERNET_BUFFERSA lpBuffersOut,
3863 DWORD dwFlags, DWORD_PTR dwContext)
3865 INTERNET_BUFFERSW BuffersInW;
3866 BOOL rc = FALSE;
3867 DWORD headerlen;
3868 LPWSTR header = NULL;
3870 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3871 lpBuffersOut, dwFlags, dwContext);
3873 if (lpBuffersIn)
3875 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3876 if (lpBuffersIn->lpcszHeader)
3878 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3879 lpBuffersIn->dwHeadersLength,0,0);
3880 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3881 if (!(BuffersInW.lpcszHeader = header))
3883 SetLastError(ERROR_OUTOFMEMORY);
3884 return FALSE;
3886 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3887 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3888 header, headerlen);
3890 else
3891 BuffersInW.lpcszHeader = NULL;
3892 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3893 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3894 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3895 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3896 BuffersInW.Next = NULL;
3899 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3901 HeapFree(GetProcessHeap(),0,header);
3903 return rc;
3906 /***********************************************************************
3907 * HttpSendRequestExW (WININET.@)
3909 * Sends the specified request to the HTTP server and allows chunked
3910 * transfers
3912 * RETURNS
3913 * Success: TRUE
3914 * Failure: FALSE, call GetLastError() for more information.
3916 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3917 LPINTERNET_BUFFERSW lpBuffersIn,
3918 LPINTERNET_BUFFERSW lpBuffersOut,
3919 DWORD dwFlags, DWORD_PTR dwContext)
3921 http_request_t *lpwhr;
3922 http_session_t *lpwhs;
3923 appinfo_t *hIC;
3924 DWORD res;
3926 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3927 lpBuffersOut, dwFlags, dwContext);
3929 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3931 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3933 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3934 goto lend;
3937 lpwhs = lpwhr->lpHttpSession;
3938 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3939 hIC = lpwhs->lpAppInfo;
3940 assert(hIC->hdr.htype == WH_HINIT);
3942 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3944 WORKREQUEST workRequest;
3945 struct WORKREQ_HTTPSENDREQUESTW *req;
3947 workRequest.asyncproc = AsyncHttpSendRequestProc;
3948 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3949 req = &workRequest.u.HttpSendRequestW;
3950 if (lpBuffersIn)
3952 DWORD size = 0;
3954 if (lpBuffersIn->lpcszHeader)
3956 if (lpBuffersIn->dwHeadersLength == ~0u)
3957 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
3958 else
3959 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
3961 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
3962 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
3964 else req->lpszHeader = NULL;
3966 req->dwHeaderLength = size / sizeof(WCHAR);
3967 req->lpOptional = lpBuffersIn->lpvBuffer;
3968 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3969 req->dwContentLength = lpBuffersIn->dwBufferTotal;
3971 else
3973 req->lpszHeader = NULL;
3974 req->dwHeaderLength = 0;
3975 req->lpOptional = NULL;
3976 req->dwOptionalLength = 0;
3977 req->dwContentLength = 0;
3980 req->bEndRequest = FALSE;
3982 INTERNET_AsyncCall(&workRequest);
3984 * This is from windows.
3986 res = ERROR_IO_PENDING;
3988 else
3990 if (lpBuffersIn)
3991 res = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3992 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
3993 lpBuffersIn->dwBufferTotal, FALSE);
3994 else
3995 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
3998 lend:
3999 if ( lpwhr )
4000 WININET_Release( &lpwhr->hdr );
4002 TRACE("<---\n");
4003 SetLastError(res);
4004 return res == ERROR_SUCCESS;
4007 /***********************************************************************
4008 * HttpSendRequestW (WININET.@)
4010 * Sends the specified request to the HTTP server
4012 * RETURNS
4013 * TRUE on success
4014 * FALSE on failure
4017 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4018 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4020 http_request_t *lpwhr;
4021 http_session_t *lpwhs = NULL;
4022 appinfo_t *hIC = NULL;
4023 DWORD res = ERROR_SUCCESS;
4025 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4026 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4028 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
4029 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4031 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4032 goto lend;
4035 lpwhs = lpwhr->lpHttpSession;
4036 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
4038 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4039 goto lend;
4042 hIC = lpwhs->lpAppInfo;
4043 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
4045 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4046 goto lend;
4049 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4051 WORKREQUEST workRequest;
4052 struct WORKREQ_HTTPSENDREQUESTW *req;
4054 workRequest.asyncproc = AsyncHttpSendRequestProc;
4055 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4056 req = &workRequest.u.HttpSendRequestW;
4057 if (lpszHeaders)
4059 DWORD size;
4061 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4062 else size = dwHeaderLength * sizeof(WCHAR);
4064 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4065 memcpy(req->lpszHeader, lpszHeaders, size);
4067 else
4068 req->lpszHeader = 0;
4069 req->dwHeaderLength = dwHeaderLength;
4070 req->lpOptional = lpOptional;
4071 req->dwOptionalLength = dwOptionalLength;
4072 req->dwContentLength = dwOptionalLength;
4073 req->bEndRequest = TRUE;
4075 INTERNET_AsyncCall(&workRequest);
4077 * This is from windows.
4079 res = ERROR_IO_PENDING;
4081 else
4083 res = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
4084 dwHeaderLength, lpOptional, dwOptionalLength,
4085 dwOptionalLength, TRUE);
4087 lend:
4088 if( lpwhr )
4089 WININET_Release( &lpwhr->hdr );
4091 SetLastError(res);
4092 return res == ERROR_SUCCESS;
4095 /***********************************************************************
4096 * HttpSendRequestA (WININET.@)
4098 * Sends the specified request to the HTTP server
4100 * RETURNS
4101 * TRUE on success
4102 * FALSE on failure
4105 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4106 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4108 BOOL result;
4109 LPWSTR szHeaders=NULL;
4110 DWORD nLen=dwHeaderLength;
4111 if(lpszHeaders!=NULL)
4113 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4114 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4115 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4117 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4118 HeapFree(GetProcessHeap(),0,szHeaders);
4119 return result;
4122 /***********************************************************************
4123 * HTTPSESSION_Destroy (internal)
4125 * Deallocate session handle
4128 static void HTTPSESSION_Destroy(object_header_t *hdr)
4130 http_session_t *lpwhs = (http_session_t*) hdr;
4132 TRACE("%p\n", lpwhs);
4134 WININET_Release(&lpwhs->lpAppInfo->hdr);
4136 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4137 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4138 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4139 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4140 HeapFree(GetProcessHeap(), 0, lpwhs);
4143 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4145 switch(option) {
4146 case INTERNET_OPTION_HANDLE_TYPE:
4147 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4149 if (*size < sizeof(ULONG))
4150 return ERROR_INSUFFICIENT_BUFFER;
4152 *size = sizeof(DWORD);
4153 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4154 return ERROR_SUCCESS;
4157 return INET_QueryOption(option, buffer, size, unicode);
4160 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4162 http_session_t *ses = (http_session_t*)hdr;
4164 switch(option) {
4165 case INTERNET_OPTION_USERNAME:
4167 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4168 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4169 return ERROR_SUCCESS;
4171 case INTERNET_OPTION_PASSWORD:
4173 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4174 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4175 return ERROR_SUCCESS;
4177 default: break;
4180 return ERROR_INTERNET_INVALID_OPTION;
4183 static const object_vtbl_t HTTPSESSIONVtbl = {
4184 HTTPSESSION_Destroy,
4185 NULL,
4186 HTTPSESSION_QueryOption,
4187 HTTPSESSION_SetOption,
4188 NULL,
4189 NULL,
4190 NULL,
4191 NULL,
4192 NULL
4196 /***********************************************************************
4197 * HTTP_Connect (internal)
4199 * Create http session handle
4201 * RETURNS
4202 * HINTERNET a session handle on success
4203 * NULL on failure
4206 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4207 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4208 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4209 DWORD dwInternalFlags, HINTERNET *ret)
4211 http_session_t *lpwhs = NULL;
4212 HINTERNET handle = NULL;
4213 DWORD res = ERROR_SUCCESS;
4215 TRACE("-->\n");
4217 if (!lpszServerName || !lpszServerName[0])
4218 return ERROR_INVALID_PARAMETER;
4220 assert( hIC->hdr.htype == WH_HINIT );
4222 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4223 if (!lpwhs)
4224 return ERROR_OUTOFMEMORY;
4227 * According to my tests. The name is not resolved until a request is sent
4230 lpwhs->hdr.htype = WH_HHTTPSESSION;
4231 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4232 lpwhs->hdr.dwFlags = dwFlags;
4233 lpwhs->hdr.dwContext = dwContext;
4234 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4235 lpwhs->hdr.refs = 1;
4236 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4238 WININET_AddRef( &hIC->hdr );
4239 lpwhs->lpAppInfo = hIC;
4240 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4242 handle = WININET_AllocHandle( &lpwhs->hdr );
4243 if (NULL == handle)
4245 ERR("Failed to alloc handle\n");
4246 res = ERROR_OUTOFMEMORY;
4247 goto lerror;
4250 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4251 if(strchrW(hIC->lpszProxy, ' '))
4252 FIXME("Several proxies not implemented.\n");
4253 if(hIC->lpszProxyBypass)
4254 FIXME("Proxy bypass is ignored.\n");
4256 if (lpszServerName && lpszServerName[0])
4258 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4259 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4261 if (lpszUserName && lpszUserName[0])
4262 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4263 if (lpszPassword && lpszPassword[0])
4264 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4265 lpwhs->nServerPort = nServerPort;
4266 lpwhs->nHostPort = nServerPort;
4268 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4269 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4271 INTERNET_SendCallback(&hIC->hdr, dwContext,
4272 INTERNET_STATUS_HANDLE_CREATED, &handle,
4273 sizeof(handle));
4276 lerror:
4277 if( lpwhs )
4278 WININET_Release( &lpwhs->hdr );
4281 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4282 * windows
4285 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4287 if(res == ERROR_SUCCESS)
4288 *ret = handle;
4289 return res;
4293 /***********************************************************************
4294 * HTTP_OpenConnection (internal)
4296 * Connect to a web server
4298 * RETURNS
4300 * TRUE on success
4301 * FALSE on failure
4303 static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
4305 http_session_t *lpwhs;
4306 appinfo_t *hIC = NULL;
4307 char szaddr[INET6_ADDRSTRLEN];
4308 const void *addr;
4309 DWORD res = ERROR_SUCCESS;
4311 TRACE("-->\n");
4314 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4316 res = ERROR_INVALID_PARAMETER;
4317 goto lend;
4320 if (NETCON_connected(&lpwhr->netConnection))
4321 goto lend;
4322 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
4324 lpwhs = lpwhr->lpHttpSession;
4326 hIC = lpwhs->lpAppInfo;
4327 switch (lpwhs->socketAddress.ss_family)
4329 case AF_INET:
4330 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4331 break;
4332 case AF_INET6:
4333 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4334 break;
4335 default:
4336 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4337 return ERROR_INTERNET_NAME_NOT_RESOLVED;
4339 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4340 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4341 INTERNET_STATUS_CONNECTING_TO_SERVER,
4342 szaddr,
4343 strlen(szaddr)+1);
4345 res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
4346 if (res != ERROR_SUCCESS)
4348 WARN("Socket creation failed: %u\n", res);
4349 goto lend;
4352 res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4353 lpwhs->sa_len);
4354 if(res != ERROR_SUCCESS)
4355 goto lend;
4357 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4359 /* Note: we differ from Microsoft's WinINet here. they seem to have
4360 * a bug that causes no status callbacks to be sent when starting
4361 * a tunnel to a proxy server using the CONNECT verb. i believe our
4362 * behaviour to be more correct and to not cause any incompatibilities
4363 * because using a secure connection through a proxy server is a rare
4364 * case that would be hard for anyone to depend on */
4365 if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS)
4366 goto lend;
4368 res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
4369 if(res != ERROR_SUCCESS)
4371 WARN("Couldn't connect securely to host\n");
4372 goto lend;
4376 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4377 INTERNET_STATUS_CONNECTED_TO_SERVER,
4378 szaddr, strlen(szaddr)+1);
4380 lend:
4381 lpwhr->read_pos = lpwhr->read_size = 0;
4382 lpwhr->read_chunked = FALSE;
4384 TRACE("%d <--\n", res);
4385 return res;
4389 /***********************************************************************
4390 * HTTP_clear_response_headers (internal)
4392 * clear out any old response headers
4394 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4396 DWORD i;
4398 for( i=0; i<lpwhr->nCustHeaders; i++)
4400 if( !lpwhr->pCustHeaders[i].lpszField )
4401 continue;
4402 if( !lpwhr->pCustHeaders[i].lpszValue )
4403 continue;
4404 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4405 continue;
4406 HTTP_DeleteCustomHeader( lpwhr, i );
4407 i--;
4411 /***********************************************************************
4412 * HTTP_GetResponseHeaders (internal)
4414 * Read server response
4416 * RETURNS
4418 * TRUE on success
4419 * FALSE on error
4421 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4423 INT cbreaks = 0;
4424 WCHAR buffer[MAX_REPLY_LEN];
4425 DWORD buflen = MAX_REPLY_LEN;
4426 BOOL bSuccess = FALSE;
4427 INT rc = 0;
4428 char bufferA[MAX_REPLY_LEN];
4429 LPWSTR status_code = NULL, status_text = NULL;
4430 DWORD cchMaxRawHeaders = 1024;
4431 LPWSTR lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4432 LPWSTR temp;
4433 DWORD cchRawHeaders = 0;
4434 BOOL codeHundred = FALSE;
4436 TRACE("-->\n");
4438 /* clear old response headers (eg. from a redirect response) */
4439 if (clear) HTTP_clear_response_headers( lpwhr );
4441 if (!NETCON_connected(&lpwhr->netConnection))
4442 goto lend;
4444 do {
4445 static const WCHAR szHundred[] = {'1','0','0',0};
4447 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4449 buflen = MAX_REPLY_LEN;
4450 if (!read_line(lpwhr, bufferA, &buflen))
4451 goto lend;
4452 rc += buflen;
4453 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4454 /* check is this a status code line? */
4455 if (!strncmpW(buffer, g_szHttp1_0, 4))
4457 /* split the version from the status code */
4458 status_code = strchrW( buffer, ' ' );
4459 if( !status_code )
4460 goto lend;
4461 *status_code++=0;
4463 /* split the status code from the status text */
4464 status_text = strchrW( status_code, ' ' );
4465 if( !status_text )
4466 goto lend;
4467 *status_text++=0;
4469 TRACE("version [%s] status code [%s] status text [%s]\n",
4470 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4472 codeHundred = (!strcmpW(status_code, szHundred));
4474 else if (!codeHundred)
4476 FIXME("Non status line at head of response (%s)\n",debugstr_w(buffer));
4477 goto lend;
4479 } while (codeHundred);
4481 /* Add status code */
4482 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4483 HTTP_ADDHDR_FLAG_REPLACE);
4485 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4486 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4488 lpwhr->lpszVersion = heap_strdupW(buffer);
4489 lpwhr->lpszStatusText = heap_strdupW(status_text);
4491 /* Restore the spaces */
4492 *(status_code-1) = ' ';
4493 *(status_text-1) = ' ';
4495 /* regenerate raw headers */
4496 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4497 cchMaxRawHeaders *= 2;
4498 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4499 if (temp == NULL) goto lend;
4500 lpszRawHeaders = temp;
4501 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4502 cchRawHeaders += (buflen-1);
4503 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4504 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4505 lpszRawHeaders[cchRawHeaders] = '\0';
4507 /* Parse each response line */
4510 buflen = MAX_REPLY_LEN;
4511 if (read_line(lpwhr, bufferA, &buflen))
4513 LPWSTR * pFieldAndValue;
4515 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4517 if (!bufferA[0]) break;
4518 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4520 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4521 if (pFieldAndValue)
4523 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4524 cchMaxRawHeaders *= 2;
4525 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4526 if (temp == NULL) goto lend;
4527 lpszRawHeaders = temp;
4528 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4529 cchRawHeaders += (buflen-1);
4530 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4531 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4532 lpszRawHeaders[cchRawHeaders] = '\0';
4534 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4535 HTTP_ADDREQ_FLAG_ADD );
4537 HTTP_FreeTokens(pFieldAndValue);
4540 else
4542 cbreaks++;
4543 if (cbreaks >= 2)
4544 break;
4546 }while(1);
4548 /* make sure the response header is terminated with an empty line. Some apps really
4549 truly care about that empty line being there for some reason. Just add it to the
4550 header. */
4551 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4553 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4554 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4555 if (temp == NULL) goto lend;
4556 lpszRawHeaders = temp;
4559 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4561 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4562 lpwhr->lpszRawHeaders = lpszRawHeaders;
4563 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4564 bSuccess = TRUE;
4566 lend:
4568 TRACE("<--\n");
4569 if (bSuccess)
4570 return rc;
4571 else
4573 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4574 return 0;
4578 /***********************************************************************
4579 * HTTP_InterpretHttpHeader (internal)
4581 * Parse server response
4583 * RETURNS
4585 * Pointer to array of field, value, NULL on success.
4586 * NULL on error.
4588 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4590 LPWSTR * pTokenPair;
4591 LPWSTR pszColon;
4592 INT len;
4594 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4596 pszColon = strchrW(buffer, ':');
4597 /* must have two tokens */
4598 if (!pszColon)
4600 HTTP_FreeTokens(pTokenPair);
4601 if (buffer[0])
4602 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4603 return NULL;
4606 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4607 if (!pTokenPair[0])
4609 HTTP_FreeTokens(pTokenPair);
4610 return NULL;
4612 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4613 pTokenPair[0][pszColon - buffer] = '\0';
4615 /* skip colon */
4616 pszColon++;
4617 len = strlenW(pszColon);
4618 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4619 if (!pTokenPair[1])
4621 HTTP_FreeTokens(pTokenPair);
4622 return NULL;
4624 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4626 strip_spaces(pTokenPair[0]);
4627 strip_spaces(pTokenPair[1]);
4629 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4630 return pTokenPair;
4633 /***********************************************************************
4634 * HTTP_ProcessHeader (internal)
4636 * Stuff header into header tables according to <dwModifier>
4640 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4642 static DWORD HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4644 LPHTTPHEADERW lphttpHdr = NULL;
4645 INT index = -1;
4646 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4647 DWORD res = ERROR_HTTP_INVALID_HEADER;
4649 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4651 /* REPLACE wins out over ADD */
4652 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4653 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4655 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4656 index = -1;
4657 else
4658 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4660 if (index >= 0)
4662 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4663 return ERROR_HTTP_INVALID_HEADER;
4664 lphttpHdr = &lpwhr->pCustHeaders[index];
4666 else if (value)
4668 HTTPHEADERW hdr;
4670 hdr.lpszField = (LPWSTR)field;
4671 hdr.lpszValue = (LPWSTR)value;
4672 hdr.wFlags = hdr.wCount = 0;
4674 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4675 hdr.wFlags |= HDR_ISREQUEST;
4677 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4679 /* no value to delete */
4680 else return ERROR_SUCCESS;
4682 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4683 lphttpHdr->wFlags |= HDR_ISREQUEST;
4684 else
4685 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4687 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4689 HTTP_DeleteCustomHeader( lpwhr, index );
4691 if (value)
4693 HTTPHEADERW hdr;
4695 hdr.lpszField = (LPWSTR)field;
4696 hdr.lpszValue = (LPWSTR)value;
4697 hdr.wFlags = hdr.wCount = 0;
4699 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4700 hdr.wFlags |= HDR_ISREQUEST;
4702 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4705 return ERROR_SUCCESS;
4707 else if (dwModifier & COALESCEFLAGS)
4709 LPWSTR lpsztmp;
4710 WCHAR ch = 0;
4711 INT len = 0;
4712 INT origlen = strlenW(lphttpHdr->lpszValue);
4713 INT valuelen = strlenW(value);
4715 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4717 ch = ',';
4718 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4720 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4722 ch = ';';
4723 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4726 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4728 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4729 if (lpsztmp)
4731 lphttpHdr->lpszValue = lpsztmp;
4732 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4733 if (ch > 0)
4735 lphttpHdr->lpszValue[origlen] = ch;
4736 origlen++;
4737 lphttpHdr->lpszValue[origlen] = ' ';
4738 origlen++;
4741 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4742 lphttpHdr->lpszValue[len] = '\0';
4743 res = ERROR_SUCCESS;
4745 else
4747 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4748 res = ERROR_OUTOFMEMORY;
4751 TRACE("<-- %d\n", res);
4752 return res;
4756 /***********************************************************************
4757 * HTTP_FinishedReading (internal)
4759 * Called when all content from server has been read by client.
4762 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4764 BOOL keepalive = HTTP_KeepAlive(lpwhr);
4766 TRACE("\n");
4769 if (!keepalive)
4771 HTTPREQ_CloseConnection(&lpwhr->hdr);
4774 /* FIXME: store data in the URL cache here */
4776 return TRUE;
4780 /***********************************************************************
4781 * HTTP_GetCustomHeaderIndex (internal)
4783 * Return index of custom header from header array
4786 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
4787 int requested_index, BOOL request_only)
4789 DWORD index;
4791 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
4793 for (index = 0; index < lpwhr->nCustHeaders; index++)
4795 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
4796 continue;
4798 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4799 continue;
4801 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4802 continue;
4804 if (requested_index == 0)
4805 break;
4806 requested_index --;
4809 if (index >= lpwhr->nCustHeaders)
4810 index = -1;
4812 TRACE("Return: %d\n", index);
4813 return index;
4817 /***********************************************************************
4818 * HTTP_InsertCustomHeader (internal)
4820 * Insert header into array
4823 static DWORD HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
4825 INT count;
4826 LPHTTPHEADERW lph = NULL;
4828 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
4829 count = lpwhr->nCustHeaders + 1;
4830 if (count > 1)
4831 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
4832 else
4833 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
4835 if (!lph)
4836 return ERROR_OUTOFMEMORY;
4838 lpwhr->pCustHeaders = lph;
4839 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
4840 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
4841 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
4842 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
4843 lpwhr->nCustHeaders++;
4845 return ERROR_SUCCESS;
4849 /***********************************************************************
4850 * HTTP_DeleteCustomHeader (internal)
4852 * Delete header from array
4853 * If this function is called, the indexs may change.
4855 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
4857 if( lpwhr->nCustHeaders <= 0 )
4858 return FALSE;
4859 if( index >= lpwhr->nCustHeaders )
4860 return FALSE;
4861 lpwhr->nCustHeaders--;
4863 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
4864 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
4866 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
4867 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
4868 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
4870 return TRUE;
4874 /***********************************************************************
4875 * HTTP_VerifyValidHeader (internal)
4877 * Verify the given header is not invalid for the given http request
4880 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
4882 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4883 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
4884 return ERROR_HTTP_INVALID_HEADER;
4886 return ERROR_SUCCESS;
4889 /***********************************************************************
4890 * IsHostInProxyBypassList (@)
4892 * Undocumented
4895 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
4897 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
4898 return FALSE;