wininet: Commit URL cache entry when cache file is closed.
[wine.git] / dlls / wininet / http.c
blobe74c97418d33c4ecf7551405dc21a4862b9242dd
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 || dwStatusCode==HTTP_STATUS_MOVED) &&
3791 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
3793 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3795 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3796 lpwhr->lpszVerb = heap_strdupW(szGET);
3798 HTTP_DrainContent(lpwhr);
3799 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3801 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3802 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3803 res = HTTP_HandleRedirect(lpwhr, new_url);
3804 if (res == ERROR_SUCCESS)
3806 HeapFree(GetProcessHeap(), 0, requestString);
3807 loop_next = TRUE;
3809 HeapFree( GetProcessHeap(), 0, new_url );
3811 redirected = TRUE;
3814 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
3816 WCHAR szAuthValue[2048];
3817 dwBufferSize=2048;
3818 if (dwStatusCode == HTTP_STATUS_DENIED)
3820 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3821 DWORD dwIndex = 0;
3822 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3824 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3825 &lpwhr->pAuthInfo,
3826 lpwhr->lpHttpSession->lpszUserName,
3827 lpwhr->lpHttpSession->lpszPassword,
3828 Host->lpszValue))
3830 HeapFree(GetProcessHeap(), 0, requestString);
3831 loop_next = TRUE;
3832 break;
3836 if(!loop_next) {
3837 TRACE("Cleaning wrong authorization data\n");
3838 destroy_authinfo(lpwhr->pAuthInfo);
3839 lpwhr->pAuthInfo = NULL;
3842 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3844 DWORD dwIndex = 0;
3845 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3847 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3848 &lpwhr->pProxyAuthInfo,
3849 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3850 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
3851 NULL))
3853 loop_next = TRUE;
3854 break;
3858 if(!loop_next) {
3859 TRACE("Cleaning wrong proxy authorization data\n");
3860 destroy_authinfo(lpwhr->pProxyAuthInfo);
3861 lpwhr->pProxyAuthInfo = NULL;
3866 else
3867 res = ERROR_SUCCESS;
3869 while (loop_next);
3871 if(res == ERROR_SUCCESS) {
3872 WCHAR url[INTERNET_MAX_URL_LENGTH];
3873 WCHAR cacheFileName[MAX_PATH+1];
3874 BOOL b;
3876 b = HTTP_GetRequestURL(lpwhr, url);
3877 if(!b) {
3878 WARN("Could not get URL\n");
3879 goto lend;
3882 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3883 if(b) {
3884 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
3885 CloseHandle(lpwhr->hCacheFile);
3887 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3888 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3889 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3890 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3891 WARN("Could not create file: %u\n", GetLastError());
3892 lpwhr->hCacheFile = NULL;
3894 }else {
3895 WARN("Could not create cache entry: %08x\n", GetLastError());
3899 lend:
3901 HeapFree(GetProcessHeap(), 0, requestString);
3903 /* TODO: send notification for P3P header */
3905 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3907 if (res == ERROR_SUCCESS && lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite)
3908 HTTP_ReceiveRequestData(lpwhr, TRUE);
3909 else
3911 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)lpwhr->hdr.hInternet : 0);
3912 iar.dwError = res;
3914 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3915 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3916 sizeof(INTERNET_ASYNC_RESULT));
3920 TRACE("<--\n");
3921 return res;
3924 /***********************************************************************
3926 * Helper functions for the HttpSendRequest(Ex) functions
3929 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
3931 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
3932 http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
3934 TRACE("%p\n", lpwhr);
3936 HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
3937 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
3938 req->dwContentLength, req->bEndRequest);
3940 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
3944 static DWORD HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
3946 INT responseLen;
3947 DWORD dwBufferSize;
3948 INTERNET_ASYNC_RESULT iar;
3949 DWORD res = ERROR_SUCCESS;
3951 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3952 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3954 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3955 if (!responseLen)
3956 res = ERROR_HTTP_HEADER_NOT_FOUND;
3958 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3959 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
3961 /* process cookies here. Is this right? */
3962 HTTP_ProcessCookies(lpwhr);
3964 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3966 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
3968 DWORD dwCode,dwCodeLength = sizeof(DWORD);
3969 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
3970 && (dwCode == 302 || dwCode == 301 || dwCode == 303))
3972 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3973 dwBufferSize=sizeof(szNewLocation);
3974 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
3976 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3978 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3979 lpwhr->lpszVerb = heap_strdupW(szGET);
3981 HTTP_DrainContent(lpwhr);
3982 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3984 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3985 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3986 res = HTTP_HandleRedirect(lpwhr, new_url);
3987 if (res == ERROR_SUCCESS)
3988 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
3989 HeapFree( GetProcessHeap(), 0, new_url );
3995 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)lpwhr->hdr.hInternet : 0);
3996 iar.dwError = res;
3998 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3999 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4000 sizeof(INTERNET_ASYNC_RESULT));
4001 return res;
4004 /***********************************************************************
4005 * HttpEndRequestA (WININET.@)
4007 * Ends an HTTP request that was started by HttpSendRequestEx
4009 * RETURNS
4010 * TRUE if successful
4011 * FALSE on failure
4014 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
4015 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4017 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
4019 if (lpBuffersOut)
4021 SetLastError(ERROR_INVALID_PARAMETER);
4022 return FALSE;
4025 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
4028 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
4030 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
4031 http_request_t *lpwhr = (http_request_t*)work->hdr;
4033 TRACE("%p\n", lpwhr);
4035 HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
4038 /***********************************************************************
4039 * HttpEndRequestW (WININET.@)
4041 * Ends an HTTP request that was started by HttpSendRequestEx
4043 * RETURNS
4044 * TRUE if successful
4045 * FALSE on failure
4048 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
4049 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4051 http_request_t *lpwhr;
4052 DWORD res;
4054 TRACE("-->\n");
4056 if (lpBuffersOut)
4058 SetLastError(ERROR_INVALID_PARAMETER);
4059 return FALSE;
4062 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
4064 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4066 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
4067 if (lpwhr)
4068 WININET_Release( &lpwhr->hdr );
4069 return FALSE;
4071 lpwhr->hdr.dwFlags |= dwFlags;
4073 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4075 WORKREQUEST work;
4076 struct WORKREQ_HTTPENDREQUESTW *request;
4078 work.asyncproc = AsyncHttpEndRequestProc;
4079 work.hdr = WININET_AddRef( &lpwhr->hdr );
4081 request = &work.u.HttpEndRequestW;
4082 request->dwFlags = dwFlags;
4083 request->dwContext = dwContext;
4085 INTERNET_AsyncCall(&work);
4086 res = ERROR_IO_PENDING;
4088 else
4089 res = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
4091 WININET_Release( &lpwhr->hdr );
4092 TRACE("%u <--\n", res);
4093 if(res != ERROR_SUCCESS)
4094 SetLastError(res);
4095 return res == ERROR_SUCCESS;
4098 /***********************************************************************
4099 * HttpSendRequestExA (WININET.@)
4101 * Sends the specified request to the HTTP server and allows chunked
4102 * transfers.
4104 * RETURNS
4105 * Success: TRUE
4106 * Failure: FALSE, call GetLastError() for more information.
4108 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
4109 LPINTERNET_BUFFERSA lpBuffersIn,
4110 LPINTERNET_BUFFERSA lpBuffersOut,
4111 DWORD dwFlags, DWORD_PTR dwContext)
4113 INTERNET_BUFFERSW BuffersInW;
4114 BOOL rc = FALSE;
4115 DWORD headerlen;
4116 LPWSTR header = NULL;
4118 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4119 lpBuffersOut, dwFlags, dwContext);
4121 if (lpBuffersIn)
4123 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
4124 if (lpBuffersIn->lpcszHeader)
4126 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
4127 lpBuffersIn->dwHeadersLength,0,0);
4128 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
4129 if (!(BuffersInW.lpcszHeader = header))
4131 SetLastError(ERROR_OUTOFMEMORY);
4132 return FALSE;
4134 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
4135 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4136 header, headerlen);
4138 else
4139 BuffersInW.lpcszHeader = NULL;
4140 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
4141 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
4142 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
4143 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
4144 BuffersInW.Next = NULL;
4147 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
4149 HeapFree(GetProcessHeap(),0,header);
4151 return rc;
4154 /***********************************************************************
4155 * HttpSendRequestExW (WININET.@)
4157 * Sends the specified request to the HTTP server and allows chunked
4158 * transfers
4160 * RETURNS
4161 * Success: TRUE
4162 * Failure: FALSE, call GetLastError() for more information.
4164 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
4165 LPINTERNET_BUFFERSW lpBuffersIn,
4166 LPINTERNET_BUFFERSW lpBuffersOut,
4167 DWORD dwFlags, DWORD_PTR dwContext)
4169 http_request_t *lpwhr;
4170 http_session_t *lpwhs;
4171 appinfo_t *hIC;
4172 DWORD res;
4174 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4175 lpBuffersOut, dwFlags, dwContext);
4177 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
4179 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4181 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4182 goto lend;
4185 lpwhs = lpwhr->lpHttpSession;
4186 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
4187 hIC = lpwhs->lpAppInfo;
4188 assert(hIC->hdr.htype == WH_HINIT);
4190 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4192 WORKREQUEST workRequest;
4193 struct WORKREQ_HTTPSENDREQUESTW *req;
4195 workRequest.asyncproc = AsyncHttpSendRequestProc;
4196 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4197 req = &workRequest.u.HttpSendRequestW;
4198 if (lpBuffersIn)
4200 DWORD size = 0;
4202 if (lpBuffersIn->lpcszHeader)
4204 if (lpBuffersIn->dwHeadersLength == ~0u)
4205 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
4206 else
4207 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
4209 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
4210 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
4212 else req->lpszHeader = NULL;
4214 req->dwHeaderLength = size / sizeof(WCHAR);
4215 req->lpOptional = lpBuffersIn->lpvBuffer;
4216 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
4217 req->dwContentLength = lpBuffersIn->dwBufferTotal;
4219 else
4221 req->lpszHeader = NULL;
4222 req->dwHeaderLength = 0;
4223 req->lpOptional = NULL;
4224 req->dwOptionalLength = 0;
4225 req->dwContentLength = 0;
4228 req->bEndRequest = FALSE;
4230 INTERNET_AsyncCall(&workRequest);
4232 * This is from windows.
4234 res = ERROR_IO_PENDING;
4236 else
4238 if (lpBuffersIn)
4239 res = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4240 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
4241 lpBuffersIn->dwBufferTotal, FALSE);
4242 else
4243 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
4246 lend:
4247 if ( lpwhr )
4248 WININET_Release( &lpwhr->hdr );
4250 TRACE("<---\n");
4251 SetLastError(res);
4252 return res == ERROR_SUCCESS;
4255 /***********************************************************************
4256 * HttpSendRequestW (WININET.@)
4258 * Sends the specified request to the HTTP server
4260 * RETURNS
4261 * TRUE on success
4262 * FALSE on failure
4265 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4266 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4268 http_request_t *lpwhr;
4269 http_session_t *lpwhs = NULL;
4270 appinfo_t *hIC = NULL;
4271 DWORD res = ERROR_SUCCESS;
4273 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4274 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4276 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
4277 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4279 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4280 goto lend;
4283 lpwhs = lpwhr->lpHttpSession;
4284 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
4286 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4287 goto lend;
4290 hIC = lpwhs->lpAppInfo;
4291 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
4293 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4294 goto lend;
4297 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4299 WORKREQUEST workRequest;
4300 struct WORKREQ_HTTPSENDREQUESTW *req;
4302 workRequest.asyncproc = AsyncHttpSendRequestProc;
4303 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4304 req = &workRequest.u.HttpSendRequestW;
4305 if (lpszHeaders)
4307 DWORD size;
4309 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4310 else size = dwHeaderLength * sizeof(WCHAR);
4312 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4313 memcpy(req->lpszHeader, lpszHeaders, size);
4315 else
4316 req->lpszHeader = 0;
4317 req->dwHeaderLength = dwHeaderLength;
4318 req->lpOptional = lpOptional;
4319 req->dwOptionalLength = dwOptionalLength;
4320 req->dwContentLength = dwOptionalLength;
4321 req->bEndRequest = TRUE;
4323 INTERNET_AsyncCall(&workRequest);
4325 * This is from windows.
4327 res = ERROR_IO_PENDING;
4329 else
4331 res = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
4332 dwHeaderLength, lpOptional, dwOptionalLength,
4333 dwOptionalLength, TRUE);
4335 lend:
4336 if( lpwhr )
4337 WININET_Release( &lpwhr->hdr );
4339 SetLastError(res);
4340 return res == ERROR_SUCCESS;
4343 /***********************************************************************
4344 * HttpSendRequestA (WININET.@)
4346 * Sends the specified request to the HTTP server
4348 * RETURNS
4349 * TRUE on success
4350 * FALSE on failure
4353 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4354 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4356 BOOL result;
4357 LPWSTR szHeaders=NULL;
4358 DWORD nLen=dwHeaderLength;
4359 if(lpszHeaders!=NULL)
4361 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4362 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4363 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4365 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4366 HeapFree(GetProcessHeap(),0,szHeaders);
4367 return result;
4370 /***********************************************************************
4371 * HTTPSESSION_Destroy (internal)
4373 * Deallocate session handle
4376 static void HTTPSESSION_Destroy(object_header_t *hdr)
4378 http_session_t *lpwhs = (http_session_t*) hdr;
4380 TRACE("%p\n", lpwhs);
4382 WININET_Release(&lpwhs->lpAppInfo->hdr);
4384 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4385 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4386 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4387 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4388 HeapFree(GetProcessHeap(), 0, lpwhs);
4391 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4393 switch(option) {
4394 case INTERNET_OPTION_HANDLE_TYPE:
4395 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4397 if (*size < sizeof(ULONG))
4398 return ERROR_INSUFFICIENT_BUFFER;
4400 *size = sizeof(DWORD);
4401 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4402 return ERROR_SUCCESS;
4405 return INET_QueryOption(hdr, option, buffer, size, unicode);
4408 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4410 http_session_t *ses = (http_session_t*)hdr;
4412 switch(option) {
4413 case INTERNET_OPTION_USERNAME:
4415 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4416 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4417 return ERROR_SUCCESS;
4419 case INTERNET_OPTION_PASSWORD:
4421 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4422 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4423 return ERROR_SUCCESS;
4425 default: break;
4428 return ERROR_INTERNET_INVALID_OPTION;
4431 static const object_vtbl_t HTTPSESSIONVtbl = {
4432 HTTPSESSION_Destroy,
4433 NULL,
4434 HTTPSESSION_QueryOption,
4435 HTTPSESSION_SetOption,
4436 NULL,
4437 NULL,
4438 NULL,
4439 NULL,
4440 NULL
4444 /***********************************************************************
4445 * HTTP_Connect (internal)
4447 * Create http session handle
4449 * RETURNS
4450 * HINTERNET a session handle on success
4451 * NULL on failure
4454 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4455 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4456 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4457 DWORD dwInternalFlags, HINTERNET *ret)
4459 http_session_t *lpwhs = NULL;
4460 HINTERNET handle = NULL;
4461 DWORD res = ERROR_SUCCESS;
4463 TRACE("-->\n");
4465 if (!lpszServerName || !lpszServerName[0])
4466 return ERROR_INVALID_PARAMETER;
4468 assert( hIC->hdr.htype == WH_HINIT );
4470 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4471 if (!lpwhs)
4472 return ERROR_OUTOFMEMORY;
4475 * According to my tests. The name is not resolved until a request is sent
4478 lpwhs->hdr.htype = WH_HHTTPSESSION;
4479 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4480 lpwhs->hdr.dwFlags = dwFlags;
4481 lpwhs->hdr.dwContext = dwContext;
4482 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4483 lpwhs->hdr.refs = 1;
4484 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4486 WININET_AddRef( &hIC->hdr );
4487 lpwhs->lpAppInfo = hIC;
4488 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4490 handle = WININET_AllocHandle( &lpwhs->hdr );
4491 if (NULL == handle)
4493 ERR("Failed to alloc handle\n");
4494 res = ERROR_OUTOFMEMORY;
4495 goto lerror;
4498 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4499 if(hIC->lpszProxyBypass)
4500 FIXME("Proxy bypass is ignored.\n");
4502 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4503 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4504 if (lpszUserName && lpszUserName[0])
4505 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4506 if (lpszPassword && lpszPassword[0])
4507 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4508 lpwhs->nServerPort = nServerPort;
4509 lpwhs->nHostPort = nServerPort;
4511 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4512 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4514 INTERNET_SendCallback(&hIC->hdr, dwContext,
4515 INTERNET_STATUS_HANDLE_CREATED, &handle,
4516 sizeof(handle));
4519 lerror:
4520 if( lpwhs )
4521 WININET_Release( &lpwhs->hdr );
4524 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4525 * windows
4528 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4530 if(res == ERROR_SUCCESS)
4531 *ret = handle;
4532 return res;
4536 /***********************************************************************
4537 * HTTP_OpenConnection (internal)
4539 * Connect to a web server
4541 * RETURNS
4543 * TRUE on success
4544 * FALSE on failure
4546 static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
4548 http_session_t *lpwhs;
4549 appinfo_t *hIC = NULL;
4550 char szaddr[INET6_ADDRSTRLEN];
4551 const void *addr;
4552 DWORD res = ERROR_SUCCESS;
4554 TRACE("-->\n");
4557 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4559 res = ERROR_INVALID_PARAMETER;
4560 goto lend;
4563 if (NETCON_connected(&lpwhr->netConnection))
4564 goto lend;
4565 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
4567 lpwhs = lpwhr->lpHttpSession;
4569 hIC = lpwhs->lpAppInfo;
4570 switch (lpwhs->socketAddress.ss_family)
4572 case AF_INET:
4573 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4574 break;
4575 case AF_INET6:
4576 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4577 break;
4578 default:
4579 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4580 return ERROR_INTERNET_NAME_NOT_RESOLVED;
4582 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4583 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4584 INTERNET_STATUS_CONNECTING_TO_SERVER,
4585 szaddr,
4586 strlen(szaddr)+1);
4588 res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
4589 if (res != ERROR_SUCCESS)
4591 WARN("Socket creation failed: %u\n", res);
4592 goto lend;
4595 res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4596 lpwhs->sa_len);
4597 if(res != ERROR_SUCCESS)
4598 goto lend;
4600 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4601 INTERNET_STATUS_CONNECTED_TO_SERVER,
4602 szaddr, strlen(szaddr)+1);
4604 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4606 /* Note: we differ from Microsoft's WinINet here. they seem to have
4607 * a bug that causes no status callbacks to be sent when starting
4608 * a tunnel to a proxy server using the CONNECT verb. i believe our
4609 * behaviour to be more correct and to not cause any incompatibilities
4610 * because using a secure connection through a proxy server is a rare
4611 * case that would be hard for anyone to depend on */
4612 if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS) {
4613 HTTPREQ_CloseConnection(&lpwhr->hdr);
4614 goto lend;
4617 res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
4618 if(res != ERROR_SUCCESS)
4620 WARN("Couldn't connect securely to host\n");
4622 if((lpwhr->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4623 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4624 || res == ERROR_INTERNET_INVALID_CA
4625 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4626 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4627 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4628 || res == ERROR_INTERNET_SEC_INVALID_CERT
4629 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4630 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4632 HTTPREQ_CloseConnection(&lpwhr->hdr);
4633 goto lend;
4638 lend:
4639 lpwhr->read_pos = lpwhr->read_size = 0;
4640 lpwhr->read_chunked = FALSE;
4642 TRACE("%d <--\n", res);
4643 return res;
4647 /***********************************************************************
4648 * HTTP_clear_response_headers (internal)
4650 * clear out any old response headers
4652 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4654 DWORD i;
4656 for( i=0; i<lpwhr->nCustHeaders; i++)
4658 if( !lpwhr->pCustHeaders[i].lpszField )
4659 continue;
4660 if( !lpwhr->pCustHeaders[i].lpszValue )
4661 continue;
4662 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4663 continue;
4664 HTTP_DeleteCustomHeader( lpwhr, i );
4665 i--;
4669 /***********************************************************************
4670 * HTTP_GetResponseHeaders (internal)
4672 * Read server response
4674 * RETURNS
4676 * TRUE on success
4677 * FALSE on error
4679 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4681 INT cbreaks = 0;
4682 WCHAR buffer[MAX_REPLY_LEN];
4683 DWORD buflen = MAX_REPLY_LEN;
4684 BOOL bSuccess = FALSE;
4685 INT rc = 0;
4686 char bufferA[MAX_REPLY_LEN];
4687 LPWSTR status_code = NULL, status_text = NULL;
4688 DWORD cchMaxRawHeaders = 1024;
4689 LPWSTR lpszRawHeaders = NULL;
4690 LPWSTR temp;
4691 DWORD cchRawHeaders = 0;
4692 BOOL codeHundred = FALSE;
4694 TRACE("-->\n");
4696 if (!NETCON_connected(&lpwhr->netConnection))
4697 goto lend;
4699 do {
4700 static const WCHAR szHundred[] = {'1','0','0',0};
4702 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4704 buflen = MAX_REPLY_LEN;
4705 if (!read_line(lpwhr, bufferA, &buflen))
4706 goto lend;
4708 /* clear old response headers (eg. from a redirect response) */
4709 if (clear) {
4710 HTTP_clear_response_headers( lpwhr );
4711 clear = FALSE;
4714 rc += buflen;
4715 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4716 /* check is this a status code line? */
4717 if (!strncmpW(buffer, g_szHttp1_0, 4))
4719 /* split the version from the status code */
4720 status_code = strchrW( buffer, ' ' );
4721 if( !status_code )
4722 goto lend;
4723 *status_code++=0;
4725 /* split the status code from the status text */
4726 status_text = strchrW( status_code, ' ' );
4727 if( !status_text )
4728 goto lend;
4729 *status_text++=0;
4731 TRACE("version [%s] status code [%s] status text [%s]\n",
4732 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4734 codeHundred = (!strcmpW(status_code, szHundred));
4736 else if (!codeHundred)
4738 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
4740 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
4741 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
4743 lpwhr->lpszVersion = heap_strdupW(g_szHttp1_0);
4744 lpwhr->lpszStatusText = heap_strdupW(szOK);
4746 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4747 lpwhr->lpszRawHeaders = heap_strdupW(szDefaultHeader);
4749 bSuccess = TRUE;
4750 goto lend;
4752 } while (codeHundred);
4754 /* Add status code */
4755 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4756 HTTP_ADDHDR_FLAG_REPLACE);
4758 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4759 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4761 lpwhr->lpszVersion = heap_strdupW(buffer);
4762 lpwhr->lpszStatusText = heap_strdupW(status_text);
4764 /* Restore the spaces */
4765 *(status_code-1) = ' ';
4766 *(status_text-1) = ' ';
4768 /* regenerate raw headers */
4769 lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4770 if (!lpszRawHeaders) goto lend;
4772 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4773 cchMaxRawHeaders *= 2;
4774 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4775 if (temp == NULL) goto lend;
4776 lpszRawHeaders = temp;
4777 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4778 cchRawHeaders += (buflen-1);
4779 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4780 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4781 lpszRawHeaders[cchRawHeaders] = '\0';
4783 /* Parse each response line */
4786 buflen = MAX_REPLY_LEN;
4787 if (read_line(lpwhr, bufferA, &buflen))
4789 LPWSTR * pFieldAndValue;
4791 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4793 if (!bufferA[0]) break;
4794 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4796 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4797 if (pFieldAndValue)
4799 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4800 cchMaxRawHeaders *= 2;
4801 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4802 if (temp == NULL) goto lend;
4803 lpszRawHeaders = temp;
4804 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4805 cchRawHeaders += (buflen-1);
4806 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4807 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4808 lpszRawHeaders[cchRawHeaders] = '\0';
4810 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4811 HTTP_ADDREQ_FLAG_ADD );
4813 HTTP_FreeTokens(pFieldAndValue);
4816 else
4818 cbreaks++;
4819 if (cbreaks >= 2)
4820 break;
4822 }while(1);
4824 /* make sure the response header is terminated with an empty line. Some apps really
4825 truly care about that empty line being there for some reason. Just add it to the
4826 header. */
4827 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4829 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4830 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4831 if (temp == NULL) goto lend;
4832 lpszRawHeaders = temp;
4835 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4837 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4838 lpwhr->lpszRawHeaders = lpszRawHeaders;
4839 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4840 bSuccess = TRUE;
4842 lend:
4844 TRACE("<--\n");
4845 if (bSuccess)
4846 return rc;
4847 else
4849 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4850 return 0;
4854 /***********************************************************************
4855 * HTTP_InterpretHttpHeader (internal)
4857 * Parse server response
4859 * RETURNS
4861 * Pointer to array of field, value, NULL on success.
4862 * NULL on error.
4864 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4866 LPWSTR * pTokenPair;
4867 LPWSTR pszColon;
4868 INT len;
4870 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4872 pszColon = strchrW(buffer, ':');
4873 /* must have two tokens */
4874 if (!pszColon)
4876 HTTP_FreeTokens(pTokenPair);
4877 if (buffer[0])
4878 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4879 return NULL;
4882 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4883 if (!pTokenPair[0])
4885 HTTP_FreeTokens(pTokenPair);
4886 return NULL;
4888 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4889 pTokenPair[0][pszColon - buffer] = '\0';
4891 /* skip colon */
4892 pszColon++;
4893 len = strlenW(pszColon);
4894 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4895 if (!pTokenPair[1])
4897 HTTP_FreeTokens(pTokenPair);
4898 return NULL;
4900 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4902 strip_spaces(pTokenPair[0]);
4903 strip_spaces(pTokenPair[1]);
4905 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4906 return pTokenPair;
4909 /***********************************************************************
4910 * HTTP_ProcessHeader (internal)
4912 * Stuff header into header tables according to <dwModifier>
4916 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4918 static DWORD HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4920 LPHTTPHEADERW lphttpHdr = NULL;
4921 INT index = -1;
4922 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4923 DWORD res = ERROR_HTTP_INVALID_HEADER;
4925 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4927 /* REPLACE wins out over ADD */
4928 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4929 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4931 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4932 index = -1;
4933 else
4934 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4936 if (index >= 0)
4938 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4939 return ERROR_HTTP_INVALID_HEADER;
4940 lphttpHdr = &lpwhr->pCustHeaders[index];
4942 else if (value)
4944 HTTPHEADERW hdr;
4946 hdr.lpszField = (LPWSTR)field;
4947 hdr.lpszValue = (LPWSTR)value;
4948 hdr.wFlags = hdr.wCount = 0;
4950 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4951 hdr.wFlags |= HDR_ISREQUEST;
4953 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4955 /* no value to delete */
4956 else return ERROR_SUCCESS;
4958 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4959 lphttpHdr->wFlags |= HDR_ISREQUEST;
4960 else
4961 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4963 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4965 HTTP_DeleteCustomHeader( lpwhr, index );
4967 if (value)
4969 HTTPHEADERW hdr;
4971 hdr.lpszField = (LPWSTR)field;
4972 hdr.lpszValue = (LPWSTR)value;
4973 hdr.wFlags = hdr.wCount = 0;
4975 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4976 hdr.wFlags |= HDR_ISREQUEST;
4978 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4981 return ERROR_SUCCESS;
4983 else if (dwModifier & COALESCEFLAGS)
4985 LPWSTR lpsztmp;
4986 WCHAR ch = 0;
4987 INT len = 0;
4988 INT origlen = strlenW(lphttpHdr->lpszValue);
4989 INT valuelen = strlenW(value);
4991 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4993 ch = ',';
4994 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4996 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4998 ch = ';';
4999 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5002 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5004 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5005 if (lpsztmp)
5007 lphttpHdr->lpszValue = lpsztmp;
5008 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5009 if (ch > 0)
5011 lphttpHdr->lpszValue[origlen] = ch;
5012 origlen++;
5013 lphttpHdr->lpszValue[origlen] = ' ';
5014 origlen++;
5017 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5018 lphttpHdr->lpszValue[len] = '\0';
5019 res = ERROR_SUCCESS;
5021 else
5023 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
5024 res = ERROR_OUTOFMEMORY;
5027 TRACE("<-- %d\n", res);
5028 return res;
5032 /***********************************************************************
5033 * HTTP_FinishedReading (internal)
5035 * Called when all content from server has been read by client.
5038 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
5040 BOOL keepalive = HTTP_KeepAlive(lpwhr);
5042 TRACE("\n");
5045 if (!keepalive)
5047 HTTPREQ_CloseConnection(&lpwhr->hdr);
5050 /* FIXME: store data in the URL cache here */
5052 return TRUE;
5056 /***********************************************************************
5057 * HTTP_GetCustomHeaderIndex (internal)
5059 * Return index of custom header from header array
5062 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
5063 int requested_index, BOOL request_only)
5065 DWORD index;
5067 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5069 for (index = 0; index < lpwhr->nCustHeaders; index++)
5071 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
5072 continue;
5074 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
5075 continue;
5077 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
5078 continue;
5080 if (requested_index == 0)
5081 break;
5082 requested_index --;
5085 if (index >= lpwhr->nCustHeaders)
5086 index = -1;
5088 TRACE("Return: %d\n", index);
5089 return index;
5093 /***********************************************************************
5094 * HTTP_InsertCustomHeader (internal)
5096 * Insert header into array
5099 static DWORD HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
5101 INT count;
5102 LPHTTPHEADERW lph = NULL;
5104 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5105 count = lpwhr->nCustHeaders + 1;
5106 if (count > 1)
5107 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
5108 else
5109 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
5111 if (!lph)
5112 return ERROR_OUTOFMEMORY;
5114 lpwhr->pCustHeaders = lph;
5115 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
5116 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
5117 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
5118 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
5119 lpwhr->nCustHeaders++;
5121 return ERROR_SUCCESS;
5125 /***********************************************************************
5126 * HTTP_DeleteCustomHeader (internal)
5128 * Delete header from array
5129 * If this function is called, the indexs may change.
5131 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
5133 if( lpwhr->nCustHeaders <= 0 )
5134 return FALSE;
5135 if( index >= lpwhr->nCustHeaders )
5136 return FALSE;
5137 lpwhr->nCustHeaders--;
5139 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
5140 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
5142 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
5143 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
5144 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
5146 return TRUE;
5150 /***********************************************************************
5151 * HTTP_VerifyValidHeader (internal)
5153 * Verify the given header is not invalid for the given http request
5156 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
5158 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5159 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
5160 return ERROR_HTTP_INVALID_HEADER;
5162 return ERROR_SUCCESS;
5165 /***********************************************************************
5166 * IsHostInProxyBypassList (@)
5168 * Undocumented
5171 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
5173 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
5174 return FALSE;