rstrtmgr: Add new stubbed dll rstrtmgr.dll.
[wine/hramrach.git] / dlls / wininet / http.c
blobf5ae426cd3ba5e3cd1d8c82480dc787cb95bb65f
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 szOK[] = {'O','K',0};
77 static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
78 static const WCHAR hostW[] = { 'H','o','s','t',0 };
79 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
80 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
81 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
82 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
83 static const WCHAR szGET[] = { 'G','E','T', 0 };
84 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
85 static const WCHAR szCrLf[] = {'\r','\n', 0};
87 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
88 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
89 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
90 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
91 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
92 static const WCHAR szAge[] = { 'A','g','e',0 };
93 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
94 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
95 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
96 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
97 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
98 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
99 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
100 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
101 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
102 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
103 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
104 static const WCHAR szContent_Transfer_Encoding[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
105 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
106 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
107 static const WCHAR szDate[] = { 'D','a','t','e',0 };
108 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
109 static const WCHAR szETag[] = { 'E','T','a','g',0 };
110 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
111 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
112 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
113 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
114 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
115 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
116 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
117 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
118 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
119 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
120 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
121 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
122 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
123 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
124 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
125 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
126 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
127 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
128 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
129 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
130 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
131 static const WCHAR szUnless_Modified_Since[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
132 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
133 static const WCHAR szURI[] = { 'U','R','I',0 };
134 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
135 static const WCHAR szVary[] = { 'V','a','r','y',0 };
136 static const WCHAR szVia[] = { 'V','i','a',0 };
137 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
138 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
140 #define MAXHOSTNAME 100
141 #define MAX_FIELD_VALUE_LEN 256
142 #define MAX_FIELD_LEN 256
144 #define HTTP_REFERER szReferer
145 #define HTTP_ACCEPT szAccept
146 #define HTTP_USERAGENT szUser_Agent
148 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
149 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
150 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
151 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
152 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
153 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
154 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
156 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
158 struct HttpAuthInfo
160 LPWSTR scheme;
161 CredHandle cred;
162 CtxtHandle ctx;
163 TimeStamp exp;
164 ULONG attr;
165 ULONG max_token;
166 void *auth_data;
167 unsigned int auth_data_len;
168 BOOL finished; /* finished authenticating */
172 struct gzip_stream_t {
173 #ifdef HAVE_ZLIB
174 z_stream zstream;
175 #endif
176 BYTE buf[8192];
177 DWORD buf_size;
178 DWORD buf_pos;
179 BOOL end_of_data;
182 typedef struct _basicAuthorizationData
184 struct list entry;
186 LPWSTR lpszwHost;
187 LPWSTR lpszwRealm;
188 LPSTR lpszAuthorization;
189 UINT AuthorizationLen;
190 } basicAuthorizationData;
192 typedef struct _authorizationData
194 struct list entry;
196 LPWSTR host;
197 LPWSTR scheme;
198 LPWSTR domain;
199 UINT domain_len;
200 LPWSTR user;
201 UINT user_len;
202 LPWSTR password;
203 UINT password_len;
204 } authorizationData;
206 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
207 static struct list authorizationCache = LIST_INIT(authorizationCache);
209 static CRITICAL_SECTION authcache_cs;
210 static CRITICAL_SECTION_DEBUG critsect_debug =
212 0, 0, &authcache_cs,
213 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
214 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
216 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
218 static DWORD HTTP_OpenConnection(http_request_t *req);
219 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
220 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
221 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
222 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
223 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
224 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
225 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
226 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
227 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
228 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
229 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
230 static void HTTP_DrainContent(http_request_t *req);
231 static BOOL HTTP_FinishedReading(http_request_t *req);
233 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
235 int HeaderIndex = 0;
236 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
237 if (HeaderIndex == -1)
238 return NULL;
239 else
240 return &req->pCustHeaders[HeaderIndex];
243 #ifdef HAVE_ZLIB
245 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
247 return HeapAlloc(GetProcessHeap(), 0, items*size);
250 static void wininet_zfree(voidpf opaque, voidpf address)
252 HeapFree(GetProcessHeap(), 0, address);
255 static void init_gzip_stream(http_request_t *req)
257 gzip_stream_t *gzip_stream;
258 int index, zres;
260 gzip_stream = HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t));
261 gzip_stream->zstream.zalloc = wininet_zalloc;
262 gzip_stream->zstream.zfree = wininet_zfree;
263 gzip_stream->zstream.opaque = NULL;
264 gzip_stream->zstream.next_in = NULL;
265 gzip_stream->zstream.avail_in = 0;
266 gzip_stream->zstream.next_out = NULL;
267 gzip_stream->zstream.avail_out = 0;
268 gzip_stream->buf_pos = 0;
269 gzip_stream->buf_size = 0;
270 gzip_stream->end_of_data = FALSE;
272 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
273 if(zres != Z_OK) {
274 ERR("inflateInit failed: %d\n", zres);
275 HeapFree(GetProcessHeap(), 0, gzip_stream);
276 return;
279 req->gzip_stream = gzip_stream;
281 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
282 if(index != -1)
283 HTTP_DeleteCustomHeader(req, index);
286 #else
288 static void init_gzip_stream(http_request_t *req)
290 ERR("gzip stream not supported, missing zlib.\n");
293 #endif
295 /* set the request content length based on the headers */
296 static DWORD set_content_length( http_request_t *lpwhr )
298 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
299 WCHAR encoding[20];
300 DWORD size;
302 size = sizeof(lpwhr->dwContentLength);
303 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
304 &lpwhr->dwContentLength, &size, NULL) != ERROR_SUCCESS)
305 lpwhr->dwContentLength = ~0u;
307 size = sizeof(encoding);
308 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
309 !strcmpiW(encoding, szChunked))
311 lpwhr->dwContentLength = ~0u;
312 lpwhr->read_chunked = TRUE;
315 if(lpwhr->decoding) {
316 int encoding_idx;
318 static const WCHAR gzipW[] = {'g','z','i','p',0};
320 encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
321 if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
322 init_gzip_stream(lpwhr);
325 return lpwhr->dwContentLength;
328 /***********************************************************************
329 * HTTP_Tokenize (internal)
331 * Tokenize a string, allocating memory for the tokens.
333 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
335 LPWSTR * token_array;
336 int tokens = 0;
337 int i;
338 LPCWSTR next_token;
340 if (string)
342 /* empty string has no tokens */
343 if (*string)
344 tokens++;
345 /* count tokens */
346 for (i = 0; string[i]; i++)
348 if (!strncmpW(string+i, token_string, strlenW(token_string)))
350 DWORD j;
351 tokens++;
352 /* we want to skip over separators, but not the null terminator */
353 for (j = 0; j < strlenW(token_string) - 1; j++)
354 if (!string[i+j])
355 break;
356 i += j;
361 /* add 1 for terminating NULL */
362 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
363 token_array[tokens] = NULL;
364 if (!tokens)
365 return token_array;
366 for (i = 0; i < tokens; i++)
368 int len;
369 next_token = strstrW(string, token_string);
370 if (!next_token) next_token = string+strlenW(string);
371 len = next_token - string;
372 token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
373 memcpy(token_array[i], string, len*sizeof(WCHAR));
374 token_array[i][len] = '\0';
375 string = next_token+strlenW(token_string);
377 return token_array;
380 /***********************************************************************
381 * HTTP_FreeTokens (internal)
383 * Frees memory returned from HTTP_Tokenize.
385 static void HTTP_FreeTokens(LPWSTR * token_array)
387 int i;
388 for (i = 0; token_array[i]; i++)
389 HeapFree(GetProcessHeap(), 0, token_array[i]);
390 HeapFree(GetProcessHeap(), 0, token_array);
393 static void HTTP_FixURL(http_request_t *lpwhr)
395 static const WCHAR szSlash[] = { '/',0 };
396 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
398 /* If we don't have a path we set it to root */
399 if (NULL == lpwhr->lpszPath)
400 lpwhr->lpszPath = heap_strdupW(szSlash);
401 else /* remove \r and \n*/
403 int nLen = strlenW(lpwhr->lpszPath);
404 while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
406 nLen--;
407 lpwhr->lpszPath[nLen]='\0';
409 /* Replace '\' with '/' */
410 while (nLen>0) {
411 nLen--;
412 if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
416 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
417 lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
418 && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
420 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
421 (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
422 *fixurl = '/';
423 strcpyW(fixurl + 1, lpwhr->lpszPath);
424 HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
425 lpwhr->lpszPath = fixurl;
429 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
431 LPWSTR requestString;
432 DWORD len, n;
433 LPCWSTR *req;
434 UINT i;
435 LPWSTR p;
437 static const WCHAR szSpace[] = { ' ',0 };
438 static const WCHAR szColon[] = { ':',' ',0 };
439 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
441 /* allocate space for an array of all the string pointers to be added */
442 len = (lpwhr->nCustHeaders)*4 + 10;
443 req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
445 /* add the verb, path and HTTP version string */
446 n = 0;
447 req[n++] = verb;
448 req[n++] = szSpace;
449 req[n++] = path;
450 req[n++] = szSpace;
451 req[n++] = version;
453 /* Append custom request headers */
454 for (i = 0; i < lpwhr->nCustHeaders; i++)
456 if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
458 req[n++] = szCrLf;
459 req[n++] = lpwhr->pCustHeaders[i].lpszField;
460 req[n++] = szColon;
461 req[n++] = lpwhr->pCustHeaders[i].lpszValue;
463 TRACE("Adding custom header %s (%s)\n",
464 debugstr_w(lpwhr->pCustHeaders[i].lpszField),
465 debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
469 if( n >= len )
470 ERR("oops. buffer overrun\n");
472 req[n] = NULL;
473 requestString = HTTP_build_req( req, 4 );
474 HeapFree( GetProcessHeap(), 0, req );
477 * Set (header) termination string for request
478 * Make sure there's exactly two new lines at the end of the request
480 p = &requestString[strlenW(requestString)-1];
481 while ( (*p == '\n') || (*p == '\r') )
482 p--;
483 strcpyW( p+1, sztwocrlf );
485 return requestString;
488 static void HTTP_ProcessCookies( http_request_t *lpwhr )
490 int HeaderIndex;
491 int numCookies = 0;
492 LPHTTPHEADERW setCookieHeader;
494 while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
496 setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
498 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
500 int len;
501 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
502 LPWSTR buf_url;
503 LPHTTPHEADERW Host;
505 Host = HTTP_GetHeader(lpwhr, hostW);
506 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
507 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
508 sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
509 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
511 HeapFree(GetProcessHeap(), 0, buf_url);
513 numCookies++;
517 static void strip_spaces(LPWSTR start)
519 LPWSTR str = start;
520 LPWSTR end;
522 while (*str == ' ' && *str != '\0')
523 str++;
525 if (str != start)
526 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
528 end = start + strlenW(start) - 1;
529 while (end >= start && *end == ' ')
531 *end = '\0';
532 end--;
536 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
538 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
539 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
540 BOOL is_basic;
541 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
542 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
543 if (is_basic && pszRealm)
545 LPCWSTR token;
546 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
547 LPCWSTR realm;
548 ptr++;
549 *pszRealm=NULL;
550 token = strchrW(ptr,'=');
551 if (!token)
552 return TRUE;
553 realm = ptr;
554 while (*realm == ' ' && *realm != '\0')
555 realm++;
556 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
557 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
559 token++;
560 while (*token == ' ' && *token != '\0')
561 token++;
562 if (*token == '\0')
563 return TRUE;
564 *pszRealm = heap_strdupW(token);
565 strip_spaces(*pszRealm);
569 return is_basic;
572 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
574 if (!authinfo) return;
576 if (SecIsValidHandle(&authinfo->ctx))
577 DeleteSecurityContext(&authinfo->ctx);
578 if (SecIsValidHandle(&authinfo->cred))
579 FreeCredentialsHandle(&authinfo->cred);
581 HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
582 HeapFree(GetProcessHeap(), 0, authinfo->scheme);
583 HeapFree(GetProcessHeap(), 0, authinfo);
586 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
588 basicAuthorizationData *ad;
589 UINT rc = 0;
591 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
593 EnterCriticalSection(&authcache_cs);
594 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
596 if (!strcmpiW(host,ad->lpszwHost) && !strcmpW(realm,ad->lpszwRealm))
598 TRACE("Authorization found in cache\n");
599 *auth_data = HeapAlloc(GetProcessHeap(),0,ad->AuthorizationLen);
600 memcpy(*auth_data,ad->lpszAuthorization,ad->AuthorizationLen);
601 rc = ad->AuthorizationLen;
602 break;
605 LeaveCriticalSection(&authcache_cs);
606 return rc;
609 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
611 struct list *cursor;
612 basicAuthorizationData* ad = NULL;
614 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
616 EnterCriticalSection(&authcache_cs);
617 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
619 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
620 if (!strcmpiW(host,check->lpszwHost) && !strcmpW(realm,check->lpszwRealm))
622 ad = check;
623 break;
627 if (ad)
629 TRACE("Found match in cache, replacing\n");
630 HeapFree(GetProcessHeap(),0,ad->lpszAuthorization);
631 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
632 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
633 ad->AuthorizationLen = auth_data_len;
635 else
637 ad = HeapAlloc(GetProcessHeap(),0,sizeof(basicAuthorizationData));
638 ad->lpszwHost = heap_strdupW(host);
639 ad->lpszwRealm = heap_strdupW(realm);
640 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
641 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
642 ad->AuthorizationLen = auth_data_len;
643 list_add_head(&basicAuthorizationCache,&ad->entry);
644 TRACE("authorization cached\n");
646 LeaveCriticalSection(&authcache_cs);
649 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
650 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
652 authorizationData *ad;
654 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
656 EnterCriticalSection(&authcache_cs);
657 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
658 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
659 TRACE("Authorization found in cache\n");
661 nt_auth_identity->User = heap_strdupW(ad->user);
662 nt_auth_identity->Password = heap_strdupW(ad->password);
663 nt_auth_identity->Domain = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*ad->domain_len);
664 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
665 (!nt_auth_identity->Domain && ad->domain_len)) {
666 HeapFree(GetProcessHeap(), 0, nt_auth_identity->User);
667 HeapFree(GetProcessHeap(), 0, nt_auth_identity->Password);
668 HeapFree(GetProcessHeap(), 0, nt_auth_identity->Domain);
669 break;
672 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
673 nt_auth_identity->UserLength = ad->user_len;
674 nt_auth_identity->PasswordLength = ad->password_len;
675 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
676 nt_auth_identity->DomainLength = ad->domain_len;
677 LeaveCriticalSection(&authcache_cs);
678 return TRUE;
681 LeaveCriticalSection(&authcache_cs);
683 return FALSE;
686 static void cache_authorization(LPWSTR host, LPWSTR scheme,
687 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
689 authorizationData *ad;
690 BOOL found = FALSE;
692 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
694 EnterCriticalSection(&authcache_cs);
695 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
696 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
697 found = TRUE;
698 break;
701 if(found) {
702 HeapFree(GetProcessHeap(), 0, ad->user);
703 HeapFree(GetProcessHeap(), 0, ad->password);
704 HeapFree(GetProcessHeap(), 0, ad->domain);
705 } else {
706 ad = HeapAlloc(GetProcessHeap(), 0, sizeof(authorizationData));
707 if(!ad) {
708 LeaveCriticalSection(&authcache_cs);
709 return;
712 ad->host = heap_strdupW(host);
713 ad->scheme = heap_strdupW(scheme);
714 list_add_head(&authorizationCache, &ad->entry);
717 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
718 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
719 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
720 ad->user_len = nt_auth_identity->UserLength;
721 ad->password_len = nt_auth_identity->PasswordLength;
722 ad->domain_len = nt_auth_identity->DomainLength;
724 if(!ad->host || !ad->scheme || !ad->user || !ad->password
725 || (nt_auth_identity->Domain && !ad->domain)) {
726 HeapFree(GetProcessHeap(), 0, ad->host);
727 HeapFree(GetProcessHeap(), 0, ad->scheme);
728 HeapFree(GetProcessHeap(), 0, ad->user);
729 HeapFree(GetProcessHeap(), 0, ad->password);
730 HeapFree(GetProcessHeap(), 0, ad->domain);
731 list_remove(&ad->entry);
732 HeapFree(GetProcessHeap(), 0, ad);
735 LeaveCriticalSection(&authcache_cs);
738 static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
739 struct HttpAuthInfo **ppAuthInfo,
740 LPWSTR domain_and_username, LPWSTR password,
741 LPWSTR host )
743 SECURITY_STATUS sec_status;
744 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
745 BOOL first = FALSE;
746 LPWSTR szRealm = NULL;
748 TRACE("%s\n", debugstr_w(pszAuthValue));
750 if (!pAuthInfo)
752 TimeStamp exp;
754 first = TRUE;
755 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
756 if (!pAuthInfo)
757 return FALSE;
759 SecInvalidateHandle(&pAuthInfo->cred);
760 SecInvalidateHandle(&pAuthInfo->ctx);
761 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
762 pAuthInfo->attr = 0;
763 pAuthInfo->auth_data = NULL;
764 pAuthInfo->auth_data_len = 0;
765 pAuthInfo->finished = FALSE;
767 if (is_basic_auth_value(pszAuthValue,NULL))
769 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
770 pAuthInfo->scheme = heap_strdupW(szBasic);
771 if (!pAuthInfo->scheme)
773 HeapFree(GetProcessHeap(), 0, pAuthInfo);
774 return FALSE;
777 else
779 PVOID pAuthData;
780 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
782 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
783 if (!pAuthInfo->scheme)
785 HeapFree(GetProcessHeap(), 0, pAuthInfo);
786 return FALSE;
789 if (domain_and_username)
791 WCHAR *user = strchrW(domain_and_username, '\\');
792 WCHAR *domain = domain_and_username;
794 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
796 pAuthData = &nt_auth_identity;
798 if (user) user++;
799 else
801 user = domain_and_username;
802 domain = NULL;
805 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
806 nt_auth_identity.User = user;
807 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
808 nt_auth_identity.Domain = domain;
809 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
810 nt_auth_identity.Password = password;
811 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
813 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
815 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
816 pAuthData = &nt_auth_identity;
817 else
818 /* use default credentials */
819 pAuthData = NULL;
821 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
822 SECPKG_CRED_OUTBOUND, NULL,
823 pAuthData, NULL,
824 NULL, &pAuthInfo->cred,
825 &exp);
827 if(pAuthData && !domain_and_username) {
828 HeapFree(GetProcessHeap(), 0, nt_auth_identity.User);
829 HeapFree(GetProcessHeap(), 0, nt_auth_identity.Domain);
830 HeapFree(GetProcessHeap(), 0, nt_auth_identity.Password);
833 if (sec_status == SEC_E_OK)
835 PSecPkgInfoW sec_pkg_info;
836 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
837 if (sec_status == SEC_E_OK)
839 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
840 FreeContextBuffer(sec_pkg_info);
843 if (sec_status != SEC_E_OK)
845 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
846 debugstr_w(pAuthInfo->scheme), sec_status);
847 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
848 HeapFree(GetProcessHeap(), 0, pAuthInfo);
849 return FALSE;
852 *ppAuthInfo = pAuthInfo;
854 else if (pAuthInfo->finished)
855 return FALSE;
857 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
858 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
860 ERR("authentication scheme changed from %s to %s\n",
861 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
862 return FALSE;
865 if (is_basic_auth_value(pszAuthValue,&szRealm))
867 int userlen;
868 int passlen;
869 char *auth_data = NULL;
870 UINT auth_data_len = 0;
872 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
874 if (!domain_and_username)
876 if (host && szRealm)
877 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
878 if (auth_data_len == 0)
880 HeapFree(GetProcessHeap(),0,szRealm);
881 return FALSE;
884 else
886 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
887 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
889 /* length includes a nul terminator, which will be re-used for the ':' */
890 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
891 if (!auth_data)
893 HeapFree(GetProcessHeap(),0,szRealm);
894 return FALSE;
897 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
898 auth_data[userlen] = ':';
899 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
900 auth_data_len = userlen + 1 + passlen;
901 if (host && szRealm)
902 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
905 pAuthInfo->auth_data = auth_data;
906 pAuthInfo->auth_data_len = auth_data_len;
907 pAuthInfo->finished = TRUE;
908 HeapFree(GetProcessHeap(),0,szRealm);
910 return TRUE;
912 else
914 LPCWSTR pszAuthData;
915 SecBufferDesc out_desc, in_desc;
916 SecBuffer out, in;
917 unsigned char *buffer;
918 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
919 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
921 in.BufferType = SECBUFFER_TOKEN;
922 in.cbBuffer = 0;
923 in.pvBuffer = NULL;
925 in_desc.ulVersion = 0;
926 in_desc.cBuffers = 1;
927 in_desc.pBuffers = &in;
929 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
930 if (*pszAuthData == ' ')
932 pszAuthData++;
933 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
934 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
935 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
938 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
940 out.BufferType = SECBUFFER_TOKEN;
941 out.cbBuffer = pAuthInfo->max_token;
942 out.pvBuffer = buffer;
944 out_desc.ulVersion = 0;
945 out_desc.cBuffers = 1;
946 out_desc.pBuffers = &out;
948 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
949 first ? NULL : &pAuthInfo->ctx,
950 first ? lpwhr->lpHttpSession->lpszServerName : NULL,
951 context_req, 0, SECURITY_NETWORK_DREP,
952 in.pvBuffer ? &in_desc : NULL,
953 0, &pAuthInfo->ctx, &out_desc,
954 &pAuthInfo->attr, &pAuthInfo->exp);
955 if (sec_status == SEC_E_OK)
957 pAuthInfo->finished = TRUE;
958 pAuthInfo->auth_data = out.pvBuffer;
959 pAuthInfo->auth_data_len = out.cbBuffer;
960 TRACE("sending last auth packet\n");
962 else if (sec_status == SEC_I_CONTINUE_NEEDED)
964 pAuthInfo->auth_data = out.pvBuffer;
965 pAuthInfo->auth_data_len = out.cbBuffer;
966 TRACE("sending next auth packet\n");
968 else
970 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
971 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
972 destroy_authinfo(pAuthInfo);
973 *ppAuthInfo = NULL;
974 return FALSE;
978 return TRUE;
981 /***********************************************************************
982 * HTTP_HttpAddRequestHeadersW (internal)
984 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
985 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
987 LPWSTR lpszStart;
988 LPWSTR lpszEnd;
989 LPWSTR buffer;
990 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
992 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
994 if( dwHeaderLength == ~0U )
995 len = strlenW(lpszHeader);
996 else
997 len = dwHeaderLength;
998 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
999 lstrcpynW( buffer, lpszHeader, len + 1);
1001 lpszStart = buffer;
1005 LPWSTR * pFieldAndValue;
1007 lpszEnd = lpszStart;
1009 while (*lpszEnd != '\0')
1011 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1012 break;
1013 lpszEnd++;
1016 if (*lpszStart == '\0')
1017 break;
1019 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1021 *lpszEnd = '\0';
1022 lpszEnd++; /* Jump over newline */
1024 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1025 if (*lpszStart == '\0')
1027 /* Skip 0-length headers */
1028 lpszStart = lpszEnd;
1029 res = ERROR_SUCCESS;
1030 continue;
1032 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1033 if (pFieldAndValue)
1035 res = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
1036 if (res == ERROR_SUCCESS)
1037 res = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
1038 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1039 HTTP_FreeTokens(pFieldAndValue);
1042 lpszStart = lpszEnd;
1043 } while (res == ERROR_SUCCESS);
1045 HeapFree(GetProcessHeap(), 0, buffer);
1047 return res;
1050 /***********************************************************************
1051 * HttpAddRequestHeadersW (WININET.@)
1053 * Adds one or more HTTP header to the request handler
1055 * NOTE
1056 * On Windows if dwHeaderLength includes the trailing '\0', then
1057 * HttpAddRequestHeadersW() adds it too. However this results in an
1058 * invalid Http header which is rejected by some servers so we probably
1059 * don't need to match Windows on that point.
1061 * RETURNS
1062 * TRUE on success
1063 * FALSE on failure
1066 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1067 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1069 http_request_t *lpwhr;
1070 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1072 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1074 if (!lpszHeader)
1075 return TRUE;
1077 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
1078 if (lpwhr && lpwhr->hdr.htype == WH_HHTTPREQ)
1079 res = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
1080 if( lpwhr )
1081 WININET_Release( &lpwhr->hdr );
1083 if(res != ERROR_SUCCESS)
1084 SetLastError(res);
1085 return res == ERROR_SUCCESS;
1088 /***********************************************************************
1089 * HttpAddRequestHeadersA (WININET.@)
1091 * Adds one or more HTTP header to the request handler
1093 * RETURNS
1094 * TRUE on success
1095 * FALSE on failure
1098 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1099 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1101 DWORD len;
1102 LPWSTR hdr;
1103 BOOL r;
1105 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1107 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1108 hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
1109 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1110 if( dwHeaderLength != ~0U )
1111 dwHeaderLength = len;
1113 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1115 HeapFree( GetProcessHeap(), 0, hdr );
1117 return r;
1120 /***********************************************************************
1121 * HttpOpenRequestA (WININET.@)
1123 * Open a HTTP request handle
1125 * RETURNS
1126 * HINTERNET a HTTP request handle on success
1127 * NULL on failure
1130 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1131 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1132 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1133 DWORD dwFlags, DWORD_PTR dwContext)
1135 LPWSTR szVerb = NULL, szObjectName = NULL;
1136 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1137 INT acceptTypesCount;
1138 HINTERNET rc = FALSE;
1139 LPCSTR *types;
1141 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1142 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1143 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1144 dwFlags, dwContext);
1146 if (lpszVerb)
1148 szVerb = heap_strdupAtoW(lpszVerb);
1149 if ( !szVerb )
1150 goto end;
1153 if (lpszObjectName)
1155 szObjectName = heap_strdupAtoW(lpszObjectName);
1156 if ( !szObjectName )
1157 goto end;
1160 if (lpszVersion)
1162 szVersion = heap_strdupAtoW(lpszVersion);
1163 if ( !szVersion )
1164 goto end;
1167 if (lpszReferrer)
1169 szReferrer = heap_strdupAtoW(lpszReferrer);
1170 if ( !szReferrer )
1171 goto end;
1174 if (lpszAcceptTypes)
1176 acceptTypesCount = 0;
1177 types = lpszAcceptTypes;
1178 while (*types)
1180 __TRY
1182 /* find out how many there are */
1183 if (*types && **types)
1185 TRACE("accept type: %s\n", debugstr_a(*types));
1186 acceptTypesCount++;
1189 __EXCEPT_PAGE_FAULT
1191 WARN("invalid accept type pointer\n");
1193 __ENDTRY;
1194 types++;
1196 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1197 if (!szAcceptTypes) goto end;
1199 acceptTypesCount = 0;
1200 types = lpszAcceptTypes;
1201 while (*types)
1203 __TRY
1205 if (*types && **types)
1206 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1208 __EXCEPT_PAGE_FAULT
1210 /* ignore invalid pointer */
1212 __ENDTRY;
1213 types++;
1215 szAcceptTypes[acceptTypesCount] = NULL;
1218 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1219 szVersion, szReferrer,
1220 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1222 end:
1223 if (szAcceptTypes)
1225 acceptTypesCount = 0;
1226 while (szAcceptTypes[acceptTypesCount])
1228 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1229 acceptTypesCount++;
1231 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1233 HeapFree(GetProcessHeap(), 0, szReferrer);
1234 HeapFree(GetProcessHeap(), 0, szVersion);
1235 HeapFree(GetProcessHeap(), 0, szObjectName);
1236 HeapFree(GetProcessHeap(), 0, szVerb);
1238 return rc;
1241 /***********************************************************************
1242 * HTTP_EncodeBase64
1244 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1246 UINT n = 0, x;
1247 static const CHAR HTTP_Base64Enc[] =
1248 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1250 while( len > 0 )
1252 /* first 6 bits, all from bin[0] */
1253 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1254 x = (bin[0] & 3) << 4;
1256 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1257 if( len == 1 )
1259 base64[n++] = HTTP_Base64Enc[x];
1260 base64[n++] = '=';
1261 base64[n++] = '=';
1262 break;
1264 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1265 x = ( bin[1] & 0x0f ) << 2;
1267 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1268 if( len == 2 )
1270 base64[n++] = HTTP_Base64Enc[x];
1271 base64[n++] = '=';
1272 break;
1274 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1276 /* last 6 bits, all from bin [2] */
1277 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1278 bin += 3;
1279 len -= 3;
1281 base64[n] = 0;
1282 return n;
1285 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1286 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1287 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1288 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1289 static const signed char HTTP_Base64Dec[256] =
1291 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1292 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1293 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1294 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1295 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1296 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1297 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1298 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1299 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1300 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1301 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1302 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1303 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1304 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1305 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1306 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1307 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1308 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1309 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1310 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1311 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1312 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1313 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1314 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1315 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1316 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1318 #undef CH
1320 /***********************************************************************
1321 * HTTP_DecodeBase64
1323 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1325 unsigned int n = 0;
1327 while(*base64)
1329 signed char in[4];
1331 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1332 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1333 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1334 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1336 WARN("invalid base64: %s\n", debugstr_w(base64));
1337 return 0;
1339 if (bin)
1340 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1341 n++;
1343 if ((base64[2] == '=') && (base64[3] == '='))
1344 break;
1345 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1346 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1348 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1349 return 0;
1351 if (bin)
1352 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1353 n++;
1355 if (base64[3] == '=')
1356 break;
1357 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1358 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1360 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1361 return 0;
1363 if (bin)
1364 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1365 n++;
1367 base64 += 4;
1370 return n;
1373 /***********************************************************************
1374 * HTTP_InsertAuthorization
1376 * Insert or delete the authorization field in the request header.
1378 static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1380 if (pAuthInfo)
1382 static const WCHAR wszSpace[] = {' ',0};
1383 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1384 unsigned int len;
1385 WCHAR *authorization = NULL;
1387 if (pAuthInfo->auth_data_len)
1389 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1390 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1391 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1392 if (!authorization)
1393 return FALSE;
1395 strcpyW(authorization, pAuthInfo->scheme);
1396 strcatW(authorization, wszSpace);
1397 HTTP_EncodeBase64(pAuthInfo->auth_data,
1398 pAuthInfo->auth_data_len,
1399 authorization+strlenW(authorization));
1401 /* clear the data as it isn't valid now that it has been sent to the
1402 * server, unless it's Basic authentication which doesn't do
1403 * connection tracking */
1404 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1406 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1407 pAuthInfo->auth_data = NULL;
1408 pAuthInfo->auth_data_len = 0;
1412 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1414 HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1416 HeapFree(GetProcessHeap(), 0, authorization);
1418 return TRUE;
1421 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1423 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1424 DWORD size;
1426 size = sizeof(new_location);
1427 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1429 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1430 strcpyW( url, new_location );
1432 else
1434 static const WCHAR slash[] = { '/',0 };
1435 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1436 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1437 http_session_t *session = req->lpHttpSession;
1439 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1440 size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
1442 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1444 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1445 sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
1446 else
1447 sprintfW( url, format, session->lpszHostName, session->nHostPort );
1448 if (req->lpszPath[0] != '/') strcatW( url, slash );
1449 strcatW( url, req->lpszPath );
1451 TRACE("url=%s\n", debugstr_w(url));
1452 return url;
1455 /***********************************************************************
1456 * HTTP_DealWithProxy
1458 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
1460 WCHAR buf[MAXHOSTNAME];
1461 WCHAR protoProxy[MAXHOSTNAME + 15];
1462 DWORD protoProxyLen = sizeof(protoProxy) / sizeof(protoProxy[0]);
1463 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1464 static WCHAR szNul[] = { 0 };
1465 URL_COMPONENTSW UrlComponents;
1466 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1467 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1468 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1470 memset( &UrlComponents, 0, sizeof UrlComponents );
1471 UrlComponents.dwStructSize = sizeof UrlComponents;
1472 UrlComponents.lpszHostName = buf;
1473 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1475 if (!INTERNET_FindProxyForProtocol(hIC->lpszProxy, protoHttp, protoProxy, &protoProxyLen))
1476 return FALSE;
1477 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1478 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1479 sprintfW(proxy, szFormat, protoProxy);
1480 else
1481 strcpyW(proxy, protoProxy);
1482 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1483 return FALSE;
1484 if( UrlComponents.dwHostNameLength == 0 )
1485 return FALSE;
1487 if( !lpwhr->lpszPath )
1488 lpwhr->lpszPath = szNul;
1490 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1491 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1493 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1494 lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1495 lpwhs->nServerPort = UrlComponents.nPort;
1497 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1498 return TRUE;
1501 #ifndef INET6_ADDRSTRLEN
1502 #define INET6_ADDRSTRLEN 46
1503 #endif
1505 static DWORD HTTP_ResolveName(http_request_t *lpwhr)
1507 char szaddr[INET6_ADDRSTRLEN];
1508 http_session_t *lpwhs = lpwhr->lpHttpSession;
1509 const void *addr;
1511 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1512 INTERNET_STATUS_RESOLVING_NAME,
1513 lpwhs->lpszServerName,
1514 (strlenW(lpwhs->lpszServerName)+1) * sizeof(WCHAR));
1516 lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1517 if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1518 (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1519 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1521 switch (lpwhs->socketAddress.ss_family)
1523 case AF_INET:
1524 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1525 break;
1526 case AF_INET6:
1527 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1528 break;
1529 default:
1530 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1531 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1533 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1534 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1535 INTERNET_STATUS_NAME_RESOLVED,
1536 szaddr, strlen(szaddr)+1);
1538 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1539 return ERROR_SUCCESS;
1542 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1544 LPHTTPHEADERW host_header;
1546 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1548 host_header = HTTP_GetHeader(req, hostW);
1549 if(!host_header)
1550 return FALSE;
1552 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1553 return TRUE;
1557 /***********************************************************************
1558 * HTTPREQ_Destroy (internal)
1560 * Deallocate request handle
1563 static void HTTPREQ_Destroy(object_header_t *hdr)
1565 http_request_t *lpwhr = (http_request_t*) hdr;
1566 DWORD i;
1568 TRACE("\n");
1570 if(lpwhr->hCacheFile) {
1571 WCHAR url[INTERNET_MAX_URL_LENGTH];
1572 FILETIME ft;
1574 CloseHandle(lpwhr->hCacheFile);
1576 memset(&ft, 0, sizeof(FILETIME));
1577 if(HTTP_GetRequestURL(lpwhr, url)) {
1578 CommitUrlCacheEntryW(url, lpwhr->lpszCacheFile, ft, ft,
1579 NORMAL_CACHE_ENTRY, NULL, 0, NULL, 0);
1583 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1585 DeleteCriticalSection( &lpwhr->read_section );
1586 WININET_Release(&lpwhr->lpHttpSession->hdr);
1588 destroy_authinfo(lpwhr->pAuthInfo);
1589 destroy_authinfo(lpwhr->pProxyAuthInfo);
1591 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1592 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1593 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1594 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1595 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1597 for (i = 0; i < lpwhr->nCustHeaders; i++)
1599 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1600 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1603 #ifdef HAVE_ZLIB
1604 if(lpwhr->gzip_stream) {
1605 if(!lpwhr->gzip_stream->end_of_data)
1606 inflateEnd(&lpwhr->gzip_stream->zstream);
1607 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1609 #endif
1611 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1612 HeapFree(GetProcessHeap(), 0, lpwhr);
1615 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1617 http_request_t *lpwhr = (http_request_t*) hdr;
1619 TRACE("%p\n",lpwhr);
1621 if (!NETCON_connected(&lpwhr->netConnection))
1622 return;
1624 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1625 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1627 NETCON_close(&lpwhr->netConnection);
1629 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1630 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1633 static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
1635 WCHAR szVersion[10];
1636 WCHAR szConnectionResponse[20];
1637 DWORD dwBufferSize = sizeof(szVersion);
1638 BOOL keepalive = FALSE;
1640 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1641 * the connection is keep-alive by default */
1642 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1643 && !strcmpiW(szVersion, g_szHttp1_1))
1645 keepalive = TRUE;
1648 dwBufferSize = sizeof(szConnectionResponse);
1649 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1650 || HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1652 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1655 return keepalive;
1658 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1660 http_request_t *req = (http_request_t*)hdr;
1662 switch(option) {
1663 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1665 http_session_t *lpwhs = req->lpHttpSession;
1666 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1668 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1670 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1671 return ERROR_INSUFFICIENT_BUFFER;
1672 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1673 /* FIXME: can't get a SOCKET from our connection since we don't use
1674 * winsock
1676 info->Socket = 0;
1677 /* FIXME: get source port from req->netConnection */
1678 info->SourcePort = 0;
1679 info->DestPort = lpwhs->nHostPort;
1680 info->Flags = 0;
1681 if (HTTP_KeepAlive(req))
1682 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1683 if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
1684 info->Flags |= IDSI_FLAG_PROXY;
1685 if (req->netConnection.useSSL)
1686 info->Flags |= IDSI_FLAG_SECURE;
1688 return ERROR_SUCCESS;
1691 case INTERNET_OPTION_SECURITY_FLAGS:
1693 http_session_t *lpwhs;
1694 lpwhs = req->lpHttpSession;
1696 if (*size < sizeof(ULONG))
1697 return ERROR_INSUFFICIENT_BUFFER;
1699 *size = sizeof(DWORD);
1700 if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1701 *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1702 else
1703 *(DWORD*)buffer = 0;
1704 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1705 return ERROR_SUCCESS;
1708 case INTERNET_OPTION_HANDLE_TYPE:
1709 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1711 if (*size < sizeof(ULONG))
1712 return ERROR_INSUFFICIENT_BUFFER;
1714 *size = sizeof(DWORD);
1715 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1716 return ERROR_SUCCESS;
1718 case INTERNET_OPTION_URL: {
1719 WCHAR url[INTERNET_MAX_URL_LENGTH];
1720 HTTPHEADERW *host;
1721 DWORD len;
1722 WCHAR *pch;
1724 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1726 TRACE("INTERNET_OPTION_URL\n");
1728 host = HTTP_GetHeader(req, hostW);
1729 strcpyW(url, httpW);
1730 strcatW(url, host->lpszValue);
1731 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1732 *pch = 0;
1733 strcatW(url, req->lpszPath);
1735 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1737 if(unicode) {
1738 len = (strlenW(url)+1) * sizeof(WCHAR);
1739 if(*size < len)
1740 return ERROR_INSUFFICIENT_BUFFER;
1742 *size = len;
1743 strcpyW(buffer, url);
1744 return ERROR_SUCCESS;
1745 }else {
1746 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1747 if(len > *size)
1748 return ERROR_INSUFFICIENT_BUFFER;
1750 *size = len;
1751 return ERROR_SUCCESS;
1755 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1756 INTERNET_CACHE_ENTRY_INFOW *info;
1757 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1758 WCHAR url[INTERNET_MAX_URL_LENGTH];
1759 DWORD nbytes, error;
1760 BOOL ret;
1762 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1764 if (*size < sizeof(*ts))
1766 *size = sizeof(*ts);
1767 return ERROR_INSUFFICIENT_BUFFER;
1769 nbytes = 0;
1770 HTTP_GetRequestURL(req, url);
1771 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1772 error = GetLastError();
1773 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1775 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1776 return ERROR_OUTOFMEMORY;
1778 GetUrlCacheEntryInfoW(url, info, &nbytes);
1780 ts->ftExpires = info->ExpireTime;
1781 ts->ftLastModified = info->LastModifiedTime;
1783 HeapFree(GetProcessHeap(), 0, info);
1784 *size = sizeof(*ts);
1785 return ERROR_SUCCESS;
1787 return error;
1790 case INTERNET_OPTION_DATAFILE_NAME: {
1791 DWORD req_size;
1793 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1795 if(!req->lpszCacheFile) {
1796 *size = 0;
1797 return ERROR_INTERNET_ITEM_NOT_FOUND;
1800 if(unicode) {
1801 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1802 if(*size < req_size)
1803 return ERROR_INSUFFICIENT_BUFFER;
1805 *size = req_size;
1806 memcpy(buffer, req->lpszCacheFile, *size);
1807 return ERROR_SUCCESS;
1808 }else {
1809 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1810 if (req_size > *size)
1811 return ERROR_INSUFFICIENT_BUFFER;
1813 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1814 -1, buffer, *size, NULL, NULL);
1815 return ERROR_SUCCESS;
1819 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1820 PCCERT_CONTEXT context;
1822 if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1823 *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1824 return ERROR_INSUFFICIENT_BUFFER;
1827 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1828 if(context) {
1829 INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1830 DWORD len;
1832 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1833 info->ftExpiry = context->pCertInfo->NotAfter;
1834 info->ftStart = context->pCertInfo->NotBefore;
1835 if(unicode) {
1836 len = CertNameToStrW(context->dwCertEncodingType,
1837 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1838 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1839 if(info->lpszSubjectInfo)
1840 CertNameToStrW(context->dwCertEncodingType,
1841 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1842 info->lpszSubjectInfo, len);
1843 len = CertNameToStrW(context->dwCertEncodingType,
1844 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1845 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1846 if (info->lpszIssuerInfo)
1847 CertNameToStrW(context->dwCertEncodingType,
1848 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1849 info->lpszIssuerInfo, len);
1850 }else {
1851 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1853 len = CertNameToStrA(context->dwCertEncodingType,
1854 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1855 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1856 if(infoA->lpszSubjectInfo)
1857 CertNameToStrA(context->dwCertEncodingType,
1858 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1859 infoA->lpszSubjectInfo, len);
1860 len = CertNameToStrA(context->dwCertEncodingType,
1861 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1862 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1863 if(infoA->lpszIssuerInfo)
1864 CertNameToStrA(context->dwCertEncodingType,
1865 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1866 infoA->lpszIssuerInfo, len);
1870 * Contrary to MSDN, these do not appear to be set.
1871 * lpszProtocolName
1872 * lpszSignatureAlgName
1873 * lpszEncryptionAlgName
1874 * dwKeySize
1876 CertFreeCertificateContext(context);
1877 return ERROR_SUCCESS;
1882 return INET_QueryOption(hdr, option, buffer, size, unicode);
1885 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1887 http_request_t *req = (http_request_t*)hdr;
1889 switch(option) {
1890 case INTERNET_OPTION_SEND_TIMEOUT:
1891 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1892 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1894 if (size != sizeof(DWORD))
1895 return ERROR_INVALID_PARAMETER;
1897 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1898 *(DWORD*)buffer);
1900 case INTERNET_OPTION_USERNAME:
1901 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1902 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1903 return ERROR_SUCCESS;
1905 case INTERNET_OPTION_PASSWORD:
1906 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1907 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1908 return ERROR_SUCCESS;
1909 case INTERNET_OPTION_HTTP_DECODING:
1910 if(size != sizeof(BOOL))
1911 return ERROR_INVALID_PARAMETER;
1912 req->decoding = *(BOOL*)buffer;
1913 return ERROR_SUCCESS;
1916 return ERROR_INTERNET_INVALID_OPTION;
1919 /* read some more data into the read buffer (the read section must be held) */
1920 static DWORD read_more_data( http_request_t *req, int maxlen )
1922 DWORD res;
1923 int len;
1925 if (req->read_pos)
1927 /* move existing data to the start of the buffer */
1928 if(req->read_size)
1929 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1930 req->read_pos = 0;
1933 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1935 res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1936 maxlen - req->read_size, 0, &len );
1937 if(res == ERROR_SUCCESS)
1938 req->read_size += len;
1940 return res;
1943 /* remove some amount of data from the read buffer (the read section must be held) */
1944 static void remove_data( http_request_t *req, int count )
1946 if (!(req->read_size -= count)) req->read_pos = 0;
1947 else req->read_pos += count;
1950 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1952 int count, bytes_read, pos = 0;
1953 DWORD res;
1955 EnterCriticalSection( &req->read_section );
1956 for (;;)
1958 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1960 if (eol)
1962 count = eol - (req->read_buf + req->read_pos);
1963 bytes_read = count + 1;
1965 else count = bytes_read = req->read_size;
1967 count = min( count, *len - pos );
1968 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1969 pos += count;
1970 remove_data( req, bytes_read );
1971 if (eol) break;
1973 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
1975 *len = 0;
1976 TRACE( "returning empty string\n" );
1977 LeaveCriticalSection( &req->read_section );
1978 INTERNET_SetLastError(res);
1979 return FALSE;
1982 LeaveCriticalSection( &req->read_section );
1984 if (pos < *len)
1986 if (pos && buffer[pos - 1] == '\r') pos--;
1987 *len = pos + 1;
1989 buffer[*len - 1] = 0;
1990 TRACE( "returning %s\n", debugstr_a(buffer));
1991 return TRUE;
1994 /* discard data contents until we reach end of line (the read section must be held) */
1995 static DWORD discard_eol( http_request_t *req )
1997 DWORD res;
2001 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2002 if (eol)
2004 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
2005 break;
2007 req->read_pos = req->read_size = 0; /* discard everything */
2008 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
2009 } while (req->read_size);
2010 return ERROR_SUCCESS;
2013 /* read the size of the next chunk (the read section must be held) */
2014 static DWORD start_next_chunk( http_request_t *req )
2016 DWORD chunk_size = 0, res;
2018 if (!req->dwContentLength) return ERROR_SUCCESS;
2019 if (req->dwContentLength == req->dwContentRead)
2021 /* read terminator for the previous chunk */
2022 if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
2023 req->dwContentLength = ~0u;
2024 req->dwContentRead = 0;
2026 for (;;)
2028 while (req->read_size)
2030 char ch = req->read_buf[req->read_pos];
2031 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2032 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2033 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2034 else if (ch == ';' || ch == '\r' || ch == '\n')
2036 TRACE( "reading %u byte chunk\n", chunk_size );
2037 req->dwContentLength = chunk_size;
2038 req->dwContentRead = 0;
2039 return discard_eol( req );
2041 remove_data( req, 1 );
2043 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
2044 if (!req->read_size)
2046 req->dwContentLength = req->dwContentRead = 0;
2047 return ERROR_SUCCESS;
2052 /* check if we have reached the end of the data to read (the read section must be held) */
2053 static BOOL end_of_read_data( http_request_t *req )
2055 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2056 if (req->read_chunked) return (req->dwContentLength == 0);
2057 if (req->dwContentLength == ~0u) return FALSE;
2058 return (req->dwContentLength == req->dwContentRead);
2061 /* fetch some more data into the read buffer (the read section must be held) */
2062 static DWORD refill_buffer( http_request_t *req )
2064 int len = sizeof(req->read_buf);
2065 DWORD res;
2067 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2069 if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
2072 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
2073 if (len <= req->read_size) return ERROR_SUCCESS;
2075 if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
2076 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
2077 return ERROR_SUCCESS;
2080 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
2082 DWORD ret = ERROR_SUCCESS;
2083 int read = 0;
2085 #ifdef HAVE_ZLIB
2086 z_stream *zstream = &req->gzip_stream->zstream;
2087 DWORD buf_avail;
2088 int zres;
2090 while(read < size && !req->gzip_stream->end_of_data) {
2091 if(!req->read_size) {
2092 if(!sync || refill_buffer(req) != ERROR_SUCCESS)
2093 break;
2096 if(req->dwContentRead == req->dwContentLength)
2097 break;
2099 buf_avail = req->dwContentLength == ~0 ? req->read_size : min(req->read_size, req->dwContentLength-req->dwContentRead);
2101 zstream->next_in = req->read_buf+req->read_pos;
2102 zstream->avail_in = buf_avail;
2103 zstream->next_out = buf+read;
2104 zstream->avail_out = size-read;
2105 zres = inflate(zstream, Z_FULL_FLUSH);
2106 read = size - zstream->avail_out;
2107 req->dwContentRead += buf_avail-zstream->avail_in;
2108 remove_data(req, buf_avail-zstream->avail_in);
2109 if(zres == Z_STREAM_END) {
2110 TRACE("end of data\n");
2111 req->gzip_stream->end_of_data = TRUE;
2112 inflateEnd(&req->gzip_stream->zstream);
2113 }else if(zres != Z_OK) {
2114 WARN("inflate failed %d\n", zres);
2115 if(!read)
2116 ret = ERROR_INTERNET_DECODING_FAILED;
2117 break;
2120 #endif
2122 *read_ret = read;
2123 return ret;
2126 static void refill_gzip_buffer(http_request_t *req)
2128 DWORD res;
2129 int len;
2131 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2132 return;
2134 if(req->gzip_stream->buf_pos) {
2135 if(req->gzip_stream->buf_size)
2136 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2137 req->gzip_stream->buf_pos = 0;
2140 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2141 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2142 if(res == ERROR_SUCCESS)
2143 req->gzip_stream->buf_size += len;
2146 /* return the size of data available to be read immediately (the read section must be held) */
2147 static DWORD get_avail_data( http_request_t *req )
2149 if (req->gzip_stream) {
2150 refill_gzip_buffer(req);
2151 return req->gzip_stream->buf_size;
2153 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2154 return 0;
2155 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2158 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2160 INTERNET_ASYNC_RESULT iar;
2161 DWORD res;
2163 TRACE("%p\n", req);
2165 EnterCriticalSection( &req->read_section );
2166 if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
2167 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2168 iar.dwError = first_notif ? 0 : get_avail_data(req);
2169 }else {
2170 iar.dwResult = 0;
2171 iar.dwError = res;
2173 LeaveCriticalSection( &req->read_section );
2175 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2176 sizeof(INTERNET_ASYNC_RESULT));
2179 /* read data from the http connection (the read section must be held) */
2180 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2182 BOOL finished_reading = FALSE;
2183 int len, bytes_read = 0;
2184 DWORD ret = ERROR_SUCCESS;
2186 EnterCriticalSection( &req->read_section );
2188 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2190 if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
2193 if(req->gzip_stream) {
2194 if(req->gzip_stream->buf_size) {
2195 bytes_read = min(req->gzip_stream->buf_size, size);
2196 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2197 req->gzip_stream->buf_pos += bytes_read;
2198 req->gzip_stream->buf_size -= bytes_read;
2199 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2200 refill_buffer(req);
2203 if(size > bytes_read) {
2204 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2205 if(ret == ERROR_SUCCESS)
2206 bytes_read += len;
2209 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2210 }else {
2211 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2213 if (req->read_size) {
2214 bytes_read = min( req->read_size, size );
2215 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2216 remove_data( req, bytes_read );
2219 if (size > bytes_read && (!bytes_read || sync)) {
2220 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2221 sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2222 bytes_read += len;
2223 /* always return success, even if the network layer returns an error */
2226 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2227 req->dwContentRead += bytes_read;
2229 done:
2230 *read = bytes_read;
2232 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2233 LeaveCriticalSection( &req->read_section );
2235 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2236 BOOL res;
2237 DWORD dwBytesWritten;
2239 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2240 if(!res)
2241 WARN("WriteFile failed: %u\n", GetLastError());
2244 if(finished_reading)
2245 HTTP_FinishedReading(req);
2247 return ret;
2251 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2253 http_request_t *req = (http_request_t*)hdr;
2254 DWORD res;
2256 EnterCriticalSection( &req->read_section );
2257 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2258 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2260 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2261 if(res == ERROR_SUCCESS)
2262 res = hdr->dwError;
2263 LeaveCriticalSection( &req->read_section );
2265 return res;
2268 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2270 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2271 http_request_t *req = (http_request_t*)workRequest->hdr;
2272 INTERNET_ASYNC_RESULT iar;
2273 DWORD res;
2275 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2277 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2278 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2280 iar.dwResult = res == ERROR_SUCCESS;
2281 iar.dwError = res;
2283 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2284 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2285 sizeof(INTERNET_ASYNC_RESULT));
2288 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2289 DWORD flags, DWORD_PTR context)
2291 http_request_t *req = (http_request_t*)hdr;
2292 DWORD res, size, read, error = ERROR_SUCCESS;
2294 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2295 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2297 if (buffers->dwStructSize != sizeof(*buffers))
2298 return ERROR_INVALID_PARAMETER;
2300 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2302 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2304 WORKREQUEST workRequest;
2306 if (TryEnterCriticalSection( &req->read_section ))
2308 if (get_avail_data(req))
2310 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2311 &buffers->dwBufferLength, FALSE);
2312 size = buffers->dwBufferLength;
2313 LeaveCriticalSection( &req->read_section );
2314 goto done;
2316 LeaveCriticalSection( &req->read_section );
2319 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2320 workRequest.hdr = WININET_AddRef(&req->hdr);
2321 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2323 INTERNET_AsyncCall(&workRequest);
2325 return ERROR_IO_PENDING;
2328 read = 0;
2329 size = buffers->dwBufferLength;
2331 EnterCriticalSection( &req->read_section );
2332 if(hdr->dwError == ERROR_SUCCESS)
2333 hdr->dwError = INTERNET_HANDLE_IN_USE;
2334 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2335 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2337 while(1) {
2338 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2339 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2340 if(res == ERROR_SUCCESS)
2341 read += buffers->dwBufferLength;
2342 else
2343 break;
2345 if(!req->read_chunked || read==size || req->dwContentLength!=req->dwContentRead
2346 || !req->dwContentLength || (req->gzip_stream && req->gzip_stream->end_of_data))
2347 break;
2348 LeaveCriticalSection( &req->read_section );
2350 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2351 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2352 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2353 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2355 EnterCriticalSection( &req->read_section );
2358 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2359 hdr->dwError = ERROR_SUCCESS;
2360 else
2361 error = hdr->dwError;
2363 LeaveCriticalSection( &req->read_section );
2364 size = buffers->dwBufferLength;
2365 buffers->dwBufferLength = read;
2367 done:
2368 if (res == ERROR_SUCCESS) {
2369 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2370 &size, sizeof(size));
2373 return res==ERROR_SUCCESS ? error : res;
2376 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2378 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2379 http_request_t *req = (http_request_t*)workRequest->hdr;
2380 INTERNET_ASYNC_RESULT iar;
2381 DWORD res;
2383 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2385 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2386 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2388 iar.dwResult = res == ERROR_SUCCESS;
2389 iar.dwError = res;
2391 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2392 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2393 sizeof(INTERNET_ASYNC_RESULT));
2396 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2397 DWORD flags, DWORD_PTR context)
2400 http_request_t *req = (http_request_t*)hdr;
2401 DWORD res, size, read, error = ERROR_SUCCESS;
2403 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2404 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2406 if (buffers->dwStructSize != sizeof(*buffers))
2407 return ERROR_INVALID_PARAMETER;
2409 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2411 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2413 WORKREQUEST workRequest;
2415 if (TryEnterCriticalSection( &req->read_section ))
2417 if (get_avail_data(req))
2419 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2420 &buffers->dwBufferLength, FALSE);
2421 size = buffers->dwBufferLength;
2422 LeaveCriticalSection( &req->read_section );
2423 goto done;
2425 LeaveCriticalSection( &req->read_section );
2428 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2429 workRequest.hdr = WININET_AddRef(&req->hdr);
2430 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2432 INTERNET_AsyncCall(&workRequest);
2434 return ERROR_IO_PENDING;
2437 read = 0;
2438 size = buffers->dwBufferLength;
2440 EnterCriticalSection( &req->read_section );
2441 if(hdr->dwError == ERROR_SUCCESS)
2442 hdr->dwError = INTERNET_HANDLE_IN_USE;
2443 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2444 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2446 while(1) {
2447 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2448 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2449 if(res == ERROR_SUCCESS)
2450 read += buffers->dwBufferLength;
2451 else
2452 break;
2454 if(!req->read_chunked || read==size || req->dwContentLength!=req->dwContentRead
2455 || !req->dwContentLength || (req->gzip_stream && req->gzip_stream->end_of_data))
2456 break;
2457 LeaveCriticalSection( &req->read_section );
2459 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2460 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2461 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2462 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2464 EnterCriticalSection( &req->read_section );
2467 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2468 hdr->dwError = ERROR_SUCCESS;
2469 else
2470 error = hdr->dwError;
2472 LeaveCriticalSection( &req->read_section );
2473 size = buffers->dwBufferLength;
2474 buffers->dwBufferLength = read;
2476 done:
2477 if (res == ERROR_SUCCESS) {
2478 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2479 &size, sizeof(size));
2482 return res==ERROR_SUCCESS ? error : res;
2485 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2487 DWORD res;
2488 http_request_t *lpwhr = (http_request_t*)hdr;
2490 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2492 *written = 0;
2493 res = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written);
2494 if (res == ERROR_SUCCESS)
2495 lpwhr->dwBytesWritten += *written;
2497 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2498 return res;
2501 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2503 http_request_t *req = (http_request_t*)workRequest->hdr;
2505 HTTP_ReceiveRequestData(req, FALSE);
2508 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2510 http_request_t *req = (http_request_t*)hdr;
2512 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2514 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2516 WORKREQUEST workRequest;
2518 /* never wait, if we can't enter the section we queue an async request right away */
2519 if (TryEnterCriticalSection( &req->read_section ))
2521 if ((*available = get_avail_data( req ))) goto done;
2522 if (end_of_read_data( req )) goto done;
2523 LeaveCriticalSection( &req->read_section );
2526 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2527 workRequest.hdr = WININET_AddRef( &req->hdr );
2529 INTERNET_AsyncCall(&workRequest);
2531 return ERROR_IO_PENDING;
2534 EnterCriticalSection( &req->read_section );
2536 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2538 refill_buffer( req );
2539 *available = get_avail_data( req );
2542 done:
2543 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2545 DWORD extra;
2546 if (NETCON_query_data_available(&req->netConnection, &extra))
2547 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2549 LeaveCriticalSection( &req->read_section );
2551 TRACE( "returning %u\n", *available );
2552 return ERROR_SUCCESS;
2555 static const object_vtbl_t HTTPREQVtbl = {
2556 HTTPREQ_Destroy,
2557 HTTPREQ_CloseConnection,
2558 HTTPREQ_QueryOption,
2559 HTTPREQ_SetOption,
2560 HTTPREQ_ReadFile,
2561 HTTPREQ_ReadFileExA,
2562 HTTPREQ_ReadFileExW,
2563 HTTPREQ_WriteFile,
2564 HTTPREQ_QueryDataAvailable,
2565 NULL
2568 /***********************************************************************
2569 * HTTP_HttpOpenRequestW (internal)
2571 * Open a HTTP request handle
2573 * RETURNS
2574 * HINTERNET a HTTP request handle on success
2575 * NULL on failure
2578 static DWORD HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2579 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2580 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2581 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2583 appinfo_t *hIC = NULL;
2584 http_request_t *lpwhr;
2585 LPWSTR lpszHostName = NULL;
2586 HINTERNET handle = NULL;
2587 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2588 DWORD len, res;
2590 TRACE("-->\n");
2592 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2593 hIC = lpwhs->lpAppInfo;
2595 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2596 if (NULL == lpwhr)
2598 res = ERROR_OUTOFMEMORY;
2599 goto lend;
2601 lpwhr->hdr.htype = WH_HHTTPREQ;
2602 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2603 lpwhr->hdr.dwFlags = dwFlags;
2604 lpwhr->hdr.dwContext = dwContext;
2605 lpwhr->hdr.refs = 1;
2606 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2607 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2608 lpwhr->dwContentLength = ~0u;
2609 InitializeCriticalSection( &lpwhr->read_section );
2611 WININET_AddRef( &lpwhs->hdr );
2612 lpwhr->lpHttpSession = lpwhs;
2613 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2615 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2616 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2617 if (NULL == lpszHostName)
2619 res = ERROR_OUTOFMEMORY;
2620 goto lend;
2623 handle = WININET_AllocHandle( &lpwhr->hdr );
2624 if (NULL == handle)
2626 res = ERROR_OUTOFMEMORY;
2627 goto lend;
2630 if ((res = NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2632 InternetCloseHandle( handle );
2633 handle = NULL;
2634 goto lend;
2637 if (lpszObjectName && *lpszObjectName) {
2638 HRESULT rc;
2640 len = 0;
2641 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2642 if (rc != E_POINTER)
2643 len = strlenW(lpszObjectName)+1;
2644 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2645 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2646 URL_ESCAPE_SPACES_ONLY);
2647 if (rc != S_OK)
2649 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2650 strcpyW(lpwhr->lpszPath,lpszObjectName);
2652 }else {
2653 static const WCHAR slashW[] = {'/',0};
2655 lpwhr->lpszPath = heap_strdupW(slashW);
2658 if (lpszReferrer && *lpszReferrer)
2659 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2661 if (lpszAcceptTypes)
2663 int i;
2664 for (i = 0; lpszAcceptTypes[i]; i++)
2666 if (!*lpszAcceptTypes[i]) continue;
2667 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2668 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2669 HTTP_ADDHDR_FLAG_REQ |
2670 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2674 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2675 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2677 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2678 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2679 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2681 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2682 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2683 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2685 else
2686 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2687 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2689 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2690 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2691 INTERNET_DEFAULT_HTTPS_PORT :
2692 INTERNET_DEFAULT_HTTP_PORT);
2694 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2695 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2696 INTERNET_DEFAULT_HTTPS_PORT :
2697 INTERNET_DEFAULT_HTTP_PORT);
2699 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2700 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2702 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2703 INTERNET_STATUS_HANDLE_CREATED, &handle,
2704 sizeof(handle));
2706 lend:
2707 HeapFree(GetProcessHeap(), 0, lpszHostName);
2708 if( lpwhr )
2709 WININET_Release( &lpwhr->hdr );
2711 TRACE("<-- %p (%p)\n", handle, lpwhr);
2712 *ret = handle;
2713 return res;
2716 /***********************************************************************
2717 * HttpOpenRequestW (WININET.@)
2719 * Open a HTTP request handle
2721 * RETURNS
2722 * HINTERNET a HTTP request handle on success
2723 * NULL on failure
2726 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2727 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2728 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2729 DWORD dwFlags, DWORD_PTR dwContext)
2731 http_session_t *lpwhs;
2732 HINTERNET handle = NULL;
2733 DWORD res;
2735 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2736 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2737 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2738 dwFlags, dwContext);
2739 if(lpszAcceptTypes!=NULL)
2741 int i;
2742 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2743 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2746 lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
2747 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
2749 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2750 goto lend;
2754 * My tests seem to show that the windows version does not
2755 * become asynchronous until after this point. And anyhow
2756 * if this call was asynchronous then how would you get the
2757 * necessary HINTERNET pointer returned by this function.
2760 res = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
2761 lpszVersion, lpszReferrer, lpszAcceptTypes,
2762 dwFlags, dwContext, &handle);
2763 lend:
2764 if( lpwhs )
2765 WININET_Release( &lpwhs->hdr );
2766 TRACE("returning %p\n", handle);
2767 if(res != ERROR_SUCCESS)
2768 SetLastError(res);
2769 return handle;
2772 /* read any content returned by the server so that the connection can be
2773 * reused */
2774 static void HTTP_DrainContent(http_request_t *req)
2776 DWORD bytes_read;
2778 if (!NETCON_connected(&req->netConnection)) return;
2780 if (req->dwContentLength == -1)
2782 NETCON_close(&req->netConnection);
2783 return;
2785 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2789 char buffer[2048];
2790 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2791 return;
2792 } while (bytes_read);
2795 static const LPCWSTR header_lookup[] = {
2796 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2797 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2798 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2799 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2800 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2801 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2802 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2803 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2804 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2805 szDate, /* HTTP_QUERY_DATE = 9 */
2806 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2807 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2808 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2809 szURI, /* HTTP_QUERY_URI = 13 */
2810 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2811 NULL, /* HTTP_QUERY_COST = 15 */
2812 NULL, /* HTTP_QUERY_LINK = 16 */
2813 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2814 NULL, /* HTTP_QUERY_VERSION = 18 */
2815 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2816 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2817 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2818 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2819 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2820 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2821 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2822 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2823 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2824 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2825 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2826 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2827 NULL, /* HTTP_QUERY_FROM = 31 */
2828 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2829 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2830 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2831 szReferer, /* HTTP_QUERY_REFERER = 35 */
2832 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2833 szServer, /* HTTP_QUERY_SERVER = 37 */
2834 NULL, /* HTTP_TITLE = 38 */
2835 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2836 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2837 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2838 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2839 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2840 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2841 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2842 NULL, /* HTTP_QUERY_REFRESH = 46 */
2843 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2844 szAge, /* HTTP_QUERY_AGE = 48 */
2845 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2846 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2847 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2848 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2849 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2850 szETag, /* HTTP_QUERY_ETAG = 54 */
2851 hostW, /* HTTP_QUERY_HOST = 55 */
2852 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2853 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2854 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2855 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2856 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2857 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2858 szRange, /* HTTP_QUERY_RANGE = 62 */
2859 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2860 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2861 szVary, /* HTTP_QUERY_VARY = 65 */
2862 szVia, /* HTTP_QUERY_VIA = 66 */
2863 szWarning, /* HTTP_QUERY_WARNING = 67 */
2864 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2865 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2866 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2869 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2871 /***********************************************************************
2872 * HTTP_HttpQueryInfoW (internal)
2874 static DWORD HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2875 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2877 LPHTTPHEADERW lphttpHdr = NULL;
2878 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2879 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2880 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2881 INT index = -1;
2883 /* Find requested header structure */
2884 switch (level)
2886 case HTTP_QUERY_CUSTOM:
2887 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
2888 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2889 break;
2890 case HTTP_QUERY_RAW_HEADERS_CRLF:
2892 LPWSTR headers;
2893 DWORD len = 0;
2894 DWORD res = ERROR_INVALID_PARAMETER;
2896 if (request_only)
2897 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2898 else
2899 headers = lpwhr->lpszRawHeaders;
2901 if (headers)
2902 len = strlenW(headers) * sizeof(WCHAR);
2904 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2906 len += sizeof(WCHAR);
2907 res = ERROR_INSUFFICIENT_BUFFER;
2909 else if (lpBuffer)
2911 if (headers)
2912 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2913 else
2915 len = strlenW(szCrLf) * sizeof(WCHAR);
2916 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2918 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2919 res = ERROR_SUCCESS;
2921 *lpdwBufferLength = len;
2923 if (request_only)
2924 HeapFree(GetProcessHeap(), 0, headers);
2925 return res;
2927 case HTTP_QUERY_RAW_HEADERS:
2929 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2930 DWORD i, size = 0;
2931 LPWSTR pszString = lpBuffer;
2933 for (i = 0; ppszRawHeaderLines[i]; i++)
2934 size += strlenW(ppszRawHeaderLines[i]) + 1;
2936 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2938 HTTP_FreeTokens(ppszRawHeaderLines);
2939 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2940 return ERROR_INSUFFICIENT_BUFFER;
2942 if (pszString)
2944 for (i = 0; ppszRawHeaderLines[i]; i++)
2946 DWORD len = strlenW(ppszRawHeaderLines[i]);
2947 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2948 pszString += len+1;
2950 *pszString = '\0';
2951 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2953 *lpdwBufferLength = size * sizeof(WCHAR);
2954 HTTP_FreeTokens(ppszRawHeaderLines);
2956 return ERROR_SUCCESS;
2958 case HTTP_QUERY_STATUS_TEXT:
2959 if (lpwhr->lpszStatusText)
2961 DWORD len = strlenW(lpwhr->lpszStatusText);
2962 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2964 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2965 return ERROR_INSUFFICIENT_BUFFER;
2967 if (lpBuffer)
2969 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2970 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2972 *lpdwBufferLength = len * sizeof(WCHAR);
2973 return ERROR_SUCCESS;
2975 break;
2976 case HTTP_QUERY_VERSION:
2977 if (lpwhr->lpszVersion)
2979 DWORD len = strlenW(lpwhr->lpszVersion);
2980 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2982 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2983 return ERROR_INSUFFICIENT_BUFFER;
2985 if (lpBuffer)
2987 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2988 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2990 *lpdwBufferLength = len * sizeof(WCHAR);
2991 return ERROR_SUCCESS;
2993 break;
2994 case HTTP_QUERY_CONTENT_ENCODING:
2995 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2996 requested_index,request_only);
2997 break;
2998 default:
2999 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3001 if (level < LAST_TABLE_HEADER && header_lookup[level])
3002 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
3003 requested_index,request_only);
3006 if (index >= 0)
3007 lphttpHdr = &lpwhr->pCustHeaders[index];
3009 /* Ensure header satisfies requested attributes */
3010 if (!lphttpHdr ||
3011 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3012 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3014 return ERROR_HTTP_HEADER_NOT_FOUND;
3017 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
3019 /* coalesce value to requested type */
3020 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3022 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3023 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3025 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3027 time_t tmpTime;
3028 struct tm tmpTM;
3029 SYSTEMTIME *STHook;
3031 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3033 tmpTM = *gmtime(&tmpTime);
3034 STHook = (SYSTEMTIME *)lpBuffer;
3035 STHook->wDay = tmpTM.tm_mday;
3036 STHook->wHour = tmpTM.tm_hour;
3037 STHook->wMilliseconds = 0;
3038 STHook->wMinute = tmpTM.tm_min;
3039 STHook->wDayOfWeek = tmpTM.tm_wday;
3040 STHook->wMonth = tmpTM.tm_mon + 1;
3041 STHook->wSecond = tmpTM.tm_sec;
3042 STHook->wYear = tmpTM.tm_year;
3044 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3045 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3046 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3048 else if (lphttpHdr->lpszValue)
3050 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3052 if (len > *lpdwBufferLength)
3054 *lpdwBufferLength = len;
3055 return ERROR_INSUFFICIENT_BUFFER;
3057 if (lpBuffer)
3059 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3060 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3062 *lpdwBufferLength = len - sizeof(WCHAR);
3064 return ERROR_SUCCESS;
3067 /***********************************************************************
3068 * HttpQueryInfoW (WININET.@)
3070 * Queries for information about an HTTP request
3072 * RETURNS
3073 * TRUE on success
3074 * FALSE on failure
3077 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3078 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3080 http_request_t *lpwhr;
3081 DWORD res;
3083 if (TRACE_ON(wininet)) {
3084 #define FE(x) { x, #x }
3085 static const wininet_flag_info query_flags[] = {
3086 FE(HTTP_QUERY_MIME_VERSION),
3087 FE(HTTP_QUERY_CONTENT_TYPE),
3088 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3089 FE(HTTP_QUERY_CONTENT_ID),
3090 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3091 FE(HTTP_QUERY_CONTENT_LENGTH),
3092 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3093 FE(HTTP_QUERY_ALLOW),
3094 FE(HTTP_QUERY_PUBLIC),
3095 FE(HTTP_QUERY_DATE),
3096 FE(HTTP_QUERY_EXPIRES),
3097 FE(HTTP_QUERY_LAST_MODIFIED),
3098 FE(HTTP_QUERY_MESSAGE_ID),
3099 FE(HTTP_QUERY_URI),
3100 FE(HTTP_QUERY_DERIVED_FROM),
3101 FE(HTTP_QUERY_COST),
3102 FE(HTTP_QUERY_LINK),
3103 FE(HTTP_QUERY_PRAGMA),
3104 FE(HTTP_QUERY_VERSION),
3105 FE(HTTP_QUERY_STATUS_CODE),
3106 FE(HTTP_QUERY_STATUS_TEXT),
3107 FE(HTTP_QUERY_RAW_HEADERS),
3108 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3109 FE(HTTP_QUERY_CONNECTION),
3110 FE(HTTP_QUERY_ACCEPT),
3111 FE(HTTP_QUERY_ACCEPT_CHARSET),
3112 FE(HTTP_QUERY_ACCEPT_ENCODING),
3113 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3114 FE(HTTP_QUERY_AUTHORIZATION),
3115 FE(HTTP_QUERY_CONTENT_ENCODING),
3116 FE(HTTP_QUERY_FORWARDED),
3117 FE(HTTP_QUERY_FROM),
3118 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3119 FE(HTTP_QUERY_LOCATION),
3120 FE(HTTP_QUERY_ORIG_URI),
3121 FE(HTTP_QUERY_REFERER),
3122 FE(HTTP_QUERY_RETRY_AFTER),
3123 FE(HTTP_QUERY_SERVER),
3124 FE(HTTP_QUERY_TITLE),
3125 FE(HTTP_QUERY_USER_AGENT),
3126 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3127 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3128 FE(HTTP_QUERY_ACCEPT_RANGES),
3129 FE(HTTP_QUERY_SET_COOKIE),
3130 FE(HTTP_QUERY_COOKIE),
3131 FE(HTTP_QUERY_REQUEST_METHOD),
3132 FE(HTTP_QUERY_REFRESH),
3133 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3134 FE(HTTP_QUERY_AGE),
3135 FE(HTTP_QUERY_CACHE_CONTROL),
3136 FE(HTTP_QUERY_CONTENT_BASE),
3137 FE(HTTP_QUERY_CONTENT_LOCATION),
3138 FE(HTTP_QUERY_CONTENT_MD5),
3139 FE(HTTP_QUERY_CONTENT_RANGE),
3140 FE(HTTP_QUERY_ETAG),
3141 FE(HTTP_QUERY_HOST),
3142 FE(HTTP_QUERY_IF_MATCH),
3143 FE(HTTP_QUERY_IF_NONE_MATCH),
3144 FE(HTTP_QUERY_IF_RANGE),
3145 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3146 FE(HTTP_QUERY_MAX_FORWARDS),
3147 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3148 FE(HTTP_QUERY_RANGE),
3149 FE(HTTP_QUERY_TRANSFER_ENCODING),
3150 FE(HTTP_QUERY_UPGRADE),
3151 FE(HTTP_QUERY_VARY),
3152 FE(HTTP_QUERY_VIA),
3153 FE(HTTP_QUERY_WARNING),
3154 FE(HTTP_QUERY_CUSTOM)
3156 static const wininet_flag_info modifier_flags[] = {
3157 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3158 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3159 FE(HTTP_QUERY_FLAG_NUMBER),
3160 FE(HTTP_QUERY_FLAG_COALESCE)
3162 #undef FE
3163 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3164 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3165 DWORD i;
3167 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3168 TRACE(" Attribute:");
3169 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3170 if (query_flags[i].val == info) {
3171 TRACE(" %s", query_flags[i].name);
3172 break;
3175 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3176 TRACE(" Unknown (%08x)", info);
3179 TRACE(" Modifier:");
3180 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3181 if (modifier_flags[i].val & info_mod) {
3182 TRACE(" %s", modifier_flags[i].name);
3183 info_mod &= ~ modifier_flags[i].val;
3187 if (info_mod) {
3188 TRACE(" Unknown (%08x)", info_mod);
3190 TRACE("\n");
3193 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3194 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3196 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3197 goto lend;
3200 if (lpBuffer == NULL)
3201 *lpdwBufferLength = 0;
3202 res = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
3203 lpBuffer, lpdwBufferLength, lpdwIndex);
3205 lend:
3206 if( lpwhr )
3207 WININET_Release( &lpwhr->hdr );
3209 TRACE("%u <--\n", res);
3210 if(res != ERROR_SUCCESS)
3211 SetLastError(res);
3212 return res == ERROR_SUCCESS;
3215 /***********************************************************************
3216 * HttpQueryInfoA (WININET.@)
3218 * Queries for information about an HTTP request
3220 * RETURNS
3221 * TRUE on success
3222 * FALSE on failure
3225 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3226 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3228 BOOL result;
3229 DWORD len;
3230 WCHAR* bufferW;
3232 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3233 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3235 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3236 lpdwBufferLength, lpdwIndex );
3239 if (lpBuffer)
3241 DWORD alloclen;
3242 len = (*lpdwBufferLength)*sizeof(WCHAR);
3243 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3245 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3246 if (alloclen < len)
3247 alloclen = len;
3249 else
3250 alloclen = len;
3251 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3252 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3253 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3254 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3255 } else
3257 bufferW = NULL;
3258 len = 0;
3261 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3262 &len, lpdwIndex );
3263 if( result )
3265 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3266 lpBuffer, *lpdwBufferLength, NULL, NULL );
3267 *lpdwBufferLength = len - 1;
3269 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3271 else
3272 /* since the strings being returned from HttpQueryInfoW should be
3273 * only ASCII characters, it is reasonable to assume that all of
3274 * the Unicode characters can be reduced to a single byte */
3275 *lpdwBufferLength = len / sizeof(WCHAR);
3277 HeapFree(GetProcessHeap(), 0, bufferW );
3279 return result;
3282 /***********************************************************************
3283 * HTTP_GetRedirectURL (internal)
3285 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3287 static WCHAR szHttp[] = {'h','t','t','p',0};
3288 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3289 http_session_t *lpwhs = lpwhr->lpHttpSession;
3290 URL_COMPONENTSW urlComponents;
3291 DWORD url_length = 0;
3292 LPWSTR orig_url;
3293 LPWSTR combined_url;
3295 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3296 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3297 urlComponents.dwSchemeLength = 0;
3298 urlComponents.lpszHostName = lpwhs->lpszHostName;
3299 urlComponents.dwHostNameLength = 0;
3300 urlComponents.nPort = lpwhs->nHostPort;
3301 urlComponents.lpszUserName = lpwhs->lpszUserName;
3302 urlComponents.dwUserNameLength = 0;
3303 urlComponents.lpszPassword = NULL;
3304 urlComponents.dwPasswordLength = 0;
3305 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3306 urlComponents.dwUrlPathLength = 0;
3307 urlComponents.lpszExtraInfo = NULL;
3308 urlComponents.dwExtraInfoLength = 0;
3310 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3311 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3312 return NULL;
3314 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3316 /* convert from bytes to characters */
3317 url_length = url_length / sizeof(WCHAR) - 1;
3318 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3320 HeapFree(GetProcessHeap(), 0, orig_url);
3321 return NULL;
3324 url_length = 0;
3325 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3326 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3328 HeapFree(GetProcessHeap(), 0, orig_url);
3329 return NULL;
3331 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3333 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3335 HeapFree(GetProcessHeap(), 0, orig_url);
3336 HeapFree(GetProcessHeap(), 0, combined_url);
3337 return NULL;
3339 HeapFree(GetProcessHeap(), 0, orig_url);
3340 return combined_url;
3344 /***********************************************************************
3345 * HTTP_HandleRedirect (internal)
3347 static DWORD HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3349 http_session_t *lpwhs = lpwhr->lpHttpSession;
3350 appinfo_t *hIC = lpwhs->lpAppInfo;
3351 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3352 WCHAR path[INTERNET_MAX_URL_LENGTH];
3353 int index;
3355 if(lpszUrl[0]=='/')
3357 /* if it's an absolute path, keep the same session info */
3358 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3360 else
3362 URL_COMPONENTSW urlComponents;
3363 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3364 static WCHAR szHttp[] = {'h','t','t','p',0};
3365 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3367 userName[0] = 0;
3368 hostName[0] = 0;
3369 protocol[0] = 0;
3371 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3372 urlComponents.lpszScheme = protocol;
3373 urlComponents.dwSchemeLength = 32;
3374 urlComponents.lpszHostName = hostName;
3375 urlComponents.dwHostNameLength = MAXHOSTNAME;
3376 urlComponents.lpszUserName = userName;
3377 urlComponents.dwUserNameLength = 1024;
3378 urlComponents.lpszPassword = NULL;
3379 urlComponents.dwPasswordLength = 0;
3380 urlComponents.lpszUrlPath = path;
3381 urlComponents.dwUrlPathLength = 2048;
3382 urlComponents.lpszExtraInfo = NULL;
3383 urlComponents.dwExtraInfoLength = 0;
3384 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3385 return INTERNET_GetLastError();
3387 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3388 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3390 TRACE("redirect from secure page to non-secure page\n");
3391 /* FIXME: warn about from secure redirect to non-secure page */
3392 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3394 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3395 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3397 TRACE("redirect from non-secure page to secure page\n");
3398 /* FIXME: notify about redirect to secure page */
3399 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3402 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3404 if (lstrlenW(protocol)>4) /*https*/
3405 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3406 else /*http*/
3407 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3410 #if 0
3412 * This upsets redirects to binary files on sourceforge.net
3413 * and gives an html page instead of the target file
3414 * Examination of the HTTP request sent by native wininet.dll
3415 * reveals that it doesn't send a referrer in that case.
3416 * Maybe there's a flag that enables this, or maybe a referrer
3417 * shouldn't be added in case of a redirect.
3420 /* consider the current host as the referrer */
3421 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3422 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3423 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3424 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3425 #endif
3427 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3428 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3429 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3431 int len;
3432 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3433 len = lstrlenW(hostName);
3434 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3435 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3436 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3438 else
3439 lpwhs->lpszHostName = heap_strdupW(hostName);
3441 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3443 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3444 lpwhs->lpszUserName = NULL;
3445 if (userName[0])
3446 lpwhs->lpszUserName = heap_strdupW(userName);
3448 if (!using_proxy)
3450 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3452 DWORD res;
3454 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3455 lpwhs->lpszServerName = heap_strdupW(hostName);
3456 lpwhs->nServerPort = urlComponents.nPort;
3458 NETCON_close(&lpwhr->netConnection);
3459 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS)
3460 return res;
3462 res = NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE);
3463 if (res != ERROR_SUCCESS)
3464 return res;
3466 lpwhr->read_pos = lpwhr->read_size = 0;
3467 lpwhr->read_chunked = FALSE;
3470 else
3471 TRACE("Redirect through proxy\n");
3474 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3475 lpwhr->lpszPath=NULL;
3476 if (*path)
3478 DWORD needed = 0;
3479 HRESULT rc;
3481 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3482 if (rc != E_POINTER)
3483 needed = strlenW(path)+1;
3484 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3485 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3486 URL_ESCAPE_SPACES_ONLY);
3487 if (rc != S_OK)
3489 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3490 strcpyW(lpwhr->lpszPath,path);
3494 /* Remove custom content-type/length headers on redirects. */
3495 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3496 if (0 <= index)
3497 HTTP_DeleteCustomHeader(lpwhr, index);
3498 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3499 if (0 <= index)
3500 HTTP_DeleteCustomHeader(lpwhr, index);
3502 return ERROR_SUCCESS;
3505 /***********************************************************************
3506 * HTTP_build_req (internal)
3508 * concatenate all the strings in the request together
3510 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3512 LPCWSTR *t;
3513 LPWSTR str;
3515 for( t = list; *t ; t++ )
3516 len += strlenW( *t );
3517 len++;
3519 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3520 *str = 0;
3522 for( t = list; *t ; t++ )
3523 strcatW( str, *t );
3525 return str;
3528 static DWORD HTTP_SecureProxyConnect(http_request_t *lpwhr)
3530 LPWSTR lpszPath;
3531 LPWSTR requestString;
3532 INT len;
3533 INT cnt;
3534 INT responseLen;
3535 char *ascii_req;
3536 DWORD res;
3537 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3538 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3539 http_session_t *lpwhs = lpwhr->lpHttpSession;
3541 TRACE("\n");
3543 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3544 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3545 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3546 HeapFree( GetProcessHeap(), 0, lpszPath );
3548 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3549 NULL, 0, NULL, NULL );
3550 len--; /* the nul terminator isn't needed */
3551 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3552 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3553 ascii_req, len, NULL, NULL );
3554 HeapFree( GetProcessHeap(), 0, requestString );
3556 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3558 res = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3559 HeapFree( GetProcessHeap(), 0, ascii_req );
3560 if (res != ERROR_SUCCESS)
3561 return res;
3563 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3564 if (!responseLen)
3565 return ERROR_HTTP_INVALID_HEADER;
3567 return ERROR_SUCCESS;
3570 static void HTTP_InsertCookies(http_request_t *lpwhr)
3572 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3573 LPWSTR lpszCookies, lpszUrl = NULL;
3574 DWORD nCookieSize, size;
3575 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3577 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3578 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3579 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3581 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3583 int cnt = 0;
3584 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3586 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3587 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3589 cnt += sprintfW(lpszCookies, szCookie);
3590 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3591 strcatW(lpszCookies, szCrLf);
3593 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3594 HeapFree(GetProcessHeap(), 0, lpszCookies);
3597 HeapFree(GetProcessHeap(), 0, lpszUrl);
3600 /***********************************************************************
3601 * HTTP_HttpSendRequestW (internal)
3603 * Sends the specified request to the HTTP server
3605 * RETURNS
3606 * TRUE on success
3607 * FALSE on failure
3610 static DWORD HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3611 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3612 DWORD dwContentLength, BOOL bEndRequest)
3614 INT cnt;
3615 BOOL redirected = FALSE;
3616 LPWSTR requestString = NULL;
3617 INT responseLen;
3618 BOOL loop_next;
3619 INTERNET_ASYNC_RESULT iar;
3620 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3621 static const WCHAR szContentLength[] =
3622 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3623 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3624 DWORD res;
3626 TRACE("--> %p\n", lpwhr);
3628 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3630 /* if the verb is NULL default to GET */
3631 if (!lpwhr->lpszVerb)
3632 lpwhr->lpszVerb = heap_strdupW(szGET);
3634 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3636 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3637 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3638 lpwhr->dwBytesToWrite = dwContentLength;
3640 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3642 WCHAR *agent_header;
3643 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3644 int len;
3646 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3647 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3648 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3650 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3651 HeapFree(GetProcessHeap(), 0, agent_header);
3653 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3655 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3656 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3658 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3660 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3661 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3662 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3667 DWORD len;
3668 BOOL reusing_connection;
3669 char *ascii_req;
3671 loop_next = FALSE;
3673 /* like native, just in case the caller forgot to call InternetReadFile
3674 * for all the data */
3675 HTTP_DrainContent(lpwhr);
3676 lpwhr->dwContentRead = 0;
3677 if(redirected) {
3678 lpwhr->dwContentLength = ~0u;
3679 lpwhr->dwBytesToWrite = 0;
3682 if (TRACE_ON(wininet))
3684 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3685 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3688 HTTP_FixURL(lpwhr);
3689 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3691 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3693 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3694 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3696 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3697 HTTP_InsertCookies(lpwhr);
3699 /* add the headers the caller supplied */
3700 if( lpszHeaders && dwHeaderLength )
3702 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3703 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3706 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3708 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3709 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3710 HeapFree(GetProcessHeap(), 0, url);
3712 else
3713 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3716 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3718 /* Send the request and store the results */
3719 if(NETCON_connected(&lpwhr->netConnection))
3720 reusing_connection = TRUE;
3721 else
3722 reusing_connection = FALSE;
3724 if ((res = HTTP_OpenConnection(lpwhr)) != ERROR_SUCCESS)
3725 goto lend;
3727 /* send the request as ASCII, tack on the optional data */
3728 if (!lpOptional || redirected)
3729 dwOptionalLength = 0;
3730 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3731 NULL, 0, NULL, NULL );
3732 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3733 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3734 ascii_req, len, NULL, NULL );
3735 if( lpOptional )
3736 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3737 len = (len + dwOptionalLength - 1);
3738 ascii_req[len] = 0;
3739 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3741 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3742 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3744 res = NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3745 HeapFree( GetProcessHeap(), 0, ascii_req );
3747 lpwhr->dwBytesWritten = dwOptionalLength;
3749 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3750 INTERNET_STATUS_REQUEST_SENT,
3751 &len, sizeof(DWORD));
3753 if (bEndRequest)
3755 DWORD dwBufferSize;
3756 DWORD dwStatusCode;
3758 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3759 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3761 if (res != ERROR_SUCCESS)
3762 goto lend;
3764 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3765 /* FIXME: We should know that connection is closed before sending
3766 * headers. Otherwise wrong callbacks are executed */
3767 if(!responseLen && reusing_connection) {
3768 TRACE("Connection closed by server, reconnecting\n");
3769 loop_next = TRUE;
3770 continue;
3773 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3774 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3775 sizeof(DWORD));
3777 HTTP_ProcessCookies(lpwhr);
3779 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3781 dwBufferSize = sizeof(dwStatusCode);
3782 if (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3783 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
3784 dwStatusCode = 0;
3786 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
3788 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3789 dwBufferSize=sizeof(szNewLocation);
3790 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
3791 dwStatusCode == HTTP_STATUS_MOVED ||
3792 dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
3793 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
3795 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3797 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3798 lpwhr->lpszVerb = heap_strdupW(szGET);
3800 HTTP_DrainContent(lpwhr);
3801 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3803 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3804 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3805 res = HTTP_HandleRedirect(lpwhr, new_url);
3806 if (res == ERROR_SUCCESS)
3808 HeapFree(GetProcessHeap(), 0, requestString);
3809 loop_next = TRUE;
3811 HeapFree( GetProcessHeap(), 0, new_url );
3813 redirected = TRUE;
3816 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
3818 WCHAR szAuthValue[2048];
3819 dwBufferSize=2048;
3820 if (dwStatusCode == HTTP_STATUS_DENIED)
3822 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3823 DWORD dwIndex = 0;
3824 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3826 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3827 &lpwhr->pAuthInfo,
3828 lpwhr->lpHttpSession->lpszUserName,
3829 lpwhr->lpHttpSession->lpszPassword,
3830 Host->lpszValue))
3832 HeapFree(GetProcessHeap(), 0, requestString);
3833 loop_next = TRUE;
3834 break;
3838 if(!loop_next) {
3839 TRACE("Cleaning wrong authorization data\n");
3840 destroy_authinfo(lpwhr->pAuthInfo);
3841 lpwhr->pAuthInfo = NULL;
3844 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3846 DWORD dwIndex = 0;
3847 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3849 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3850 &lpwhr->pProxyAuthInfo,
3851 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3852 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
3853 NULL))
3855 loop_next = TRUE;
3856 break;
3860 if(!loop_next) {
3861 TRACE("Cleaning wrong proxy authorization data\n");
3862 destroy_authinfo(lpwhr->pProxyAuthInfo);
3863 lpwhr->pProxyAuthInfo = NULL;
3868 else
3869 res = ERROR_SUCCESS;
3871 while (loop_next);
3873 if(res == ERROR_SUCCESS) {
3874 WCHAR url[INTERNET_MAX_URL_LENGTH];
3875 WCHAR cacheFileName[MAX_PATH+1];
3876 BOOL b;
3878 b = HTTP_GetRequestURL(lpwhr, url);
3879 if(!b) {
3880 WARN("Could not get URL\n");
3881 goto lend;
3884 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3885 if(b) {
3886 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
3887 CloseHandle(lpwhr->hCacheFile);
3889 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3890 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3891 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3892 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3893 WARN("Could not create file: %u\n", GetLastError());
3894 lpwhr->hCacheFile = NULL;
3896 }else {
3897 WARN("Could not create cache entry: %08x\n", GetLastError());
3901 lend:
3903 HeapFree(GetProcessHeap(), 0, requestString);
3905 /* TODO: send notification for P3P header */
3907 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3909 if (res == ERROR_SUCCESS && lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite)
3910 HTTP_ReceiveRequestData(lpwhr, TRUE);
3911 else
3913 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)lpwhr->hdr.hInternet : 0);
3914 iar.dwError = res;
3916 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3917 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3918 sizeof(INTERNET_ASYNC_RESULT));
3922 TRACE("<--\n");
3923 return res;
3926 /***********************************************************************
3928 * Helper functions for the HttpSendRequest(Ex) functions
3931 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
3933 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
3934 http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
3936 TRACE("%p\n", lpwhr);
3938 HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
3939 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
3940 req->dwContentLength, req->bEndRequest);
3942 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
3946 static DWORD HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
3948 INT responseLen;
3949 DWORD dwBufferSize;
3950 INTERNET_ASYNC_RESULT iar;
3951 DWORD res = ERROR_SUCCESS;
3953 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3954 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3956 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3957 if (!responseLen)
3958 res = ERROR_HTTP_HEADER_NOT_FOUND;
3960 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3961 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
3963 /* process cookies here. Is this right? */
3964 HTTP_ProcessCookies(lpwhr);
3966 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3968 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
3970 DWORD dwCode,dwCodeLength = sizeof(DWORD);
3971 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
3972 && (dwCode == 302 || dwCode == 301 || dwCode == 303))
3974 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3975 dwBufferSize=sizeof(szNewLocation);
3976 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
3978 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3980 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3981 lpwhr->lpszVerb = heap_strdupW(szGET);
3983 HTTP_DrainContent(lpwhr);
3984 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3986 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3987 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3988 res = HTTP_HandleRedirect(lpwhr, new_url);
3989 if (res == ERROR_SUCCESS)
3990 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
3991 HeapFree( GetProcessHeap(), 0, new_url );
3997 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)lpwhr->hdr.hInternet : 0);
3998 iar.dwError = res;
4000 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4001 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4002 sizeof(INTERNET_ASYNC_RESULT));
4003 return res;
4006 /***********************************************************************
4007 * HttpEndRequestA (WININET.@)
4009 * Ends an HTTP request that was started by HttpSendRequestEx
4011 * RETURNS
4012 * TRUE if successful
4013 * FALSE on failure
4016 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
4017 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4019 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
4021 if (lpBuffersOut)
4023 SetLastError(ERROR_INVALID_PARAMETER);
4024 return FALSE;
4027 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
4030 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
4032 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
4033 http_request_t *lpwhr = (http_request_t*)work->hdr;
4035 TRACE("%p\n", lpwhr);
4037 HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
4040 /***********************************************************************
4041 * HttpEndRequestW (WININET.@)
4043 * Ends an HTTP request that was started by HttpSendRequestEx
4045 * RETURNS
4046 * TRUE if successful
4047 * FALSE on failure
4050 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
4051 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4053 http_request_t *lpwhr;
4054 DWORD res;
4056 TRACE("-->\n");
4058 if (lpBuffersOut)
4060 SetLastError(ERROR_INVALID_PARAMETER);
4061 return FALSE;
4064 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
4066 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4068 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
4069 if (lpwhr)
4070 WININET_Release( &lpwhr->hdr );
4071 return FALSE;
4073 lpwhr->hdr.dwFlags |= dwFlags;
4075 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4077 WORKREQUEST work;
4078 struct WORKREQ_HTTPENDREQUESTW *request;
4080 work.asyncproc = AsyncHttpEndRequestProc;
4081 work.hdr = WININET_AddRef( &lpwhr->hdr );
4083 request = &work.u.HttpEndRequestW;
4084 request->dwFlags = dwFlags;
4085 request->dwContext = dwContext;
4087 INTERNET_AsyncCall(&work);
4088 res = ERROR_IO_PENDING;
4090 else
4091 res = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
4093 WININET_Release( &lpwhr->hdr );
4094 TRACE("%u <--\n", res);
4095 if(res != ERROR_SUCCESS)
4096 SetLastError(res);
4097 return res == ERROR_SUCCESS;
4100 /***********************************************************************
4101 * HttpSendRequestExA (WININET.@)
4103 * Sends the specified request to the HTTP server and allows chunked
4104 * transfers.
4106 * RETURNS
4107 * Success: TRUE
4108 * Failure: FALSE, call GetLastError() for more information.
4110 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
4111 LPINTERNET_BUFFERSA lpBuffersIn,
4112 LPINTERNET_BUFFERSA lpBuffersOut,
4113 DWORD dwFlags, DWORD_PTR dwContext)
4115 INTERNET_BUFFERSW BuffersInW;
4116 BOOL rc = FALSE;
4117 DWORD headerlen;
4118 LPWSTR header = NULL;
4120 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4121 lpBuffersOut, dwFlags, dwContext);
4123 if (lpBuffersIn)
4125 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
4126 if (lpBuffersIn->lpcszHeader)
4128 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
4129 lpBuffersIn->dwHeadersLength,0,0);
4130 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
4131 if (!(BuffersInW.lpcszHeader = header))
4133 SetLastError(ERROR_OUTOFMEMORY);
4134 return FALSE;
4136 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
4137 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4138 header, headerlen);
4140 else
4141 BuffersInW.lpcszHeader = NULL;
4142 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
4143 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
4144 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
4145 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
4146 BuffersInW.Next = NULL;
4149 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
4151 HeapFree(GetProcessHeap(),0,header);
4153 return rc;
4156 /***********************************************************************
4157 * HttpSendRequestExW (WININET.@)
4159 * Sends the specified request to the HTTP server and allows chunked
4160 * transfers
4162 * RETURNS
4163 * Success: TRUE
4164 * Failure: FALSE, call GetLastError() for more information.
4166 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
4167 LPINTERNET_BUFFERSW lpBuffersIn,
4168 LPINTERNET_BUFFERSW lpBuffersOut,
4169 DWORD dwFlags, DWORD_PTR dwContext)
4171 http_request_t *lpwhr;
4172 http_session_t *lpwhs;
4173 appinfo_t *hIC;
4174 DWORD res;
4176 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4177 lpBuffersOut, dwFlags, dwContext);
4179 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
4181 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4183 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4184 goto lend;
4187 lpwhs = lpwhr->lpHttpSession;
4188 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
4189 hIC = lpwhs->lpAppInfo;
4190 assert(hIC->hdr.htype == WH_HINIT);
4192 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4194 WORKREQUEST workRequest;
4195 struct WORKREQ_HTTPSENDREQUESTW *req;
4197 workRequest.asyncproc = AsyncHttpSendRequestProc;
4198 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4199 req = &workRequest.u.HttpSendRequestW;
4200 if (lpBuffersIn)
4202 DWORD size = 0;
4204 if (lpBuffersIn->lpcszHeader)
4206 if (lpBuffersIn->dwHeadersLength == ~0u)
4207 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
4208 else
4209 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
4211 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
4212 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
4214 else req->lpszHeader = NULL;
4216 req->dwHeaderLength = size / sizeof(WCHAR);
4217 req->lpOptional = lpBuffersIn->lpvBuffer;
4218 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
4219 req->dwContentLength = lpBuffersIn->dwBufferTotal;
4221 else
4223 req->lpszHeader = NULL;
4224 req->dwHeaderLength = 0;
4225 req->lpOptional = NULL;
4226 req->dwOptionalLength = 0;
4227 req->dwContentLength = 0;
4230 req->bEndRequest = FALSE;
4232 INTERNET_AsyncCall(&workRequest);
4234 * This is from windows.
4236 res = ERROR_IO_PENDING;
4238 else
4240 if (lpBuffersIn)
4241 res = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4242 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
4243 lpBuffersIn->dwBufferTotal, FALSE);
4244 else
4245 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
4248 lend:
4249 if ( lpwhr )
4250 WININET_Release( &lpwhr->hdr );
4252 TRACE("<---\n");
4253 SetLastError(res);
4254 return res == ERROR_SUCCESS;
4257 /***********************************************************************
4258 * HttpSendRequestW (WININET.@)
4260 * Sends the specified request to the HTTP server
4262 * RETURNS
4263 * TRUE on success
4264 * FALSE on failure
4267 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4268 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4270 http_request_t *lpwhr;
4271 http_session_t *lpwhs = NULL;
4272 appinfo_t *hIC = NULL;
4273 DWORD res = ERROR_SUCCESS;
4275 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4276 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4278 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
4279 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4281 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4282 goto lend;
4285 lpwhs = lpwhr->lpHttpSession;
4286 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
4288 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4289 goto lend;
4292 hIC = lpwhs->lpAppInfo;
4293 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
4295 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4296 goto lend;
4299 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4301 WORKREQUEST workRequest;
4302 struct WORKREQ_HTTPSENDREQUESTW *req;
4304 workRequest.asyncproc = AsyncHttpSendRequestProc;
4305 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4306 req = &workRequest.u.HttpSendRequestW;
4307 if (lpszHeaders)
4309 DWORD size;
4311 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4312 else size = dwHeaderLength * sizeof(WCHAR);
4314 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4315 memcpy(req->lpszHeader, lpszHeaders, size);
4317 else
4318 req->lpszHeader = 0;
4319 req->dwHeaderLength = dwHeaderLength;
4320 req->lpOptional = lpOptional;
4321 req->dwOptionalLength = dwOptionalLength;
4322 req->dwContentLength = dwOptionalLength;
4323 req->bEndRequest = TRUE;
4325 INTERNET_AsyncCall(&workRequest);
4327 * This is from windows.
4329 res = ERROR_IO_PENDING;
4331 else
4333 res = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
4334 dwHeaderLength, lpOptional, dwOptionalLength,
4335 dwOptionalLength, TRUE);
4337 lend:
4338 if( lpwhr )
4339 WININET_Release( &lpwhr->hdr );
4341 SetLastError(res);
4342 return res == ERROR_SUCCESS;
4345 /***********************************************************************
4346 * HttpSendRequestA (WININET.@)
4348 * Sends the specified request to the HTTP server
4350 * RETURNS
4351 * TRUE on success
4352 * FALSE on failure
4355 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4356 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4358 BOOL result;
4359 LPWSTR szHeaders=NULL;
4360 DWORD nLen=dwHeaderLength;
4361 if(lpszHeaders!=NULL)
4363 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4364 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4365 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4367 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4368 HeapFree(GetProcessHeap(),0,szHeaders);
4369 return result;
4372 /***********************************************************************
4373 * HTTPSESSION_Destroy (internal)
4375 * Deallocate session handle
4378 static void HTTPSESSION_Destroy(object_header_t *hdr)
4380 http_session_t *lpwhs = (http_session_t*) hdr;
4382 TRACE("%p\n", lpwhs);
4384 WININET_Release(&lpwhs->lpAppInfo->hdr);
4386 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4387 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4388 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4389 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4390 HeapFree(GetProcessHeap(), 0, lpwhs);
4393 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4395 switch(option) {
4396 case INTERNET_OPTION_HANDLE_TYPE:
4397 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4399 if (*size < sizeof(ULONG))
4400 return ERROR_INSUFFICIENT_BUFFER;
4402 *size = sizeof(DWORD);
4403 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4404 return ERROR_SUCCESS;
4407 return INET_QueryOption(hdr, option, buffer, size, unicode);
4410 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4412 http_session_t *ses = (http_session_t*)hdr;
4414 switch(option) {
4415 case INTERNET_OPTION_USERNAME:
4417 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4418 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4419 return ERROR_SUCCESS;
4421 case INTERNET_OPTION_PASSWORD:
4423 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4424 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4425 return ERROR_SUCCESS;
4427 default: break;
4430 return ERROR_INTERNET_INVALID_OPTION;
4433 static const object_vtbl_t HTTPSESSIONVtbl = {
4434 HTTPSESSION_Destroy,
4435 NULL,
4436 HTTPSESSION_QueryOption,
4437 HTTPSESSION_SetOption,
4438 NULL,
4439 NULL,
4440 NULL,
4441 NULL,
4442 NULL
4446 /***********************************************************************
4447 * HTTP_Connect (internal)
4449 * Create http session handle
4451 * RETURNS
4452 * HINTERNET a session handle on success
4453 * NULL on failure
4456 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4457 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4458 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4459 DWORD dwInternalFlags, HINTERNET *ret)
4461 http_session_t *lpwhs = NULL;
4462 HINTERNET handle = NULL;
4463 DWORD res = ERROR_SUCCESS;
4465 TRACE("-->\n");
4467 if (!lpszServerName || !lpszServerName[0])
4468 return ERROR_INVALID_PARAMETER;
4470 assert( hIC->hdr.htype == WH_HINIT );
4472 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4473 if (!lpwhs)
4474 return ERROR_OUTOFMEMORY;
4477 * According to my tests. The name is not resolved until a request is sent
4480 lpwhs->hdr.htype = WH_HHTTPSESSION;
4481 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4482 lpwhs->hdr.dwFlags = dwFlags;
4483 lpwhs->hdr.dwContext = dwContext;
4484 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4485 lpwhs->hdr.refs = 1;
4486 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4488 WININET_AddRef( &hIC->hdr );
4489 lpwhs->lpAppInfo = hIC;
4490 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4492 handle = WININET_AllocHandle( &lpwhs->hdr );
4493 if (NULL == handle)
4495 ERR("Failed to alloc handle\n");
4496 res = ERROR_OUTOFMEMORY;
4497 goto lerror;
4500 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4501 if(hIC->lpszProxyBypass)
4502 FIXME("Proxy bypass is ignored.\n");
4504 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4505 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4506 if (lpszUserName && lpszUserName[0])
4507 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4508 if (lpszPassword && lpszPassword[0])
4509 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4510 lpwhs->nServerPort = nServerPort;
4511 lpwhs->nHostPort = nServerPort;
4513 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4514 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4516 INTERNET_SendCallback(&hIC->hdr, dwContext,
4517 INTERNET_STATUS_HANDLE_CREATED, &handle,
4518 sizeof(handle));
4521 lerror:
4522 if( lpwhs )
4523 WININET_Release( &lpwhs->hdr );
4526 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4527 * windows
4530 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4532 if(res == ERROR_SUCCESS)
4533 *ret = handle;
4534 return res;
4538 /***********************************************************************
4539 * HTTP_OpenConnection (internal)
4541 * Connect to a web server
4543 * RETURNS
4545 * TRUE on success
4546 * FALSE on failure
4548 static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
4550 http_session_t *lpwhs;
4551 appinfo_t *hIC = NULL;
4552 char szaddr[INET6_ADDRSTRLEN];
4553 const void *addr;
4554 DWORD res = ERROR_SUCCESS;
4556 TRACE("-->\n");
4559 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4561 res = ERROR_INVALID_PARAMETER;
4562 goto lend;
4565 if (NETCON_connected(&lpwhr->netConnection))
4566 goto lend;
4567 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
4569 lpwhs = lpwhr->lpHttpSession;
4571 hIC = lpwhs->lpAppInfo;
4572 switch (lpwhs->socketAddress.ss_family)
4574 case AF_INET:
4575 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4576 break;
4577 case AF_INET6:
4578 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4579 break;
4580 default:
4581 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4582 return ERROR_INTERNET_NAME_NOT_RESOLVED;
4584 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4585 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4586 INTERNET_STATUS_CONNECTING_TO_SERVER,
4587 szaddr,
4588 strlen(szaddr)+1);
4590 res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
4591 if (res != ERROR_SUCCESS)
4593 WARN("Socket creation failed: %u\n", res);
4594 goto lend;
4597 res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4598 lpwhs->sa_len);
4599 if(res != ERROR_SUCCESS)
4600 goto lend;
4602 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4603 INTERNET_STATUS_CONNECTED_TO_SERVER,
4604 szaddr, strlen(szaddr)+1);
4606 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4608 /* Note: we differ from Microsoft's WinINet here. they seem to have
4609 * a bug that causes no status callbacks to be sent when starting
4610 * a tunnel to a proxy server using the CONNECT verb. i believe our
4611 * behaviour to be more correct and to not cause any incompatibilities
4612 * because using a secure connection through a proxy server is a rare
4613 * case that would be hard for anyone to depend on */
4614 if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS) {
4615 HTTPREQ_CloseConnection(&lpwhr->hdr);
4616 goto lend;
4619 res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
4620 if(res != ERROR_SUCCESS)
4622 WARN("Couldn't connect securely to host\n");
4624 if((lpwhr->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4625 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4626 || res == ERROR_INTERNET_INVALID_CA
4627 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4628 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4629 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4630 || res == ERROR_INTERNET_SEC_INVALID_CERT
4631 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4632 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4634 HTTPREQ_CloseConnection(&lpwhr->hdr);
4635 goto lend;
4640 lend:
4641 lpwhr->read_pos = lpwhr->read_size = 0;
4642 lpwhr->read_chunked = FALSE;
4644 TRACE("%d <--\n", res);
4645 return res;
4649 /***********************************************************************
4650 * HTTP_clear_response_headers (internal)
4652 * clear out any old response headers
4654 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4656 DWORD i;
4658 for( i=0; i<lpwhr->nCustHeaders; i++)
4660 if( !lpwhr->pCustHeaders[i].lpszField )
4661 continue;
4662 if( !lpwhr->pCustHeaders[i].lpszValue )
4663 continue;
4664 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4665 continue;
4666 HTTP_DeleteCustomHeader( lpwhr, i );
4667 i--;
4671 /***********************************************************************
4672 * HTTP_GetResponseHeaders (internal)
4674 * Read server response
4676 * RETURNS
4678 * TRUE on success
4679 * FALSE on error
4681 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4683 INT cbreaks = 0;
4684 WCHAR buffer[MAX_REPLY_LEN];
4685 DWORD buflen = MAX_REPLY_LEN;
4686 BOOL bSuccess = FALSE;
4687 INT rc = 0;
4688 char bufferA[MAX_REPLY_LEN];
4689 LPWSTR status_code = NULL, status_text = NULL;
4690 DWORD cchMaxRawHeaders = 1024;
4691 LPWSTR lpszRawHeaders = NULL;
4692 LPWSTR temp;
4693 DWORD cchRawHeaders = 0;
4694 BOOL codeHundred = FALSE;
4696 TRACE("-->\n");
4698 if (!NETCON_connected(&lpwhr->netConnection))
4699 goto lend;
4701 do {
4702 static const WCHAR szHundred[] = {'1','0','0',0};
4704 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4706 buflen = MAX_REPLY_LEN;
4707 if (!read_line(lpwhr, bufferA, &buflen))
4708 goto lend;
4710 /* clear old response headers (eg. from a redirect response) */
4711 if (clear) {
4712 HTTP_clear_response_headers( lpwhr );
4713 clear = FALSE;
4716 rc += buflen;
4717 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4718 /* check is this a status code line? */
4719 if (!strncmpW(buffer, g_szHttp1_0, 4))
4721 /* split the version from the status code */
4722 status_code = strchrW( buffer, ' ' );
4723 if( !status_code )
4724 goto lend;
4725 *status_code++=0;
4727 /* split the status code from the status text */
4728 status_text = strchrW( status_code, ' ' );
4729 if( !status_text )
4730 goto lend;
4731 *status_text++=0;
4733 TRACE("version [%s] status code [%s] status text [%s]\n",
4734 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4736 codeHundred = (!strcmpW(status_code, szHundred));
4738 else if (!codeHundred)
4740 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
4742 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
4743 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
4745 lpwhr->lpszVersion = heap_strdupW(g_szHttp1_0);
4746 lpwhr->lpszStatusText = heap_strdupW(szOK);
4748 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4749 lpwhr->lpszRawHeaders = heap_strdupW(szDefaultHeader);
4751 bSuccess = TRUE;
4752 goto lend;
4754 } while (codeHundred);
4756 /* Add status code */
4757 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4758 HTTP_ADDHDR_FLAG_REPLACE);
4760 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4761 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4763 lpwhr->lpszVersion = heap_strdupW(buffer);
4764 lpwhr->lpszStatusText = heap_strdupW(status_text);
4766 /* Restore the spaces */
4767 *(status_code-1) = ' ';
4768 *(status_text-1) = ' ';
4770 /* regenerate raw headers */
4771 lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4772 if (!lpszRawHeaders) goto lend;
4774 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4775 cchMaxRawHeaders *= 2;
4776 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4777 if (temp == NULL) goto lend;
4778 lpszRawHeaders = temp;
4779 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4780 cchRawHeaders += (buflen-1);
4781 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4782 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4783 lpszRawHeaders[cchRawHeaders] = '\0';
4785 /* Parse each response line */
4788 buflen = MAX_REPLY_LEN;
4789 if (read_line(lpwhr, bufferA, &buflen))
4791 LPWSTR * pFieldAndValue;
4793 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4795 if (!bufferA[0]) break;
4796 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4798 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4799 if (pFieldAndValue)
4801 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4802 cchMaxRawHeaders *= 2;
4803 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4804 if (temp == NULL) goto lend;
4805 lpszRawHeaders = temp;
4806 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4807 cchRawHeaders += (buflen-1);
4808 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4809 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4810 lpszRawHeaders[cchRawHeaders] = '\0';
4812 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4813 HTTP_ADDREQ_FLAG_ADD );
4815 HTTP_FreeTokens(pFieldAndValue);
4818 else
4820 cbreaks++;
4821 if (cbreaks >= 2)
4822 break;
4824 }while(1);
4826 /* make sure the response header is terminated with an empty line. Some apps really
4827 truly care about that empty line being there for some reason. Just add it to the
4828 header. */
4829 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4831 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4832 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4833 if (temp == NULL) goto lend;
4834 lpszRawHeaders = temp;
4837 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4839 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4840 lpwhr->lpszRawHeaders = lpszRawHeaders;
4841 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4842 bSuccess = TRUE;
4844 lend:
4846 TRACE("<--\n");
4847 if (bSuccess)
4848 return rc;
4849 else
4851 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4852 return 0;
4856 /***********************************************************************
4857 * HTTP_InterpretHttpHeader (internal)
4859 * Parse server response
4861 * RETURNS
4863 * Pointer to array of field, value, NULL on success.
4864 * NULL on error.
4866 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4868 LPWSTR * pTokenPair;
4869 LPWSTR pszColon;
4870 INT len;
4872 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4874 pszColon = strchrW(buffer, ':');
4875 /* must have two tokens */
4876 if (!pszColon)
4878 HTTP_FreeTokens(pTokenPair);
4879 if (buffer[0])
4880 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4881 return NULL;
4884 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4885 if (!pTokenPair[0])
4887 HTTP_FreeTokens(pTokenPair);
4888 return NULL;
4890 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4891 pTokenPair[0][pszColon - buffer] = '\0';
4893 /* skip colon */
4894 pszColon++;
4895 len = strlenW(pszColon);
4896 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4897 if (!pTokenPair[1])
4899 HTTP_FreeTokens(pTokenPair);
4900 return NULL;
4902 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4904 strip_spaces(pTokenPair[0]);
4905 strip_spaces(pTokenPair[1]);
4907 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4908 return pTokenPair;
4911 /***********************************************************************
4912 * HTTP_ProcessHeader (internal)
4914 * Stuff header into header tables according to <dwModifier>
4918 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4920 static DWORD HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4922 LPHTTPHEADERW lphttpHdr = NULL;
4923 INT index = -1;
4924 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4925 DWORD res = ERROR_HTTP_INVALID_HEADER;
4927 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4929 /* REPLACE wins out over ADD */
4930 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4931 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4933 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4934 index = -1;
4935 else
4936 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4938 if (index >= 0)
4940 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4941 return ERROR_HTTP_INVALID_HEADER;
4942 lphttpHdr = &lpwhr->pCustHeaders[index];
4944 else if (value)
4946 HTTPHEADERW hdr;
4948 hdr.lpszField = (LPWSTR)field;
4949 hdr.lpszValue = (LPWSTR)value;
4950 hdr.wFlags = hdr.wCount = 0;
4952 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4953 hdr.wFlags |= HDR_ISREQUEST;
4955 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4957 /* no value to delete */
4958 else return ERROR_SUCCESS;
4960 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4961 lphttpHdr->wFlags |= HDR_ISREQUEST;
4962 else
4963 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4965 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4967 HTTP_DeleteCustomHeader( lpwhr, index );
4969 if (value)
4971 HTTPHEADERW hdr;
4973 hdr.lpszField = (LPWSTR)field;
4974 hdr.lpszValue = (LPWSTR)value;
4975 hdr.wFlags = hdr.wCount = 0;
4977 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4978 hdr.wFlags |= HDR_ISREQUEST;
4980 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4983 return ERROR_SUCCESS;
4985 else if (dwModifier & COALESCEFLAGS)
4987 LPWSTR lpsztmp;
4988 WCHAR ch = 0;
4989 INT len = 0;
4990 INT origlen = strlenW(lphttpHdr->lpszValue);
4991 INT valuelen = strlenW(value);
4993 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4995 ch = ',';
4996 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4998 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5000 ch = ';';
5001 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5004 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5006 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5007 if (lpsztmp)
5009 lphttpHdr->lpszValue = lpsztmp;
5010 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5011 if (ch > 0)
5013 lphttpHdr->lpszValue[origlen] = ch;
5014 origlen++;
5015 lphttpHdr->lpszValue[origlen] = ' ';
5016 origlen++;
5019 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5020 lphttpHdr->lpszValue[len] = '\0';
5021 res = ERROR_SUCCESS;
5023 else
5025 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
5026 res = ERROR_OUTOFMEMORY;
5029 TRACE("<-- %d\n", res);
5030 return res;
5034 /***********************************************************************
5035 * HTTP_FinishedReading (internal)
5037 * Called when all content from server has been read by client.
5040 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
5042 BOOL keepalive = HTTP_KeepAlive(lpwhr);
5044 TRACE("\n");
5047 if (!keepalive)
5049 HTTPREQ_CloseConnection(&lpwhr->hdr);
5052 /* FIXME: store data in the URL cache here */
5054 return TRUE;
5058 /***********************************************************************
5059 * HTTP_GetCustomHeaderIndex (internal)
5061 * Return index of custom header from header array
5064 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
5065 int requested_index, BOOL request_only)
5067 DWORD index;
5069 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5071 for (index = 0; index < lpwhr->nCustHeaders; index++)
5073 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
5074 continue;
5076 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
5077 continue;
5079 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
5080 continue;
5082 if (requested_index == 0)
5083 break;
5084 requested_index --;
5087 if (index >= lpwhr->nCustHeaders)
5088 index = -1;
5090 TRACE("Return: %d\n", index);
5091 return index;
5095 /***********************************************************************
5096 * HTTP_InsertCustomHeader (internal)
5098 * Insert header into array
5101 static DWORD HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
5103 INT count;
5104 LPHTTPHEADERW lph = NULL;
5106 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5107 count = lpwhr->nCustHeaders + 1;
5108 if (count > 1)
5109 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
5110 else
5111 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
5113 if (!lph)
5114 return ERROR_OUTOFMEMORY;
5116 lpwhr->pCustHeaders = lph;
5117 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
5118 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
5119 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
5120 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
5121 lpwhr->nCustHeaders++;
5123 return ERROR_SUCCESS;
5127 /***********************************************************************
5128 * HTTP_DeleteCustomHeader (internal)
5130 * Delete header from array
5131 * If this function is called, the indexs may change.
5133 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
5135 if( lpwhr->nCustHeaders <= 0 )
5136 return FALSE;
5137 if( index >= lpwhr->nCustHeaders )
5138 return FALSE;
5139 lpwhr->nCustHeaders--;
5141 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
5142 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
5144 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
5145 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
5146 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
5148 return TRUE;
5152 /***********************************************************************
5153 * HTTP_VerifyValidHeader (internal)
5155 * Verify the given header is not invalid for the given http request
5158 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
5160 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5161 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
5162 return ERROR_HTTP_INVALID_HEADER;
5164 return ERROR_SUCCESS;
5167 /***********************************************************************
5168 * IsHostInProxyBypassList (@)
5170 * Undocumented
5173 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
5175 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
5176 return FALSE;