wpp: Add explicit casts when mixing signed and unsigned integers.
[wine/wine-gecko.git] / dlls / wininet / http.c
blob0b723c3f6df138e53ebd05102d1b3eac5d561d14
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(), HEAP_ZERO_MEMORY, sizeof(gzip_stream_t));
261 gzip_stream->zstream.zalloc = wininet_zalloc;
262 gzip_stream->zstream.zfree = wininet_zfree;
264 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
265 if(zres != Z_OK) {
266 ERR("inflateInit failed: %d\n", zres);
267 HeapFree(GetProcessHeap(), 0, gzip_stream);
268 return;
271 req->gzip_stream = gzip_stream;
273 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
274 if(index != -1)
275 HTTP_DeleteCustomHeader(req, index);
278 #else
280 static void init_gzip_stream(http_request_t *req)
282 ERR("gzip stream not supported, missing zlib.\n");
285 #endif
287 /* set the request content length based on the headers */
288 static DWORD set_content_length( http_request_t *lpwhr )
290 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
291 WCHAR encoding[20];
292 DWORD size;
294 size = sizeof(lpwhr->dwContentLength);
295 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
296 &lpwhr->dwContentLength, &size, NULL) != ERROR_SUCCESS)
297 lpwhr->dwContentLength = ~0u;
299 size = sizeof(encoding);
300 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
301 !strcmpiW(encoding, szChunked))
303 lpwhr->dwContentLength = ~0u;
304 lpwhr->read_chunked = TRUE;
307 if(lpwhr->decoding) {
308 int encoding_idx;
310 static const WCHAR gzipW[] = {'g','z','i','p',0};
312 encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
313 if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
314 init_gzip_stream(lpwhr);
317 return lpwhr->dwContentLength;
320 /***********************************************************************
321 * HTTP_Tokenize (internal)
323 * Tokenize a string, allocating memory for the tokens.
325 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
327 LPWSTR * token_array;
328 int tokens = 0;
329 int i;
330 LPCWSTR next_token;
332 if (string)
334 /* empty string has no tokens */
335 if (*string)
336 tokens++;
337 /* count tokens */
338 for (i = 0; string[i]; i++)
340 if (!strncmpW(string+i, token_string, strlenW(token_string)))
342 DWORD j;
343 tokens++;
344 /* we want to skip over separators, but not the null terminator */
345 for (j = 0; j < strlenW(token_string) - 1; j++)
346 if (!string[i+j])
347 break;
348 i += j;
353 /* add 1 for terminating NULL */
354 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
355 token_array[tokens] = NULL;
356 if (!tokens)
357 return token_array;
358 for (i = 0; i < tokens; i++)
360 int len;
361 next_token = strstrW(string, token_string);
362 if (!next_token) next_token = string+strlenW(string);
363 len = next_token - string;
364 token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
365 memcpy(token_array[i], string, len*sizeof(WCHAR));
366 token_array[i][len] = '\0';
367 string = next_token+strlenW(token_string);
369 return token_array;
372 /***********************************************************************
373 * HTTP_FreeTokens (internal)
375 * Frees memory returned from HTTP_Tokenize.
377 static void HTTP_FreeTokens(LPWSTR * token_array)
379 int i;
380 for (i = 0; token_array[i]; i++)
381 HeapFree(GetProcessHeap(), 0, token_array[i]);
382 HeapFree(GetProcessHeap(), 0, token_array);
385 static void HTTP_FixURL(http_request_t *lpwhr)
387 static const WCHAR szSlash[] = { '/',0 };
388 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
390 /* If we don't have a path we set it to root */
391 if (NULL == lpwhr->lpszPath)
392 lpwhr->lpszPath = heap_strdupW(szSlash);
393 else /* remove \r and \n*/
395 int nLen = strlenW(lpwhr->lpszPath);
396 while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
398 nLen--;
399 lpwhr->lpszPath[nLen]='\0';
401 /* Replace '\' with '/' */
402 while (nLen>0) {
403 nLen--;
404 if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
408 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
409 lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
410 && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
412 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
413 (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
414 *fixurl = '/';
415 strcpyW(fixurl + 1, lpwhr->lpszPath);
416 HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
417 lpwhr->lpszPath = fixurl;
421 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
423 LPWSTR requestString;
424 DWORD len, n;
425 LPCWSTR *req;
426 UINT i;
427 LPWSTR p;
429 static const WCHAR szSpace[] = { ' ',0 };
430 static const WCHAR szColon[] = { ':',' ',0 };
431 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
433 /* allocate space for an array of all the string pointers to be added */
434 len = (lpwhr->nCustHeaders)*4 + 10;
435 req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
437 /* add the verb, path and HTTP version string */
438 n = 0;
439 req[n++] = verb;
440 req[n++] = szSpace;
441 req[n++] = path;
442 req[n++] = szSpace;
443 req[n++] = version;
445 /* Append custom request headers */
446 for (i = 0; i < lpwhr->nCustHeaders; i++)
448 if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
450 req[n++] = szCrLf;
451 req[n++] = lpwhr->pCustHeaders[i].lpszField;
452 req[n++] = szColon;
453 req[n++] = lpwhr->pCustHeaders[i].lpszValue;
455 TRACE("Adding custom header %s (%s)\n",
456 debugstr_w(lpwhr->pCustHeaders[i].lpszField),
457 debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
461 if( n >= len )
462 ERR("oops. buffer overrun\n");
464 req[n] = NULL;
465 requestString = HTTP_build_req( req, 4 );
466 HeapFree( GetProcessHeap(), 0, req );
469 * Set (header) termination string for request
470 * Make sure there's exactly two new lines at the end of the request
472 p = &requestString[strlenW(requestString)-1];
473 while ( (*p == '\n') || (*p == '\r') )
474 p--;
475 strcpyW( p+1, sztwocrlf );
477 return requestString;
480 static void HTTP_ProcessCookies( http_request_t *lpwhr )
482 int HeaderIndex;
483 int numCookies = 0;
484 LPHTTPHEADERW setCookieHeader;
486 while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
488 setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
490 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
492 int len;
493 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
494 LPWSTR buf_url;
495 LPHTTPHEADERW Host;
497 Host = HTTP_GetHeader(lpwhr, hostW);
498 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
499 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
500 sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
501 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
503 HeapFree(GetProcessHeap(), 0, buf_url);
505 numCookies++;
509 static void strip_spaces(LPWSTR start)
511 LPWSTR str = start;
512 LPWSTR end;
514 while (*str == ' ' && *str != '\0')
515 str++;
517 if (str != start)
518 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
520 end = start + strlenW(start) - 1;
521 while (end >= start && *end == ' ')
523 *end = '\0';
524 end--;
528 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
530 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
531 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
532 BOOL is_basic;
533 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
534 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
535 if (is_basic && pszRealm)
537 LPCWSTR token;
538 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
539 LPCWSTR realm;
540 ptr++;
541 *pszRealm=NULL;
542 token = strchrW(ptr,'=');
543 if (!token)
544 return TRUE;
545 realm = ptr;
546 while (*realm == ' ' && *realm != '\0')
547 realm++;
548 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
549 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
551 token++;
552 while (*token == ' ' && *token != '\0')
553 token++;
554 if (*token == '\0')
555 return TRUE;
556 *pszRealm = heap_strdupW(token);
557 strip_spaces(*pszRealm);
561 return is_basic;
564 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
566 if (!authinfo) return;
568 if (SecIsValidHandle(&authinfo->ctx))
569 DeleteSecurityContext(&authinfo->ctx);
570 if (SecIsValidHandle(&authinfo->cred))
571 FreeCredentialsHandle(&authinfo->cred);
573 HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
574 HeapFree(GetProcessHeap(), 0, authinfo->scheme);
575 HeapFree(GetProcessHeap(), 0, authinfo);
578 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
580 basicAuthorizationData *ad;
581 UINT rc = 0;
583 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
585 EnterCriticalSection(&authcache_cs);
586 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
588 if (!strcmpiW(host,ad->lpszwHost) && !strcmpW(realm,ad->lpszwRealm))
590 TRACE("Authorization found in cache\n");
591 *auth_data = HeapAlloc(GetProcessHeap(),0,ad->AuthorizationLen);
592 memcpy(*auth_data,ad->lpszAuthorization,ad->AuthorizationLen);
593 rc = ad->AuthorizationLen;
594 break;
597 LeaveCriticalSection(&authcache_cs);
598 return rc;
601 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
603 struct list *cursor;
604 basicAuthorizationData* ad = NULL;
606 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
608 EnterCriticalSection(&authcache_cs);
609 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
611 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
612 if (!strcmpiW(host,check->lpszwHost) && !strcmpW(realm,check->lpszwRealm))
614 ad = check;
615 break;
619 if (ad)
621 TRACE("Found match in cache, replacing\n");
622 HeapFree(GetProcessHeap(),0,ad->lpszAuthorization);
623 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
624 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
625 ad->AuthorizationLen = auth_data_len;
627 else
629 ad = HeapAlloc(GetProcessHeap(),0,sizeof(basicAuthorizationData));
630 ad->lpszwHost = heap_strdupW(host);
631 ad->lpszwRealm = heap_strdupW(realm);
632 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
633 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
634 ad->AuthorizationLen = auth_data_len;
635 list_add_head(&basicAuthorizationCache,&ad->entry);
636 TRACE("authorization cached\n");
638 LeaveCriticalSection(&authcache_cs);
641 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
642 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
644 authorizationData *ad;
646 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
648 EnterCriticalSection(&authcache_cs);
649 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
650 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
651 TRACE("Authorization found in cache\n");
653 nt_auth_identity->User = heap_strdupW(ad->user);
654 nt_auth_identity->Password = heap_strdupW(ad->password);
655 nt_auth_identity->Domain = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*ad->domain_len);
656 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
657 (!nt_auth_identity->Domain && ad->domain_len)) {
658 HeapFree(GetProcessHeap(), 0, nt_auth_identity->User);
659 HeapFree(GetProcessHeap(), 0, nt_auth_identity->Password);
660 HeapFree(GetProcessHeap(), 0, nt_auth_identity->Domain);
661 break;
664 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
665 nt_auth_identity->UserLength = ad->user_len;
666 nt_auth_identity->PasswordLength = ad->password_len;
667 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
668 nt_auth_identity->DomainLength = ad->domain_len;
669 LeaveCriticalSection(&authcache_cs);
670 return TRUE;
673 LeaveCriticalSection(&authcache_cs);
675 return FALSE;
678 static void cache_authorization(LPWSTR host, LPWSTR scheme,
679 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
681 authorizationData *ad;
682 BOOL found = FALSE;
684 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
686 EnterCriticalSection(&authcache_cs);
687 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
688 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
689 found = TRUE;
690 break;
693 if(found) {
694 HeapFree(GetProcessHeap(), 0, ad->user);
695 HeapFree(GetProcessHeap(), 0, ad->password);
696 HeapFree(GetProcessHeap(), 0, ad->domain);
697 } else {
698 ad = HeapAlloc(GetProcessHeap(), 0, sizeof(authorizationData));
699 if(!ad) {
700 LeaveCriticalSection(&authcache_cs);
701 return;
704 ad->host = heap_strdupW(host);
705 ad->scheme = heap_strdupW(scheme);
706 list_add_head(&authorizationCache, &ad->entry);
709 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
710 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
711 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
712 ad->user_len = nt_auth_identity->UserLength;
713 ad->password_len = nt_auth_identity->PasswordLength;
714 ad->domain_len = nt_auth_identity->DomainLength;
716 if(!ad->host || !ad->scheme || !ad->user || !ad->password
717 || (nt_auth_identity->Domain && !ad->domain)) {
718 HeapFree(GetProcessHeap(), 0, ad->host);
719 HeapFree(GetProcessHeap(), 0, ad->scheme);
720 HeapFree(GetProcessHeap(), 0, ad->user);
721 HeapFree(GetProcessHeap(), 0, ad->password);
722 HeapFree(GetProcessHeap(), 0, ad->domain);
723 list_remove(&ad->entry);
724 HeapFree(GetProcessHeap(), 0, ad);
727 LeaveCriticalSection(&authcache_cs);
730 static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
731 struct HttpAuthInfo **ppAuthInfo,
732 LPWSTR domain_and_username, LPWSTR password,
733 LPWSTR host )
735 SECURITY_STATUS sec_status;
736 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
737 BOOL first = FALSE;
738 LPWSTR szRealm = NULL;
740 TRACE("%s\n", debugstr_w(pszAuthValue));
742 if (!pAuthInfo)
744 TimeStamp exp;
746 first = TRUE;
747 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
748 if (!pAuthInfo)
749 return FALSE;
751 SecInvalidateHandle(&pAuthInfo->cred);
752 SecInvalidateHandle(&pAuthInfo->ctx);
753 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
754 pAuthInfo->attr = 0;
755 pAuthInfo->auth_data = NULL;
756 pAuthInfo->auth_data_len = 0;
757 pAuthInfo->finished = FALSE;
759 if (is_basic_auth_value(pszAuthValue,NULL))
761 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
762 pAuthInfo->scheme = heap_strdupW(szBasic);
763 if (!pAuthInfo->scheme)
765 HeapFree(GetProcessHeap(), 0, pAuthInfo);
766 return FALSE;
769 else
771 PVOID pAuthData;
772 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
774 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
775 if (!pAuthInfo->scheme)
777 HeapFree(GetProcessHeap(), 0, pAuthInfo);
778 return FALSE;
781 if (domain_and_username)
783 WCHAR *user = strchrW(domain_and_username, '\\');
784 WCHAR *domain = domain_and_username;
786 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
788 pAuthData = &nt_auth_identity;
790 if (user) user++;
791 else
793 user = domain_and_username;
794 domain = NULL;
797 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
798 nt_auth_identity.User = user;
799 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
800 nt_auth_identity.Domain = domain;
801 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
802 nt_auth_identity.Password = password;
803 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
805 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
807 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
808 pAuthData = &nt_auth_identity;
809 else
810 /* use default credentials */
811 pAuthData = NULL;
813 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
814 SECPKG_CRED_OUTBOUND, NULL,
815 pAuthData, NULL,
816 NULL, &pAuthInfo->cred,
817 &exp);
819 if(pAuthData && !domain_and_username) {
820 HeapFree(GetProcessHeap(), 0, nt_auth_identity.User);
821 HeapFree(GetProcessHeap(), 0, nt_auth_identity.Domain);
822 HeapFree(GetProcessHeap(), 0, nt_auth_identity.Password);
825 if (sec_status == SEC_E_OK)
827 PSecPkgInfoW sec_pkg_info;
828 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
829 if (sec_status == SEC_E_OK)
831 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
832 FreeContextBuffer(sec_pkg_info);
835 if (sec_status != SEC_E_OK)
837 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
838 debugstr_w(pAuthInfo->scheme), sec_status);
839 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
840 HeapFree(GetProcessHeap(), 0, pAuthInfo);
841 return FALSE;
844 *ppAuthInfo = pAuthInfo;
846 else if (pAuthInfo->finished)
847 return FALSE;
849 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
850 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
852 ERR("authentication scheme changed from %s to %s\n",
853 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
854 return FALSE;
857 if (is_basic_auth_value(pszAuthValue,&szRealm))
859 int userlen;
860 int passlen;
861 char *auth_data = NULL;
862 UINT auth_data_len = 0;
864 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
866 if (!domain_and_username)
868 if (host && szRealm)
869 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
870 if (auth_data_len == 0)
872 HeapFree(GetProcessHeap(),0,szRealm);
873 return FALSE;
876 else
878 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
879 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
881 /* length includes a nul terminator, which will be re-used for the ':' */
882 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
883 if (!auth_data)
885 HeapFree(GetProcessHeap(),0,szRealm);
886 return FALSE;
889 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
890 auth_data[userlen] = ':';
891 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
892 auth_data_len = userlen + 1 + passlen;
893 if (host && szRealm)
894 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
897 pAuthInfo->auth_data = auth_data;
898 pAuthInfo->auth_data_len = auth_data_len;
899 pAuthInfo->finished = TRUE;
900 HeapFree(GetProcessHeap(),0,szRealm);
902 return TRUE;
904 else
906 LPCWSTR pszAuthData;
907 SecBufferDesc out_desc, in_desc;
908 SecBuffer out, in;
909 unsigned char *buffer;
910 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
911 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
913 in.BufferType = SECBUFFER_TOKEN;
914 in.cbBuffer = 0;
915 in.pvBuffer = NULL;
917 in_desc.ulVersion = 0;
918 in_desc.cBuffers = 1;
919 in_desc.pBuffers = &in;
921 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
922 if (*pszAuthData == ' ')
924 pszAuthData++;
925 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
926 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
927 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
930 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
932 out.BufferType = SECBUFFER_TOKEN;
933 out.cbBuffer = pAuthInfo->max_token;
934 out.pvBuffer = buffer;
936 out_desc.ulVersion = 0;
937 out_desc.cBuffers = 1;
938 out_desc.pBuffers = &out;
940 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
941 first ? NULL : &pAuthInfo->ctx,
942 first ? lpwhr->lpHttpSession->lpszServerName : NULL,
943 context_req, 0, SECURITY_NETWORK_DREP,
944 in.pvBuffer ? &in_desc : NULL,
945 0, &pAuthInfo->ctx, &out_desc,
946 &pAuthInfo->attr, &pAuthInfo->exp);
947 if (sec_status == SEC_E_OK)
949 pAuthInfo->finished = TRUE;
950 pAuthInfo->auth_data = out.pvBuffer;
951 pAuthInfo->auth_data_len = out.cbBuffer;
952 TRACE("sending last auth packet\n");
954 else if (sec_status == SEC_I_CONTINUE_NEEDED)
956 pAuthInfo->auth_data = out.pvBuffer;
957 pAuthInfo->auth_data_len = out.cbBuffer;
958 TRACE("sending next auth packet\n");
960 else
962 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
963 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
964 destroy_authinfo(pAuthInfo);
965 *ppAuthInfo = NULL;
966 return FALSE;
970 return TRUE;
973 /***********************************************************************
974 * HTTP_HttpAddRequestHeadersW (internal)
976 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
977 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
979 LPWSTR lpszStart;
980 LPWSTR lpszEnd;
981 LPWSTR buffer;
982 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
984 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
986 if( dwHeaderLength == ~0U )
987 len = strlenW(lpszHeader);
988 else
989 len = dwHeaderLength;
990 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
991 lstrcpynW( buffer, lpszHeader, len + 1);
993 lpszStart = buffer;
997 LPWSTR * pFieldAndValue;
999 lpszEnd = lpszStart;
1001 while (*lpszEnd != '\0')
1003 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1004 break;
1005 lpszEnd++;
1008 if (*lpszStart == '\0')
1009 break;
1011 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1013 *lpszEnd = '\0';
1014 lpszEnd++; /* Jump over newline */
1016 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1017 if (*lpszStart == '\0')
1019 /* Skip 0-length headers */
1020 lpszStart = lpszEnd;
1021 res = ERROR_SUCCESS;
1022 continue;
1024 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1025 if (pFieldAndValue)
1027 res = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
1028 if (res == ERROR_SUCCESS)
1029 res = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
1030 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1031 HTTP_FreeTokens(pFieldAndValue);
1034 lpszStart = lpszEnd;
1035 } while (res == ERROR_SUCCESS);
1037 HeapFree(GetProcessHeap(), 0, buffer);
1039 return res;
1042 /***********************************************************************
1043 * HttpAddRequestHeadersW (WININET.@)
1045 * Adds one or more HTTP header to the request handler
1047 * NOTE
1048 * On Windows if dwHeaderLength includes the trailing '\0', then
1049 * HttpAddRequestHeadersW() adds it too. However this results in an
1050 * invalid Http header which is rejected by some servers so we probably
1051 * don't need to match Windows on that point.
1053 * RETURNS
1054 * TRUE on success
1055 * FALSE on failure
1058 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1059 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1061 http_request_t *lpwhr;
1062 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1064 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1066 if (!lpszHeader)
1067 return TRUE;
1069 lpwhr = (http_request_t*) get_handle_object( hHttpRequest );
1070 if (lpwhr && lpwhr->hdr.htype == WH_HHTTPREQ)
1071 res = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
1072 if( lpwhr )
1073 WININET_Release( &lpwhr->hdr );
1075 if(res != ERROR_SUCCESS)
1076 SetLastError(res);
1077 return res == ERROR_SUCCESS;
1080 /***********************************************************************
1081 * HttpAddRequestHeadersA (WININET.@)
1083 * Adds one or more HTTP header to the request handler
1085 * RETURNS
1086 * TRUE on success
1087 * FALSE on failure
1090 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1091 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1093 DWORD len;
1094 LPWSTR hdr;
1095 BOOL r;
1097 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1099 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1100 hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
1101 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1102 if( dwHeaderLength != ~0U )
1103 dwHeaderLength = len;
1105 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1107 HeapFree( GetProcessHeap(), 0, hdr );
1109 return r;
1112 /***********************************************************************
1113 * HttpOpenRequestA (WININET.@)
1115 * Open a HTTP request handle
1117 * RETURNS
1118 * HINTERNET a HTTP request handle on success
1119 * NULL on failure
1122 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1123 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1124 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1125 DWORD dwFlags, DWORD_PTR dwContext)
1127 LPWSTR szVerb = NULL, szObjectName = NULL;
1128 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1129 INT acceptTypesCount;
1130 HINTERNET rc = FALSE;
1131 LPCSTR *types;
1133 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1134 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1135 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1136 dwFlags, dwContext);
1138 if (lpszVerb)
1140 szVerb = heap_strdupAtoW(lpszVerb);
1141 if ( !szVerb )
1142 goto end;
1145 if (lpszObjectName)
1147 szObjectName = heap_strdupAtoW(lpszObjectName);
1148 if ( !szObjectName )
1149 goto end;
1152 if (lpszVersion)
1154 szVersion = heap_strdupAtoW(lpszVersion);
1155 if ( !szVersion )
1156 goto end;
1159 if (lpszReferrer)
1161 szReferrer = heap_strdupAtoW(lpszReferrer);
1162 if ( !szReferrer )
1163 goto end;
1166 if (lpszAcceptTypes)
1168 acceptTypesCount = 0;
1169 types = lpszAcceptTypes;
1170 while (*types)
1172 __TRY
1174 /* find out how many there are */
1175 if (*types && **types)
1177 TRACE("accept type: %s\n", debugstr_a(*types));
1178 acceptTypesCount++;
1181 __EXCEPT_PAGE_FAULT
1183 WARN("invalid accept type pointer\n");
1185 __ENDTRY;
1186 types++;
1188 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1189 if (!szAcceptTypes) goto end;
1191 acceptTypesCount = 0;
1192 types = lpszAcceptTypes;
1193 while (*types)
1195 __TRY
1197 if (*types && **types)
1198 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1200 __EXCEPT_PAGE_FAULT
1202 /* ignore invalid pointer */
1204 __ENDTRY;
1205 types++;
1207 szAcceptTypes[acceptTypesCount] = NULL;
1210 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1211 szVersion, szReferrer,
1212 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1214 end:
1215 if (szAcceptTypes)
1217 acceptTypesCount = 0;
1218 while (szAcceptTypes[acceptTypesCount])
1220 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1221 acceptTypesCount++;
1223 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1225 HeapFree(GetProcessHeap(), 0, szReferrer);
1226 HeapFree(GetProcessHeap(), 0, szVersion);
1227 HeapFree(GetProcessHeap(), 0, szObjectName);
1228 HeapFree(GetProcessHeap(), 0, szVerb);
1230 return rc;
1233 /***********************************************************************
1234 * HTTP_EncodeBase64
1236 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1238 UINT n = 0, x;
1239 static const CHAR HTTP_Base64Enc[] =
1240 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1242 while( len > 0 )
1244 /* first 6 bits, all from bin[0] */
1245 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1246 x = (bin[0] & 3) << 4;
1248 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1249 if( len == 1 )
1251 base64[n++] = HTTP_Base64Enc[x];
1252 base64[n++] = '=';
1253 base64[n++] = '=';
1254 break;
1256 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1257 x = ( bin[1] & 0x0f ) << 2;
1259 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1260 if( len == 2 )
1262 base64[n++] = HTTP_Base64Enc[x];
1263 base64[n++] = '=';
1264 break;
1266 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1268 /* last 6 bits, all from bin [2] */
1269 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1270 bin += 3;
1271 len -= 3;
1273 base64[n] = 0;
1274 return n;
1277 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1278 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1279 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1280 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1281 static const signed char HTTP_Base64Dec[256] =
1283 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1284 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1285 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1286 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1287 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1288 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1289 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1290 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1291 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1292 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1293 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1294 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1295 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1296 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1297 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1298 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1299 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1300 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1301 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1302 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1303 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1304 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1305 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1306 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1307 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1308 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1310 #undef CH
1312 /***********************************************************************
1313 * HTTP_DecodeBase64
1315 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1317 unsigned int n = 0;
1319 while(*base64)
1321 signed char in[4];
1323 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1324 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1325 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1326 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1328 WARN("invalid base64: %s\n", debugstr_w(base64));
1329 return 0;
1331 if (bin)
1332 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1333 n++;
1335 if ((base64[2] == '=') && (base64[3] == '='))
1336 break;
1337 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1338 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1340 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1341 return 0;
1343 if (bin)
1344 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1345 n++;
1347 if (base64[3] == '=')
1348 break;
1349 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1350 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1352 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1353 return 0;
1355 if (bin)
1356 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1357 n++;
1359 base64 += 4;
1362 return n;
1365 /***********************************************************************
1366 * HTTP_InsertAuthorization
1368 * Insert or delete the authorization field in the request header.
1370 static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1372 if (pAuthInfo)
1374 static const WCHAR wszSpace[] = {' ',0};
1375 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1376 unsigned int len;
1377 WCHAR *authorization = NULL;
1379 if (pAuthInfo->auth_data_len)
1381 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1382 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1383 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1384 if (!authorization)
1385 return FALSE;
1387 strcpyW(authorization, pAuthInfo->scheme);
1388 strcatW(authorization, wszSpace);
1389 HTTP_EncodeBase64(pAuthInfo->auth_data,
1390 pAuthInfo->auth_data_len,
1391 authorization+strlenW(authorization));
1393 /* clear the data as it isn't valid now that it has been sent to the
1394 * server, unless it's Basic authentication which doesn't do
1395 * connection tracking */
1396 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1398 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1399 pAuthInfo->auth_data = NULL;
1400 pAuthInfo->auth_data_len = 0;
1404 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1406 HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1408 HeapFree(GetProcessHeap(), 0, authorization);
1410 return TRUE;
1413 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1415 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1416 DWORD size;
1418 size = sizeof(new_location);
1419 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1421 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1422 strcpyW( url, new_location );
1424 else
1426 static const WCHAR slash[] = { '/',0 };
1427 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1428 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1429 http_session_t *session = req->lpHttpSession;
1431 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1432 size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
1434 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1436 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1437 sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
1438 else
1439 sprintfW( url, format, session->lpszHostName, session->nHostPort );
1440 if (req->lpszPath[0] != '/') strcatW( url, slash );
1441 strcatW( url, req->lpszPath );
1443 TRACE("url=%s\n", debugstr_w(url));
1444 return url;
1447 /***********************************************************************
1448 * HTTP_DealWithProxy
1450 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
1452 WCHAR buf[MAXHOSTNAME];
1453 WCHAR protoProxy[MAXHOSTNAME + 15];
1454 DWORD protoProxyLen = sizeof(protoProxy) / sizeof(protoProxy[0]);
1455 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1456 static WCHAR szNul[] = { 0 };
1457 URL_COMPONENTSW UrlComponents;
1458 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1459 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1460 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1462 memset( &UrlComponents, 0, sizeof UrlComponents );
1463 UrlComponents.dwStructSize = sizeof UrlComponents;
1464 UrlComponents.lpszHostName = buf;
1465 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1467 if (!INTERNET_FindProxyForProtocol(hIC->lpszProxy, protoHttp, protoProxy, &protoProxyLen))
1468 return FALSE;
1469 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1470 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1471 sprintfW(proxy, szFormat, protoProxy);
1472 else
1473 strcpyW(proxy, protoProxy);
1474 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1475 return FALSE;
1476 if( UrlComponents.dwHostNameLength == 0 )
1477 return FALSE;
1479 if( !lpwhr->lpszPath )
1480 lpwhr->lpszPath = szNul;
1482 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1483 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1485 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1486 lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1487 lpwhs->nServerPort = UrlComponents.nPort;
1489 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1490 return TRUE;
1493 #ifndef INET6_ADDRSTRLEN
1494 #define INET6_ADDRSTRLEN 46
1495 #endif
1497 static DWORD HTTP_ResolveName(http_request_t *lpwhr)
1499 char szaddr[INET6_ADDRSTRLEN];
1500 http_session_t *lpwhs = lpwhr->lpHttpSession;
1501 const void *addr;
1503 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1504 INTERNET_STATUS_RESOLVING_NAME,
1505 lpwhs->lpszServerName,
1506 (strlenW(lpwhs->lpszServerName)+1) * sizeof(WCHAR));
1508 lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1509 if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1510 (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1511 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1513 switch (lpwhs->socketAddress.ss_family)
1515 case AF_INET:
1516 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1517 break;
1518 case AF_INET6:
1519 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1520 break;
1521 default:
1522 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1523 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1525 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1526 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1527 INTERNET_STATUS_NAME_RESOLVED,
1528 szaddr, strlen(szaddr)+1);
1530 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1531 return ERROR_SUCCESS;
1534 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1536 LPHTTPHEADERW host_header;
1538 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1540 host_header = HTTP_GetHeader(req, hostW);
1541 if(!host_header)
1542 return FALSE;
1544 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1545 return TRUE;
1549 /***********************************************************************
1550 * HTTPREQ_Destroy (internal)
1552 * Deallocate request handle
1555 static void HTTPREQ_Destroy(object_header_t *hdr)
1557 http_request_t *lpwhr = (http_request_t*) hdr;
1558 DWORD i;
1560 TRACE("\n");
1562 if(lpwhr->hCacheFile) {
1563 WCHAR url[INTERNET_MAX_URL_LENGTH];
1564 FILETIME ft;
1566 CloseHandle(lpwhr->hCacheFile);
1568 memset(&ft, 0, sizeof(FILETIME));
1569 if(HTTP_GetRequestURL(lpwhr, url)) {
1570 CommitUrlCacheEntryW(url, lpwhr->lpszCacheFile, ft, ft,
1571 NORMAL_CACHE_ENTRY, NULL, 0, NULL, 0);
1575 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1577 DeleteCriticalSection( &lpwhr->read_section );
1578 WININET_Release(&lpwhr->lpHttpSession->hdr);
1580 destroy_authinfo(lpwhr->pAuthInfo);
1581 destroy_authinfo(lpwhr->pProxyAuthInfo);
1583 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1584 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1585 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1586 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1587 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1589 for (i = 0; i < lpwhr->nCustHeaders; i++)
1591 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1592 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1595 #ifdef HAVE_ZLIB
1596 if(lpwhr->gzip_stream) {
1597 if(!lpwhr->gzip_stream->end_of_data)
1598 inflateEnd(&lpwhr->gzip_stream->zstream);
1599 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1601 #endif
1603 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1606 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1608 http_request_t *lpwhr = (http_request_t*) hdr;
1610 TRACE("%p\n",lpwhr);
1612 if (!NETCON_connected(&lpwhr->netConnection))
1613 return;
1615 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1616 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1618 NETCON_close(&lpwhr->netConnection);
1620 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1621 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1624 static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
1626 WCHAR szVersion[10];
1627 WCHAR szConnectionResponse[20];
1628 DWORD dwBufferSize = sizeof(szVersion);
1629 BOOL keepalive = FALSE;
1631 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1632 * the connection is keep-alive by default */
1633 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1634 && !strcmpiW(szVersion, g_szHttp1_1))
1636 keepalive = TRUE;
1639 dwBufferSize = sizeof(szConnectionResponse);
1640 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1641 || HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1643 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1646 return keepalive;
1649 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1651 http_request_t *req = (http_request_t*)hdr;
1653 switch(option) {
1654 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1656 http_session_t *lpwhs = req->lpHttpSession;
1657 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1659 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1661 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1662 return ERROR_INSUFFICIENT_BUFFER;
1663 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1664 /* FIXME: can't get a SOCKET from our connection since we don't use
1665 * winsock
1667 info->Socket = 0;
1668 /* FIXME: get source port from req->netConnection */
1669 info->SourcePort = 0;
1670 info->DestPort = lpwhs->nHostPort;
1671 info->Flags = 0;
1672 if (HTTP_KeepAlive(req))
1673 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1674 if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
1675 info->Flags |= IDSI_FLAG_PROXY;
1676 if (req->netConnection.useSSL)
1677 info->Flags |= IDSI_FLAG_SECURE;
1679 return ERROR_SUCCESS;
1682 case INTERNET_OPTION_SECURITY_FLAGS:
1684 DWORD flags;
1685 int bits;
1687 if (*size < sizeof(ULONG))
1688 return ERROR_INSUFFICIENT_BUFFER;
1690 *size = sizeof(DWORD);
1691 flags = 0;
1692 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1693 flags |= SECURITY_FLAG_SECURE;
1694 flags |= req->netConnection.security_flags;
1695 bits = NETCON_GetCipherStrength(&req->netConnection);
1696 if (bits >= 128)
1697 flags |= SECURITY_FLAG_STRENGTH_STRONG;
1698 else if (bits >= 56)
1699 flags |= SECURITY_FLAG_STRENGTH_MEDIUM;
1700 else
1701 flags |= SECURITY_FLAG_STRENGTH_WEAK;
1702 *(DWORD *)buffer = flags;
1703 return ERROR_SUCCESS;
1706 case INTERNET_OPTION_HANDLE_TYPE:
1707 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1709 if (*size < sizeof(ULONG))
1710 return ERROR_INSUFFICIENT_BUFFER;
1712 *size = sizeof(DWORD);
1713 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1714 return ERROR_SUCCESS;
1716 case INTERNET_OPTION_URL: {
1717 WCHAR url[INTERNET_MAX_URL_LENGTH];
1718 HTTPHEADERW *host;
1719 DWORD len;
1720 WCHAR *pch;
1722 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1724 TRACE("INTERNET_OPTION_URL\n");
1726 host = HTTP_GetHeader(req, hostW);
1727 strcpyW(url, httpW);
1728 strcatW(url, host->lpszValue);
1729 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1730 *pch = 0;
1731 strcatW(url, req->lpszPath);
1733 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1735 if(unicode) {
1736 len = (strlenW(url)+1) * sizeof(WCHAR);
1737 if(*size < len)
1738 return ERROR_INSUFFICIENT_BUFFER;
1740 *size = len;
1741 strcpyW(buffer, url);
1742 return ERROR_SUCCESS;
1743 }else {
1744 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1745 if(len > *size)
1746 return ERROR_INSUFFICIENT_BUFFER;
1748 *size = len;
1749 return ERROR_SUCCESS;
1753 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1754 INTERNET_CACHE_ENTRY_INFOW *info;
1755 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1756 WCHAR url[INTERNET_MAX_URL_LENGTH];
1757 DWORD nbytes, error;
1758 BOOL ret;
1760 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1762 if (*size < sizeof(*ts))
1764 *size = sizeof(*ts);
1765 return ERROR_INSUFFICIENT_BUFFER;
1767 nbytes = 0;
1768 HTTP_GetRequestURL(req, url);
1769 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1770 error = GetLastError();
1771 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1773 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1774 return ERROR_OUTOFMEMORY;
1776 GetUrlCacheEntryInfoW(url, info, &nbytes);
1778 ts->ftExpires = info->ExpireTime;
1779 ts->ftLastModified = info->LastModifiedTime;
1781 HeapFree(GetProcessHeap(), 0, info);
1782 *size = sizeof(*ts);
1783 return ERROR_SUCCESS;
1785 return error;
1788 case INTERNET_OPTION_DATAFILE_NAME: {
1789 DWORD req_size;
1791 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1793 if(!req->lpszCacheFile) {
1794 *size = 0;
1795 return ERROR_INTERNET_ITEM_NOT_FOUND;
1798 if(unicode) {
1799 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1800 if(*size < req_size)
1801 return ERROR_INSUFFICIENT_BUFFER;
1803 *size = req_size;
1804 memcpy(buffer, req->lpszCacheFile, *size);
1805 return ERROR_SUCCESS;
1806 }else {
1807 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1808 if (req_size > *size)
1809 return ERROR_INSUFFICIENT_BUFFER;
1811 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1812 -1, buffer, *size, NULL, NULL);
1813 return ERROR_SUCCESS;
1817 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1818 PCCERT_CONTEXT context;
1820 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
1821 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
1822 return ERROR_INSUFFICIENT_BUFFER;
1825 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1826 if(context) {
1827 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
1828 DWORD len;
1830 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1831 info->ftExpiry = context->pCertInfo->NotAfter;
1832 info->ftStart = context->pCertInfo->NotBefore;
1833 len = CertNameToStrA(context->dwCertEncodingType,
1834 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1835 info->lpszSubjectInfo = LocalAlloc(0, len);
1836 if(info->lpszSubjectInfo)
1837 CertNameToStrA(context->dwCertEncodingType,
1838 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1839 info->lpszSubjectInfo, len);
1840 len = CertNameToStrA(context->dwCertEncodingType,
1841 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1842 info->lpszIssuerInfo = LocalAlloc(0, len);
1843 if(info->lpszIssuerInfo)
1844 CertNameToStrA(context->dwCertEncodingType,
1845 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1846 info->lpszIssuerInfo, len);
1847 info->dwKeySize = NETCON_GetCipherStrength(&req->netConnection);
1848 CertFreeCertificateContext(context);
1849 return ERROR_SUCCESS;
1854 return INET_QueryOption(hdr, option, buffer, size, unicode);
1857 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1859 http_request_t *req = (http_request_t*)hdr;
1861 switch(option) {
1862 case INTERNET_OPTION_SECURITY_FLAGS:
1864 DWORD flags;
1866 if (!buffer || size != sizeof(DWORD))
1867 return ERROR_INVALID_PARAMETER;
1868 flags = *(DWORD *)buffer;
1869 TRACE("%08x\n", flags);
1870 req->netConnection.security_flags = flags;
1871 return ERROR_SUCCESS;
1873 case INTERNET_OPTION_SEND_TIMEOUT:
1874 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1875 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1877 if (size != sizeof(DWORD))
1878 return ERROR_INVALID_PARAMETER;
1880 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1881 *(DWORD*)buffer);
1883 case INTERNET_OPTION_USERNAME:
1884 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1885 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1886 return ERROR_SUCCESS;
1888 case INTERNET_OPTION_PASSWORD:
1889 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1890 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1891 return ERROR_SUCCESS;
1892 case INTERNET_OPTION_HTTP_DECODING:
1893 if(size != sizeof(BOOL))
1894 return ERROR_INVALID_PARAMETER;
1895 req->decoding = *(BOOL*)buffer;
1896 return ERROR_SUCCESS;
1899 return ERROR_INTERNET_INVALID_OPTION;
1902 /* read some more data into the read buffer (the read section must be held) */
1903 static DWORD read_more_data( http_request_t *req, int maxlen )
1905 DWORD res;
1906 int len;
1908 if (req->read_pos)
1910 /* move existing data to the start of the buffer */
1911 if(req->read_size)
1912 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1913 req->read_pos = 0;
1916 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1918 res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1919 maxlen - req->read_size, 0, &len );
1920 if(res == ERROR_SUCCESS)
1921 req->read_size += len;
1923 return res;
1926 /* remove some amount of data from the read buffer (the read section must be held) */
1927 static void remove_data( http_request_t *req, int count )
1929 if (!(req->read_size -= count)) req->read_pos = 0;
1930 else req->read_pos += count;
1933 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1935 int count, bytes_read, pos = 0;
1936 DWORD res;
1938 EnterCriticalSection( &req->read_section );
1939 for (;;)
1941 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1943 if (eol)
1945 count = eol - (req->read_buf + req->read_pos);
1946 bytes_read = count + 1;
1948 else count = bytes_read = req->read_size;
1950 count = min( count, *len - pos );
1951 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1952 pos += count;
1953 remove_data( req, bytes_read );
1954 if (eol) break;
1956 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
1958 *len = 0;
1959 TRACE( "returning empty string\n" );
1960 LeaveCriticalSection( &req->read_section );
1961 INTERNET_SetLastError(res);
1962 return FALSE;
1965 LeaveCriticalSection( &req->read_section );
1967 if (pos < *len)
1969 if (pos && buffer[pos - 1] == '\r') pos--;
1970 *len = pos + 1;
1972 buffer[*len - 1] = 0;
1973 TRACE( "returning %s\n", debugstr_a(buffer));
1974 return TRUE;
1977 /* discard data contents until we reach end of line (the read section must be held) */
1978 static DWORD discard_eol( http_request_t *req )
1980 DWORD res;
1984 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1985 if (eol)
1987 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
1988 break;
1990 req->read_pos = req->read_size = 0; /* discard everything */
1991 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1992 } while (req->read_size);
1993 return ERROR_SUCCESS;
1996 /* read the size of the next chunk (the read section must be held) */
1997 static DWORD start_next_chunk( http_request_t *req )
1999 DWORD chunk_size = 0, res;
2001 if (!req->dwContentLength) return ERROR_SUCCESS;
2002 if (req->dwContentLength == req->dwContentRead)
2004 /* read terminator for the previous chunk */
2005 if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
2006 req->dwContentLength = ~0u;
2007 req->dwContentRead = 0;
2009 for (;;)
2011 while (req->read_size)
2013 char ch = req->read_buf[req->read_pos];
2014 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2015 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2016 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2017 else if (ch == ';' || ch == '\r' || ch == '\n')
2019 TRACE( "reading %u byte chunk\n", chunk_size );
2020 req->dwContentLength = chunk_size;
2021 req->dwContentRead = 0;
2022 return discard_eol( req );
2024 remove_data( req, 1 );
2026 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
2027 if (!req->read_size)
2029 req->dwContentLength = req->dwContentRead = 0;
2030 return ERROR_SUCCESS;
2035 /* check if we have reached the end of the data to read (the read section must be held) */
2036 static BOOL end_of_read_data( http_request_t *req )
2038 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2039 if (req->read_chunked) return (req->dwContentLength == 0);
2040 if (req->dwContentLength == ~0u) return FALSE;
2041 return (req->dwContentLength == req->dwContentRead);
2044 /* fetch some more data into the read buffer (the read section must be held) */
2045 static DWORD refill_buffer( http_request_t *req )
2047 int len = sizeof(req->read_buf);
2048 DWORD res;
2050 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2052 if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
2055 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
2056 if (len <= req->read_size) return ERROR_SUCCESS;
2058 if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
2059 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
2060 return ERROR_SUCCESS;
2063 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
2065 DWORD ret = ERROR_SUCCESS;
2066 int read = 0;
2068 #ifdef HAVE_ZLIB
2069 z_stream *zstream = &req->gzip_stream->zstream;
2070 DWORD buf_avail;
2071 int zres;
2073 while(read < size && !req->gzip_stream->end_of_data) {
2074 if(!req->read_size) {
2075 if(!sync || refill_buffer(req) != ERROR_SUCCESS)
2076 break;
2079 if(req->dwContentRead == req->dwContentLength)
2080 break;
2082 buf_avail = req->dwContentLength == ~0 ? req->read_size : min(req->read_size, req->dwContentLength-req->dwContentRead);
2084 zstream->next_in = req->read_buf+req->read_pos;
2085 zstream->avail_in = buf_avail;
2086 zstream->next_out = buf+read;
2087 zstream->avail_out = size-read;
2088 zres = inflate(zstream, Z_FULL_FLUSH);
2089 read = size - zstream->avail_out;
2090 req->dwContentRead += buf_avail-zstream->avail_in;
2091 remove_data(req, buf_avail-zstream->avail_in);
2092 if(zres == Z_STREAM_END) {
2093 TRACE("end of data\n");
2094 req->gzip_stream->end_of_data = TRUE;
2095 inflateEnd(&req->gzip_stream->zstream);
2096 }else if(zres != Z_OK) {
2097 WARN("inflate failed %d\n", zres);
2098 if(!read)
2099 ret = ERROR_INTERNET_DECODING_FAILED;
2100 break;
2103 #endif
2105 *read_ret = read;
2106 return ret;
2109 static void refill_gzip_buffer(http_request_t *req)
2111 DWORD res;
2112 int len;
2114 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2115 return;
2117 if(req->gzip_stream->buf_pos) {
2118 if(req->gzip_stream->buf_size)
2119 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2120 req->gzip_stream->buf_pos = 0;
2123 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2124 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2125 if(res == ERROR_SUCCESS)
2126 req->gzip_stream->buf_size += len;
2129 /* return the size of data available to be read immediately (the read section must be held) */
2130 static DWORD get_avail_data( http_request_t *req )
2132 if (req->gzip_stream) {
2133 refill_gzip_buffer(req);
2134 return req->gzip_stream->buf_size;
2136 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2137 return 0;
2138 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2141 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2143 INTERNET_ASYNC_RESULT iar;
2144 DWORD res;
2146 TRACE("%p\n", req);
2148 EnterCriticalSection( &req->read_section );
2149 if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
2150 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2151 iar.dwError = first_notif ? 0 : get_avail_data(req);
2152 }else {
2153 iar.dwResult = 0;
2154 iar.dwError = res;
2156 LeaveCriticalSection( &req->read_section );
2158 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2159 sizeof(INTERNET_ASYNC_RESULT));
2162 /* read data from the http connection (the read section must be held) */
2163 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2165 BOOL finished_reading = FALSE;
2166 int len, bytes_read = 0;
2167 DWORD ret = ERROR_SUCCESS;
2169 EnterCriticalSection( &req->read_section );
2171 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2173 if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
2176 if(req->gzip_stream) {
2177 if(req->gzip_stream->buf_size) {
2178 bytes_read = min(req->gzip_stream->buf_size, size);
2179 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2180 req->gzip_stream->buf_pos += bytes_read;
2181 req->gzip_stream->buf_size -= bytes_read;
2182 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2183 refill_buffer(req);
2186 if(size > bytes_read) {
2187 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2188 if(ret == ERROR_SUCCESS)
2189 bytes_read += len;
2192 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2193 }else {
2194 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2196 if (req->read_size) {
2197 bytes_read = min( req->read_size, size );
2198 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2199 remove_data( req, bytes_read );
2202 if (size > bytes_read && (!bytes_read || sync)) {
2203 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2204 sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2205 bytes_read += len;
2206 /* always return success, even if the network layer returns an error */
2209 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2210 req->dwContentRead += bytes_read;
2212 done:
2213 *read = bytes_read;
2215 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2216 LeaveCriticalSection( &req->read_section );
2218 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2219 BOOL res;
2220 DWORD dwBytesWritten;
2222 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2223 if(!res)
2224 WARN("WriteFile failed: %u\n", GetLastError());
2227 if(finished_reading)
2228 HTTP_FinishedReading(req);
2230 return ret;
2234 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2236 http_request_t *req = (http_request_t*)hdr;
2237 DWORD res;
2239 EnterCriticalSection( &req->read_section );
2240 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2241 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2243 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2244 if(res == ERROR_SUCCESS)
2245 res = hdr->dwError;
2246 LeaveCriticalSection( &req->read_section );
2248 return res;
2251 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2253 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2254 http_request_t *req = (http_request_t*)workRequest->hdr;
2255 INTERNET_ASYNC_RESULT iar;
2256 DWORD res;
2258 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2260 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2261 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2263 iar.dwResult = res == ERROR_SUCCESS;
2264 iar.dwError = res;
2266 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2267 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2268 sizeof(INTERNET_ASYNC_RESULT));
2271 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2272 DWORD flags, DWORD_PTR context)
2274 http_request_t *req = (http_request_t*)hdr;
2275 DWORD res, size, read, error = ERROR_SUCCESS;
2277 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2278 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2280 if (buffers->dwStructSize != sizeof(*buffers))
2281 return ERROR_INVALID_PARAMETER;
2283 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2285 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2287 WORKREQUEST workRequest;
2289 if (TryEnterCriticalSection( &req->read_section ))
2291 if (get_avail_data(req))
2293 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2294 &buffers->dwBufferLength, FALSE);
2295 size = buffers->dwBufferLength;
2296 LeaveCriticalSection( &req->read_section );
2297 goto done;
2299 LeaveCriticalSection( &req->read_section );
2302 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2303 workRequest.hdr = WININET_AddRef(&req->hdr);
2304 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2306 INTERNET_AsyncCall(&workRequest);
2308 return ERROR_IO_PENDING;
2311 read = 0;
2312 size = buffers->dwBufferLength;
2314 EnterCriticalSection( &req->read_section );
2315 if(hdr->dwError == ERROR_SUCCESS)
2316 hdr->dwError = INTERNET_HANDLE_IN_USE;
2317 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2318 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2320 while(1) {
2321 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2322 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2323 if(res == ERROR_SUCCESS)
2324 read += buffers->dwBufferLength;
2325 else
2326 break;
2328 if(!req->read_chunked || read==size || req->dwContentLength!=req->dwContentRead
2329 || !req->dwContentLength || (req->gzip_stream && req->gzip_stream->end_of_data))
2330 break;
2331 LeaveCriticalSection( &req->read_section );
2333 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2334 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2335 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2336 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2338 EnterCriticalSection( &req->read_section );
2341 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2342 hdr->dwError = ERROR_SUCCESS;
2343 else
2344 error = hdr->dwError;
2346 LeaveCriticalSection( &req->read_section );
2347 size = buffers->dwBufferLength;
2348 buffers->dwBufferLength = read;
2350 done:
2351 if (res == ERROR_SUCCESS) {
2352 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2353 &size, sizeof(size));
2356 return res==ERROR_SUCCESS ? error : res;
2359 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2361 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2362 http_request_t *req = (http_request_t*)workRequest->hdr;
2363 INTERNET_ASYNC_RESULT iar;
2364 DWORD res;
2366 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2368 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2369 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2371 iar.dwResult = res == ERROR_SUCCESS;
2372 iar.dwError = res;
2374 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2375 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2376 sizeof(INTERNET_ASYNC_RESULT));
2379 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2380 DWORD flags, DWORD_PTR context)
2383 http_request_t *req = (http_request_t*)hdr;
2384 DWORD res, size, read, error = ERROR_SUCCESS;
2386 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2387 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2389 if (buffers->dwStructSize != sizeof(*buffers))
2390 return ERROR_INVALID_PARAMETER;
2392 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2394 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2396 WORKREQUEST workRequest;
2398 if (TryEnterCriticalSection( &req->read_section ))
2400 if (get_avail_data(req))
2402 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2403 &buffers->dwBufferLength, FALSE);
2404 size = buffers->dwBufferLength;
2405 LeaveCriticalSection( &req->read_section );
2406 goto done;
2408 LeaveCriticalSection( &req->read_section );
2411 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2412 workRequest.hdr = WININET_AddRef(&req->hdr);
2413 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2415 INTERNET_AsyncCall(&workRequest);
2417 return ERROR_IO_PENDING;
2420 read = 0;
2421 size = buffers->dwBufferLength;
2423 EnterCriticalSection( &req->read_section );
2424 if(hdr->dwError == ERROR_SUCCESS)
2425 hdr->dwError = INTERNET_HANDLE_IN_USE;
2426 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2427 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2429 while(1) {
2430 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2431 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2432 if(res == ERROR_SUCCESS)
2433 read += buffers->dwBufferLength;
2434 else
2435 break;
2437 if(!req->read_chunked || read==size || req->dwContentLength!=req->dwContentRead
2438 || !req->dwContentLength || (req->gzip_stream && req->gzip_stream->end_of_data))
2439 break;
2440 LeaveCriticalSection( &req->read_section );
2442 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2443 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2444 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2445 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2447 EnterCriticalSection( &req->read_section );
2450 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2451 hdr->dwError = ERROR_SUCCESS;
2452 else
2453 error = hdr->dwError;
2455 LeaveCriticalSection( &req->read_section );
2456 size = buffers->dwBufferLength;
2457 buffers->dwBufferLength = read;
2459 done:
2460 if (res == ERROR_SUCCESS) {
2461 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2462 &size, sizeof(size));
2465 return res==ERROR_SUCCESS ? error : res;
2468 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2470 DWORD res;
2471 http_request_t *lpwhr = (http_request_t*)hdr;
2473 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2475 *written = 0;
2476 res = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written);
2477 if (res == ERROR_SUCCESS)
2478 lpwhr->dwBytesWritten += *written;
2480 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2481 return res;
2484 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2486 http_request_t *req = (http_request_t*)workRequest->hdr;
2488 HTTP_ReceiveRequestData(req, FALSE);
2491 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2493 http_request_t *req = (http_request_t*)hdr;
2495 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2497 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2499 WORKREQUEST workRequest;
2501 /* never wait, if we can't enter the section we queue an async request right away */
2502 if (TryEnterCriticalSection( &req->read_section ))
2504 if ((*available = get_avail_data( req ))) goto done;
2505 if (end_of_read_data( req )) goto done;
2506 LeaveCriticalSection( &req->read_section );
2509 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2510 workRequest.hdr = WININET_AddRef( &req->hdr );
2512 INTERNET_AsyncCall(&workRequest);
2514 return ERROR_IO_PENDING;
2517 EnterCriticalSection( &req->read_section );
2519 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2521 refill_buffer( req );
2522 *available = get_avail_data( req );
2525 done:
2526 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2528 DWORD extra;
2529 if (NETCON_query_data_available(&req->netConnection, &extra))
2530 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2532 LeaveCriticalSection( &req->read_section );
2534 TRACE( "returning %u\n", *available );
2535 return ERROR_SUCCESS;
2538 static const object_vtbl_t HTTPREQVtbl = {
2539 HTTPREQ_Destroy,
2540 HTTPREQ_CloseConnection,
2541 HTTPREQ_QueryOption,
2542 HTTPREQ_SetOption,
2543 HTTPREQ_ReadFile,
2544 HTTPREQ_ReadFileExA,
2545 HTTPREQ_ReadFileExW,
2546 HTTPREQ_WriteFile,
2547 HTTPREQ_QueryDataAvailable,
2548 NULL
2551 /***********************************************************************
2552 * HTTP_HttpOpenRequestW (internal)
2554 * Open a HTTP request handle
2556 * RETURNS
2557 * HINTERNET a HTTP request handle on success
2558 * NULL on failure
2561 static DWORD HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2562 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2563 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2564 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2566 appinfo_t *hIC = NULL;
2567 http_request_t *lpwhr;
2568 LPWSTR lpszHostName = NULL;
2569 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2570 DWORD len, res;
2572 TRACE("-->\n");
2574 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2575 hIC = lpwhs->lpAppInfo;
2577 lpwhr = alloc_object(&lpwhs->hdr, &HTTPREQVtbl, sizeof(http_request_t));
2578 if(!lpwhr)
2579 return ERROR_OUTOFMEMORY;
2581 lpwhr->hdr.htype = WH_HHTTPREQ;
2582 lpwhr->hdr.dwFlags = dwFlags;
2583 lpwhr->hdr.dwContext = dwContext;
2584 lpwhr->dwContentLength = ~0u;
2586 InitializeCriticalSection( &lpwhr->read_section );
2588 WININET_AddRef( &lpwhs->hdr );
2589 lpwhr->lpHttpSession = lpwhs;
2590 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2592 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2593 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2594 if (NULL == lpszHostName)
2596 res = ERROR_OUTOFMEMORY;
2597 goto lend;
2600 if ((res = NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2601 goto lend;
2603 if (lpszObjectName && *lpszObjectName) {
2604 HRESULT rc;
2606 len = 0;
2607 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2608 if (rc != E_POINTER)
2609 len = strlenW(lpszObjectName)+1;
2610 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2611 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2612 URL_ESCAPE_SPACES_ONLY);
2613 if (rc != S_OK)
2615 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2616 strcpyW(lpwhr->lpszPath,lpszObjectName);
2618 }else {
2619 static const WCHAR slashW[] = {'/',0};
2621 lpwhr->lpszPath = heap_strdupW(slashW);
2624 if (lpszReferrer && *lpszReferrer)
2625 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2627 if (lpszAcceptTypes)
2629 int i;
2630 for (i = 0; lpszAcceptTypes[i]; i++)
2632 if (!*lpszAcceptTypes[i]) continue;
2633 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2634 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2635 HTTP_ADDHDR_FLAG_REQ |
2636 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2640 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2641 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2643 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2644 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2645 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2647 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2648 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2649 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2651 else
2652 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2653 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2655 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2656 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2657 INTERNET_DEFAULT_HTTPS_PORT :
2658 INTERNET_DEFAULT_HTTP_PORT);
2660 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2661 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2662 INTERNET_DEFAULT_HTTPS_PORT :
2663 INTERNET_DEFAULT_HTTP_PORT);
2665 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2666 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2668 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2669 INTERNET_STATUS_HANDLE_CREATED, &lpwhr->hdr.hInternet,
2670 sizeof(HINTERNET));
2672 lend:
2673 TRACE("<-- %u (%p)\n", res, lpwhr);
2675 HeapFree(GetProcessHeap(), 0, lpszHostName);
2676 if(res != ERROR_SUCCESS) {
2677 WININET_Release( &lpwhr->hdr );
2678 *ret = NULL;
2679 return res;
2682 *ret = lpwhr->hdr.hInternet;
2683 return ERROR_SUCCESS;
2686 /***********************************************************************
2687 * HttpOpenRequestW (WININET.@)
2689 * Open a HTTP request handle
2691 * RETURNS
2692 * HINTERNET a HTTP request handle on success
2693 * NULL on failure
2696 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2697 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2698 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2699 DWORD dwFlags, DWORD_PTR dwContext)
2701 http_session_t *lpwhs;
2702 HINTERNET handle = NULL;
2703 DWORD res;
2705 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2706 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2707 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2708 dwFlags, dwContext);
2709 if(lpszAcceptTypes!=NULL)
2711 int i;
2712 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2713 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2716 lpwhs = (http_session_t*) get_handle_object( hHttpSession );
2717 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
2719 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2720 goto lend;
2724 * My tests seem to show that the windows version does not
2725 * become asynchronous until after this point. And anyhow
2726 * if this call was asynchronous then how would you get the
2727 * necessary HINTERNET pointer returned by this function.
2730 res = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
2731 lpszVersion, lpszReferrer, lpszAcceptTypes,
2732 dwFlags, dwContext, &handle);
2733 lend:
2734 if( lpwhs )
2735 WININET_Release( &lpwhs->hdr );
2736 TRACE("returning %p\n", handle);
2737 if(res != ERROR_SUCCESS)
2738 SetLastError(res);
2739 return handle;
2742 /* read any content returned by the server so that the connection can be
2743 * reused */
2744 static void HTTP_DrainContent(http_request_t *req)
2746 DWORD bytes_read;
2748 if (!NETCON_connected(&req->netConnection)) return;
2750 if (req->dwContentLength == -1)
2752 NETCON_close(&req->netConnection);
2753 return;
2755 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2759 char buffer[2048];
2760 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2761 return;
2762 } while (bytes_read);
2765 static const LPCWSTR header_lookup[] = {
2766 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2767 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2768 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2769 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2770 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2771 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2772 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2773 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2774 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2775 szDate, /* HTTP_QUERY_DATE = 9 */
2776 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2777 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2778 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2779 szURI, /* HTTP_QUERY_URI = 13 */
2780 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2781 NULL, /* HTTP_QUERY_COST = 15 */
2782 NULL, /* HTTP_QUERY_LINK = 16 */
2783 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2784 NULL, /* HTTP_QUERY_VERSION = 18 */
2785 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2786 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2787 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2788 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2789 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2790 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2791 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2792 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2793 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2794 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2795 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2796 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2797 NULL, /* HTTP_QUERY_FROM = 31 */
2798 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2799 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2800 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2801 szReferer, /* HTTP_QUERY_REFERER = 35 */
2802 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2803 szServer, /* HTTP_QUERY_SERVER = 37 */
2804 NULL, /* HTTP_TITLE = 38 */
2805 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2806 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2807 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2808 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2809 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2810 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2811 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2812 NULL, /* HTTP_QUERY_REFRESH = 46 */
2813 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2814 szAge, /* HTTP_QUERY_AGE = 48 */
2815 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2816 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2817 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2818 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2819 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2820 szETag, /* HTTP_QUERY_ETAG = 54 */
2821 hostW, /* HTTP_QUERY_HOST = 55 */
2822 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2823 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2824 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2825 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2826 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2827 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2828 szRange, /* HTTP_QUERY_RANGE = 62 */
2829 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2830 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2831 szVary, /* HTTP_QUERY_VARY = 65 */
2832 szVia, /* HTTP_QUERY_VIA = 66 */
2833 szWarning, /* HTTP_QUERY_WARNING = 67 */
2834 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2835 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2836 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2839 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2841 /***********************************************************************
2842 * HTTP_HttpQueryInfoW (internal)
2844 static DWORD HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2845 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2847 LPHTTPHEADERW lphttpHdr = NULL;
2848 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2849 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2850 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2851 INT index = -1;
2853 /* Find requested header structure */
2854 switch (level)
2856 case HTTP_QUERY_CUSTOM:
2857 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
2858 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2859 break;
2860 case HTTP_QUERY_RAW_HEADERS_CRLF:
2862 LPWSTR headers;
2863 DWORD len = 0;
2864 DWORD res = ERROR_INVALID_PARAMETER;
2866 if (request_only)
2867 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2868 else
2869 headers = lpwhr->lpszRawHeaders;
2871 if (headers)
2872 len = strlenW(headers) * sizeof(WCHAR);
2874 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2876 len += sizeof(WCHAR);
2877 res = ERROR_INSUFFICIENT_BUFFER;
2879 else if (lpBuffer)
2881 if (headers)
2882 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2883 else
2885 len = strlenW(szCrLf) * sizeof(WCHAR);
2886 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2888 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2889 res = ERROR_SUCCESS;
2891 *lpdwBufferLength = len;
2893 if (request_only)
2894 HeapFree(GetProcessHeap(), 0, headers);
2895 return res;
2897 case HTTP_QUERY_RAW_HEADERS:
2899 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2900 DWORD i, size = 0;
2901 LPWSTR pszString = lpBuffer;
2903 for (i = 0; ppszRawHeaderLines[i]; i++)
2904 size += strlenW(ppszRawHeaderLines[i]) + 1;
2906 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2908 HTTP_FreeTokens(ppszRawHeaderLines);
2909 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2910 return ERROR_INSUFFICIENT_BUFFER;
2912 if (pszString)
2914 for (i = 0; ppszRawHeaderLines[i]; i++)
2916 DWORD len = strlenW(ppszRawHeaderLines[i]);
2917 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2918 pszString += len+1;
2920 *pszString = '\0';
2921 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2923 *lpdwBufferLength = size * sizeof(WCHAR);
2924 HTTP_FreeTokens(ppszRawHeaderLines);
2926 return ERROR_SUCCESS;
2928 case HTTP_QUERY_STATUS_TEXT:
2929 if (lpwhr->lpszStatusText)
2931 DWORD len = strlenW(lpwhr->lpszStatusText);
2932 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2934 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2935 return ERROR_INSUFFICIENT_BUFFER;
2937 if (lpBuffer)
2939 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2940 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2942 *lpdwBufferLength = len * sizeof(WCHAR);
2943 return ERROR_SUCCESS;
2945 break;
2946 case HTTP_QUERY_VERSION:
2947 if (lpwhr->lpszVersion)
2949 DWORD len = strlenW(lpwhr->lpszVersion);
2950 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2952 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2953 return ERROR_INSUFFICIENT_BUFFER;
2955 if (lpBuffer)
2957 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2958 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2960 *lpdwBufferLength = len * sizeof(WCHAR);
2961 return ERROR_SUCCESS;
2963 break;
2964 case HTTP_QUERY_CONTENT_ENCODING:
2965 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2966 requested_index,request_only);
2967 break;
2968 default:
2969 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2971 if (level < LAST_TABLE_HEADER && header_lookup[level])
2972 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2973 requested_index,request_only);
2976 if (index >= 0)
2977 lphttpHdr = &lpwhr->pCustHeaders[index];
2979 /* Ensure header satisfies requested attributes */
2980 if (!lphttpHdr ||
2981 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2982 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2984 return ERROR_HTTP_HEADER_NOT_FOUND;
2987 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2989 /* coalesce value to requested type */
2990 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2992 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2993 TRACE(" returning number: %d\n", *(int *)lpBuffer);
2995 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2997 time_t tmpTime;
2998 struct tm tmpTM;
2999 SYSTEMTIME *STHook;
3001 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3003 tmpTM = *gmtime(&tmpTime);
3004 STHook = (SYSTEMTIME *)lpBuffer;
3005 STHook->wDay = tmpTM.tm_mday;
3006 STHook->wHour = tmpTM.tm_hour;
3007 STHook->wMilliseconds = 0;
3008 STHook->wMinute = tmpTM.tm_min;
3009 STHook->wDayOfWeek = tmpTM.tm_wday;
3010 STHook->wMonth = tmpTM.tm_mon + 1;
3011 STHook->wSecond = tmpTM.tm_sec;
3012 STHook->wYear = tmpTM.tm_year;
3014 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3015 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3016 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3018 else if (lphttpHdr->lpszValue)
3020 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3022 if (len > *lpdwBufferLength)
3024 *lpdwBufferLength = len;
3025 return ERROR_INSUFFICIENT_BUFFER;
3027 if (lpBuffer)
3029 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3030 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3032 *lpdwBufferLength = len - sizeof(WCHAR);
3034 return ERROR_SUCCESS;
3037 /***********************************************************************
3038 * HttpQueryInfoW (WININET.@)
3040 * Queries for information about an HTTP request
3042 * RETURNS
3043 * TRUE on success
3044 * FALSE on failure
3047 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3048 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3050 http_request_t *lpwhr;
3051 DWORD res;
3053 if (TRACE_ON(wininet)) {
3054 #define FE(x) { x, #x }
3055 static const wininet_flag_info query_flags[] = {
3056 FE(HTTP_QUERY_MIME_VERSION),
3057 FE(HTTP_QUERY_CONTENT_TYPE),
3058 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3059 FE(HTTP_QUERY_CONTENT_ID),
3060 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3061 FE(HTTP_QUERY_CONTENT_LENGTH),
3062 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3063 FE(HTTP_QUERY_ALLOW),
3064 FE(HTTP_QUERY_PUBLIC),
3065 FE(HTTP_QUERY_DATE),
3066 FE(HTTP_QUERY_EXPIRES),
3067 FE(HTTP_QUERY_LAST_MODIFIED),
3068 FE(HTTP_QUERY_MESSAGE_ID),
3069 FE(HTTP_QUERY_URI),
3070 FE(HTTP_QUERY_DERIVED_FROM),
3071 FE(HTTP_QUERY_COST),
3072 FE(HTTP_QUERY_LINK),
3073 FE(HTTP_QUERY_PRAGMA),
3074 FE(HTTP_QUERY_VERSION),
3075 FE(HTTP_QUERY_STATUS_CODE),
3076 FE(HTTP_QUERY_STATUS_TEXT),
3077 FE(HTTP_QUERY_RAW_HEADERS),
3078 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3079 FE(HTTP_QUERY_CONNECTION),
3080 FE(HTTP_QUERY_ACCEPT),
3081 FE(HTTP_QUERY_ACCEPT_CHARSET),
3082 FE(HTTP_QUERY_ACCEPT_ENCODING),
3083 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3084 FE(HTTP_QUERY_AUTHORIZATION),
3085 FE(HTTP_QUERY_CONTENT_ENCODING),
3086 FE(HTTP_QUERY_FORWARDED),
3087 FE(HTTP_QUERY_FROM),
3088 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3089 FE(HTTP_QUERY_LOCATION),
3090 FE(HTTP_QUERY_ORIG_URI),
3091 FE(HTTP_QUERY_REFERER),
3092 FE(HTTP_QUERY_RETRY_AFTER),
3093 FE(HTTP_QUERY_SERVER),
3094 FE(HTTP_QUERY_TITLE),
3095 FE(HTTP_QUERY_USER_AGENT),
3096 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3097 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3098 FE(HTTP_QUERY_ACCEPT_RANGES),
3099 FE(HTTP_QUERY_SET_COOKIE),
3100 FE(HTTP_QUERY_COOKIE),
3101 FE(HTTP_QUERY_REQUEST_METHOD),
3102 FE(HTTP_QUERY_REFRESH),
3103 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3104 FE(HTTP_QUERY_AGE),
3105 FE(HTTP_QUERY_CACHE_CONTROL),
3106 FE(HTTP_QUERY_CONTENT_BASE),
3107 FE(HTTP_QUERY_CONTENT_LOCATION),
3108 FE(HTTP_QUERY_CONTENT_MD5),
3109 FE(HTTP_QUERY_CONTENT_RANGE),
3110 FE(HTTP_QUERY_ETAG),
3111 FE(HTTP_QUERY_HOST),
3112 FE(HTTP_QUERY_IF_MATCH),
3113 FE(HTTP_QUERY_IF_NONE_MATCH),
3114 FE(HTTP_QUERY_IF_RANGE),
3115 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3116 FE(HTTP_QUERY_MAX_FORWARDS),
3117 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3118 FE(HTTP_QUERY_RANGE),
3119 FE(HTTP_QUERY_TRANSFER_ENCODING),
3120 FE(HTTP_QUERY_UPGRADE),
3121 FE(HTTP_QUERY_VARY),
3122 FE(HTTP_QUERY_VIA),
3123 FE(HTTP_QUERY_WARNING),
3124 FE(HTTP_QUERY_CUSTOM)
3126 static const wininet_flag_info modifier_flags[] = {
3127 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3128 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3129 FE(HTTP_QUERY_FLAG_NUMBER),
3130 FE(HTTP_QUERY_FLAG_COALESCE)
3132 #undef FE
3133 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3134 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3135 DWORD i;
3137 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3138 TRACE(" Attribute:");
3139 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3140 if (query_flags[i].val == info) {
3141 TRACE(" %s", query_flags[i].name);
3142 break;
3145 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3146 TRACE(" Unknown (%08x)", info);
3149 TRACE(" Modifier:");
3150 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3151 if (modifier_flags[i].val & info_mod) {
3152 TRACE(" %s", modifier_flags[i].name);
3153 info_mod &= ~ modifier_flags[i].val;
3157 if (info_mod) {
3158 TRACE(" Unknown (%08x)", info_mod);
3160 TRACE("\n");
3163 lpwhr = (http_request_t*) get_handle_object( hHttpRequest );
3164 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3166 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3167 goto lend;
3170 if (lpBuffer == NULL)
3171 *lpdwBufferLength = 0;
3172 res = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
3173 lpBuffer, lpdwBufferLength, lpdwIndex);
3175 lend:
3176 if( lpwhr )
3177 WININET_Release( &lpwhr->hdr );
3179 TRACE("%u <--\n", res);
3180 if(res != ERROR_SUCCESS)
3181 SetLastError(res);
3182 return res == ERROR_SUCCESS;
3185 /***********************************************************************
3186 * HttpQueryInfoA (WININET.@)
3188 * Queries for information about an HTTP request
3190 * RETURNS
3191 * TRUE on success
3192 * FALSE on failure
3195 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3196 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3198 BOOL result;
3199 DWORD len;
3200 WCHAR* bufferW;
3202 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3203 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3205 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3206 lpdwBufferLength, lpdwIndex );
3209 if (lpBuffer)
3211 DWORD alloclen;
3212 len = (*lpdwBufferLength)*sizeof(WCHAR);
3213 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3215 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3216 if (alloclen < len)
3217 alloclen = len;
3219 else
3220 alloclen = len;
3221 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3222 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3223 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3224 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3225 } else
3227 bufferW = NULL;
3228 len = 0;
3231 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3232 &len, lpdwIndex );
3233 if( result )
3235 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3236 lpBuffer, *lpdwBufferLength, NULL, NULL );
3237 *lpdwBufferLength = len - 1;
3239 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3241 else
3242 /* since the strings being returned from HttpQueryInfoW should be
3243 * only ASCII characters, it is reasonable to assume that all of
3244 * the Unicode characters can be reduced to a single byte */
3245 *lpdwBufferLength = len / sizeof(WCHAR);
3247 HeapFree(GetProcessHeap(), 0, bufferW );
3249 return result;
3252 /***********************************************************************
3253 * HTTP_GetRedirectURL (internal)
3255 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3257 static WCHAR szHttp[] = {'h','t','t','p',0};
3258 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3259 http_session_t *lpwhs = lpwhr->lpHttpSession;
3260 URL_COMPONENTSW urlComponents;
3261 DWORD url_length = 0;
3262 LPWSTR orig_url;
3263 LPWSTR combined_url;
3265 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3266 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3267 urlComponents.dwSchemeLength = 0;
3268 urlComponents.lpszHostName = lpwhs->lpszHostName;
3269 urlComponents.dwHostNameLength = 0;
3270 urlComponents.nPort = lpwhs->nHostPort;
3271 urlComponents.lpszUserName = lpwhs->lpszUserName;
3272 urlComponents.dwUserNameLength = 0;
3273 urlComponents.lpszPassword = NULL;
3274 urlComponents.dwPasswordLength = 0;
3275 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3276 urlComponents.dwUrlPathLength = 0;
3277 urlComponents.lpszExtraInfo = NULL;
3278 urlComponents.dwExtraInfoLength = 0;
3280 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3281 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3282 return NULL;
3284 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3286 /* convert from bytes to characters */
3287 url_length = url_length / sizeof(WCHAR) - 1;
3288 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3290 HeapFree(GetProcessHeap(), 0, orig_url);
3291 return NULL;
3294 url_length = 0;
3295 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3296 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3298 HeapFree(GetProcessHeap(), 0, orig_url);
3299 return NULL;
3301 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3303 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3305 HeapFree(GetProcessHeap(), 0, orig_url);
3306 HeapFree(GetProcessHeap(), 0, combined_url);
3307 return NULL;
3309 HeapFree(GetProcessHeap(), 0, orig_url);
3310 return combined_url;
3314 /***********************************************************************
3315 * HTTP_HandleRedirect (internal)
3317 static DWORD HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3319 http_session_t *lpwhs = lpwhr->lpHttpSession;
3320 appinfo_t *hIC = lpwhs->lpAppInfo;
3321 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3322 WCHAR path[INTERNET_MAX_URL_LENGTH];
3323 int index;
3325 if(lpszUrl[0]=='/')
3327 /* if it's an absolute path, keep the same session info */
3328 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3330 else
3332 URL_COMPONENTSW urlComponents;
3333 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3334 static WCHAR szHttp[] = {'h','t','t','p',0};
3335 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3337 userName[0] = 0;
3338 hostName[0] = 0;
3339 protocol[0] = 0;
3341 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3342 urlComponents.lpszScheme = protocol;
3343 urlComponents.dwSchemeLength = 32;
3344 urlComponents.lpszHostName = hostName;
3345 urlComponents.dwHostNameLength = MAXHOSTNAME;
3346 urlComponents.lpszUserName = userName;
3347 urlComponents.dwUserNameLength = 1024;
3348 urlComponents.lpszPassword = NULL;
3349 urlComponents.dwPasswordLength = 0;
3350 urlComponents.lpszUrlPath = path;
3351 urlComponents.dwUrlPathLength = 2048;
3352 urlComponents.lpszExtraInfo = NULL;
3353 urlComponents.dwExtraInfoLength = 0;
3354 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3355 return INTERNET_GetLastError();
3357 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3358 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3360 TRACE("redirect from secure page to non-secure page\n");
3361 /* FIXME: warn about from secure redirect to non-secure page */
3362 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3364 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3365 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3367 TRACE("redirect from non-secure page to secure page\n");
3368 /* FIXME: notify about redirect to secure page */
3369 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3372 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3374 if (lstrlenW(protocol)>4) /*https*/
3375 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3376 else /*http*/
3377 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3380 #if 0
3382 * This upsets redirects to binary files on sourceforge.net
3383 * and gives an html page instead of the target file
3384 * Examination of the HTTP request sent by native wininet.dll
3385 * reveals that it doesn't send a referrer in that case.
3386 * Maybe there's a flag that enables this, or maybe a referrer
3387 * shouldn't be added in case of a redirect.
3390 /* consider the current host as the referrer */
3391 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3392 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3393 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3394 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3395 #endif
3397 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3398 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3399 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3401 int len;
3402 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3403 len = lstrlenW(hostName);
3404 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3405 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3406 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3408 else
3409 lpwhs->lpszHostName = heap_strdupW(hostName);
3411 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3413 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3414 lpwhs->lpszUserName = NULL;
3415 if (userName[0])
3416 lpwhs->lpszUserName = heap_strdupW(userName);
3418 if (!using_proxy)
3420 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3422 DWORD res;
3424 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3425 lpwhs->lpszServerName = heap_strdupW(hostName);
3426 lpwhs->nServerPort = urlComponents.nPort;
3428 NETCON_close(&lpwhr->netConnection);
3429 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS)
3430 return res;
3432 res = NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE);
3433 if (res != ERROR_SUCCESS)
3434 return res;
3436 lpwhr->read_pos = lpwhr->read_size = 0;
3437 lpwhr->read_chunked = FALSE;
3440 else
3441 TRACE("Redirect through proxy\n");
3444 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3445 lpwhr->lpszPath=NULL;
3446 if (*path)
3448 DWORD needed = 0;
3449 HRESULT rc;
3451 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3452 if (rc != E_POINTER)
3453 needed = strlenW(path)+1;
3454 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3455 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3456 URL_ESCAPE_SPACES_ONLY);
3457 if (rc != S_OK)
3459 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3460 strcpyW(lpwhr->lpszPath,path);
3464 /* Remove custom content-type/length headers on redirects. */
3465 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3466 if (0 <= index)
3467 HTTP_DeleteCustomHeader(lpwhr, index);
3468 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3469 if (0 <= index)
3470 HTTP_DeleteCustomHeader(lpwhr, index);
3472 return ERROR_SUCCESS;
3475 /***********************************************************************
3476 * HTTP_build_req (internal)
3478 * concatenate all the strings in the request together
3480 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3482 LPCWSTR *t;
3483 LPWSTR str;
3485 for( t = list; *t ; t++ )
3486 len += strlenW( *t );
3487 len++;
3489 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3490 *str = 0;
3492 for( t = list; *t ; t++ )
3493 strcatW( str, *t );
3495 return str;
3498 static DWORD HTTP_SecureProxyConnect(http_request_t *lpwhr)
3500 LPWSTR lpszPath;
3501 LPWSTR requestString;
3502 INT len;
3503 INT cnt;
3504 INT responseLen;
3505 char *ascii_req;
3506 DWORD res;
3507 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3508 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3509 http_session_t *lpwhs = lpwhr->lpHttpSession;
3511 TRACE("\n");
3513 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3514 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3515 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3516 HeapFree( GetProcessHeap(), 0, lpszPath );
3518 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3519 NULL, 0, NULL, NULL );
3520 len--; /* the nul terminator isn't needed */
3521 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3522 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3523 ascii_req, len, NULL, NULL );
3524 HeapFree( GetProcessHeap(), 0, requestString );
3526 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3528 res = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3529 HeapFree( GetProcessHeap(), 0, ascii_req );
3530 if (res != ERROR_SUCCESS)
3531 return res;
3533 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3534 if (!responseLen)
3535 return ERROR_HTTP_INVALID_HEADER;
3537 return ERROR_SUCCESS;
3540 static void HTTP_InsertCookies(http_request_t *lpwhr)
3542 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3543 LPWSTR lpszCookies, lpszUrl = NULL;
3544 DWORD nCookieSize, size;
3545 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3547 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3548 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3549 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3551 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3553 int cnt = 0;
3554 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3556 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3557 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3559 cnt += sprintfW(lpszCookies, szCookie);
3560 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3561 strcatW(lpszCookies, szCrLf);
3563 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3564 HeapFree(GetProcessHeap(), 0, lpszCookies);
3567 HeapFree(GetProcessHeap(), 0, lpszUrl);
3570 /***********************************************************************
3571 * HTTP_HttpSendRequestW (internal)
3573 * Sends the specified request to the HTTP server
3575 * RETURNS
3576 * TRUE on success
3577 * FALSE on failure
3580 static DWORD HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3581 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3582 DWORD dwContentLength, BOOL bEndRequest)
3584 INT cnt;
3585 BOOL redirected = FALSE;
3586 LPWSTR requestString = NULL;
3587 INT responseLen;
3588 BOOL loop_next;
3589 INTERNET_ASYNC_RESULT iar;
3590 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3591 static const WCHAR szContentLength[] =
3592 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3593 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3594 DWORD res;
3596 TRACE("--> %p\n", lpwhr);
3598 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3600 /* if the verb is NULL default to GET */
3601 if (!lpwhr->lpszVerb)
3602 lpwhr->lpszVerb = heap_strdupW(szGET);
3604 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3606 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3607 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3608 lpwhr->dwBytesToWrite = dwContentLength;
3610 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3612 WCHAR *agent_header;
3613 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3614 int len;
3616 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3617 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3618 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3620 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3621 HeapFree(GetProcessHeap(), 0, agent_header);
3623 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3625 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3626 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3628 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3630 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3631 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3632 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3637 DWORD len;
3638 BOOL reusing_connection;
3639 char *ascii_req;
3641 loop_next = FALSE;
3643 /* like native, just in case the caller forgot to call InternetReadFile
3644 * for all the data */
3645 HTTP_DrainContent(lpwhr);
3646 lpwhr->dwContentRead = 0;
3647 if(redirected) {
3648 lpwhr->dwContentLength = ~0u;
3649 lpwhr->dwBytesToWrite = 0;
3652 if (TRACE_ON(wininet))
3654 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3655 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3658 HTTP_FixURL(lpwhr);
3659 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3661 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3663 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3664 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3666 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3667 HTTP_InsertCookies(lpwhr);
3669 /* add the headers the caller supplied */
3670 if( lpszHeaders && dwHeaderLength )
3672 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3673 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3676 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3678 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3679 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3680 HeapFree(GetProcessHeap(), 0, url);
3682 else
3683 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3686 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3688 /* Send the request and store the results */
3689 if(NETCON_connected(&lpwhr->netConnection))
3690 reusing_connection = TRUE;
3691 else
3692 reusing_connection = FALSE;
3694 if ((res = HTTP_OpenConnection(lpwhr)) != ERROR_SUCCESS)
3695 goto lend;
3697 /* send the request as ASCII, tack on the optional data */
3698 if (!lpOptional || redirected)
3699 dwOptionalLength = 0;
3700 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3701 NULL, 0, NULL, NULL );
3702 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3703 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3704 ascii_req, len, NULL, NULL );
3705 if( lpOptional )
3706 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3707 len = (len + dwOptionalLength - 1);
3708 ascii_req[len] = 0;
3709 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3711 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3712 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3714 res = NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3715 HeapFree( GetProcessHeap(), 0, ascii_req );
3717 lpwhr->dwBytesWritten = dwOptionalLength;
3719 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3720 INTERNET_STATUS_REQUEST_SENT,
3721 &len, sizeof(DWORD));
3723 if (bEndRequest)
3725 DWORD dwBufferSize;
3726 DWORD dwStatusCode;
3728 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3729 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3731 if (res != ERROR_SUCCESS)
3732 goto lend;
3734 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3735 /* FIXME: We should know that connection is closed before sending
3736 * headers. Otherwise wrong callbacks are executed */
3737 if(!responseLen && reusing_connection) {
3738 TRACE("Connection closed by server, reconnecting\n");
3739 loop_next = TRUE;
3740 continue;
3743 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3744 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3745 sizeof(DWORD));
3747 HTTP_ProcessCookies(lpwhr);
3749 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3751 dwBufferSize = sizeof(dwStatusCode);
3752 if (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3753 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
3754 dwStatusCode = 0;
3756 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
3758 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3759 dwBufferSize=sizeof(szNewLocation);
3760 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
3761 dwStatusCode == HTTP_STATUS_MOVED ||
3762 dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
3763 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
3765 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3767 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3768 lpwhr->lpszVerb = heap_strdupW(szGET);
3770 HTTP_DrainContent(lpwhr);
3771 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3773 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3774 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3775 res = HTTP_HandleRedirect(lpwhr, new_url);
3776 if (res == ERROR_SUCCESS)
3778 HeapFree(GetProcessHeap(), 0, requestString);
3779 loop_next = TRUE;
3781 HeapFree( GetProcessHeap(), 0, new_url );
3783 redirected = TRUE;
3786 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
3788 WCHAR szAuthValue[2048];
3789 dwBufferSize=2048;
3790 if (dwStatusCode == HTTP_STATUS_DENIED)
3792 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3793 DWORD dwIndex = 0;
3794 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3796 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3797 &lpwhr->pAuthInfo,
3798 lpwhr->lpHttpSession->lpszUserName,
3799 lpwhr->lpHttpSession->lpszPassword,
3800 Host->lpszValue))
3802 HeapFree(GetProcessHeap(), 0, requestString);
3803 loop_next = TRUE;
3804 break;
3808 if(!loop_next) {
3809 TRACE("Cleaning wrong authorization data\n");
3810 destroy_authinfo(lpwhr->pAuthInfo);
3811 lpwhr->pAuthInfo = NULL;
3814 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3816 DWORD dwIndex = 0;
3817 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3819 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3820 &lpwhr->pProxyAuthInfo,
3821 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3822 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
3823 NULL))
3825 loop_next = TRUE;
3826 break;
3830 if(!loop_next) {
3831 TRACE("Cleaning wrong proxy authorization data\n");
3832 destroy_authinfo(lpwhr->pProxyAuthInfo);
3833 lpwhr->pProxyAuthInfo = NULL;
3838 else
3839 res = ERROR_SUCCESS;
3841 while (loop_next);
3843 if(res == ERROR_SUCCESS) {
3844 WCHAR url[INTERNET_MAX_URL_LENGTH];
3845 WCHAR cacheFileName[MAX_PATH+1];
3846 BOOL b;
3848 b = HTTP_GetRequestURL(lpwhr, url);
3849 if(!b) {
3850 WARN("Could not get URL\n");
3851 goto lend;
3854 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3855 if(b) {
3856 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
3857 CloseHandle(lpwhr->hCacheFile);
3859 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3860 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3861 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3862 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3863 WARN("Could not create file: %u\n", GetLastError());
3864 lpwhr->hCacheFile = NULL;
3866 }else {
3867 WARN("Could not create cache entry: %08x\n", GetLastError());
3871 lend:
3873 HeapFree(GetProcessHeap(), 0, requestString);
3875 /* TODO: send notification for P3P header */
3877 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3879 if (res == ERROR_SUCCESS && lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite)
3880 HTTP_ReceiveRequestData(lpwhr, TRUE);
3881 else
3883 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)lpwhr->hdr.hInternet : 0);
3884 iar.dwError = res;
3886 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3887 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3888 sizeof(INTERNET_ASYNC_RESULT));
3892 TRACE("<--\n");
3893 return res;
3896 /***********************************************************************
3898 * Helper functions for the HttpSendRequest(Ex) functions
3901 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
3903 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
3904 http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
3906 TRACE("%p\n", lpwhr);
3908 HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
3909 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
3910 req->dwContentLength, req->bEndRequest);
3912 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
3916 static DWORD HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
3918 INT responseLen;
3919 DWORD dwBufferSize;
3920 INTERNET_ASYNC_RESULT iar;
3921 DWORD res = ERROR_SUCCESS;
3923 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3924 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3926 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3927 if (!responseLen)
3928 res = ERROR_HTTP_HEADER_NOT_FOUND;
3930 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3931 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
3933 /* process cookies here. Is this right? */
3934 HTTP_ProcessCookies(lpwhr);
3936 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3938 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
3940 DWORD dwCode,dwCodeLength = sizeof(DWORD);
3941 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
3942 && (dwCode == 302 || dwCode == 301 || dwCode == 303))
3944 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3945 dwBufferSize=sizeof(szNewLocation);
3946 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
3948 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3950 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3951 lpwhr->lpszVerb = heap_strdupW(szGET);
3953 HTTP_DrainContent(lpwhr);
3954 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3956 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3957 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3958 res = HTTP_HandleRedirect(lpwhr, new_url);
3959 if (res == ERROR_SUCCESS)
3960 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
3961 HeapFree( GetProcessHeap(), 0, new_url );
3967 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)lpwhr->hdr.hInternet : 0);
3968 iar.dwError = res;
3970 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3971 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3972 sizeof(INTERNET_ASYNC_RESULT));
3973 return res;
3976 /***********************************************************************
3977 * HttpEndRequestA (WININET.@)
3979 * Ends an HTTP request that was started by HttpSendRequestEx
3981 * RETURNS
3982 * TRUE if successful
3983 * FALSE on failure
3986 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
3987 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3989 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
3991 if (lpBuffersOut)
3993 SetLastError(ERROR_INVALID_PARAMETER);
3994 return FALSE;
3997 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
4000 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
4002 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
4003 http_request_t *lpwhr = (http_request_t*)work->hdr;
4005 TRACE("%p\n", lpwhr);
4007 HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
4010 /***********************************************************************
4011 * HttpEndRequestW (WININET.@)
4013 * Ends an HTTP request that was started by HttpSendRequestEx
4015 * RETURNS
4016 * TRUE if successful
4017 * FALSE on failure
4020 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
4021 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4023 http_request_t *lpwhr;
4024 DWORD res;
4026 TRACE("-->\n");
4028 if (lpBuffersOut)
4030 SetLastError(ERROR_INVALID_PARAMETER);
4031 return FALSE;
4034 lpwhr = (http_request_t*) get_handle_object( hRequest );
4036 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4038 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
4039 if (lpwhr)
4040 WININET_Release( &lpwhr->hdr );
4041 return FALSE;
4043 lpwhr->hdr.dwFlags |= dwFlags;
4045 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4047 WORKREQUEST work;
4048 struct WORKREQ_HTTPENDREQUESTW *request;
4050 work.asyncproc = AsyncHttpEndRequestProc;
4051 work.hdr = WININET_AddRef( &lpwhr->hdr );
4053 request = &work.u.HttpEndRequestW;
4054 request->dwFlags = dwFlags;
4055 request->dwContext = dwContext;
4057 INTERNET_AsyncCall(&work);
4058 res = ERROR_IO_PENDING;
4060 else
4061 res = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
4063 WININET_Release( &lpwhr->hdr );
4064 TRACE("%u <--\n", res);
4065 if(res != ERROR_SUCCESS)
4066 SetLastError(res);
4067 return res == ERROR_SUCCESS;
4070 /***********************************************************************
4071 * HttpSendRequestExA (WININET.@)
4073 * Sends the specified request to the HTTP server and allows chunked
4074 * transfers.
4076 * RETURNS
4077 * Success: TRUE
4078 * Failure: FALSE, call GetLastError() for more information.
4080 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
4081 LPINTERNET_BUFFERSA lpBuffersIn,
4082 LPINTERNET_BUFFERSA lpBuffersOut,
4083 DWORD dwFlags, DWORD_PTR dwContext)
4085 INTERNET_BUFFERSW BuffersInW;
4086 BOOL rc = FALSE;
4087 DWORD headerlen;
4088 LPWSTR header = NULL;
4090 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4091 lpBuffersOut, dwFlags, dwContext);
4093 if (lpBuffersIn)
4095 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
4096 if (lpBuffersIn->lpcszHeader)
4098 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
4099 lpBuffersIn->dwHeadersLength,0,0);
4100 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
4101 if (!(BuffersInW.lpcszHeader = header))
4103 SetLastError(ERROR_OUTOFMEMORY);
4104 return FALSE;
4106 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
4107 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4108 header, headerlen);
4110 else
4111 BuffersInW.lpcszHeader = NULL;
4112 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
4113 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
4114 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
4115 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
4116 BuffersInW.Next = NULL;
4119 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
4121 HeapFree(GetProcessHeap(),0,header);
4123 return rc;
4126 /***********************************************************************
4127 * HttpSendRequestExW (WININET.@)
4129 * Sends the specified request to the HTTP server and allows chunked
4130 * transfers
4132 * RETURNS
4133 * Success: TRUE
4134 * Failure: FALSE, call GetLastError() for more information.
4136 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
4137 LPINTERNET_BUFFERSW lpBuffersIn,
4138 LPINTERNET_BUFFERSW lpBuffersOut,
4139 DWORD dwFlags, DWORD_PTR dwContext)
4141 http_request_t *lpwhr;
4142 http_session_t *lpwhs;
4143 appinfo_t *hIC;
4144 DWORD res;
4146 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4147 lpBuffersOut, dwFlags, dwContext);
4149 lpwhr = (http_request_t*) get_handle_object( hRequest );
4151 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4153 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4154 goto lend;
4157 lpwhs = lpwhr->lpHttpSession;
4158 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
4159 hIC = lpwhs->lpAppInfo;
4160 assert(hIC->hdr.htype == WH_HINIT);
4162 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4164 WORKREQUEST workRequest;
4165 struct WORKREQ_HTTPSENDREQUESTW *req;
4167 workRequest.asyncproc = AsyncHttpSendRequestProc;
4168 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4169 req = &workRequest.u.HttpSendRequestW;
4170 if (lpBuffersIn)
4172 DWORD size = 0;
4174 if (lpBuffersIn->lpcszHeader)
4176 if (lpBuffersIn->dwHeadersLength == ~0u)
4177 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
4178 else
4179 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
4181 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
4182 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
4184 else req->lpszHeader = NULL;
4186 req->dwHeaderLength = size / sizeof(WCHAR);
4187 req->lpOptional = lpBuffersIn->lpvBuffer;
4188 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
4189 req->dwContentLength = lpBuffersIn->dwBufferTotal;
4191 else
4193 req->lpszHeader = NULL;
4194 req->dwHeaderLength = 0;
4195 req->lpOptional = NULL;
4196 req->dwOptionalLength = 0;
4197 req->dwContentLength = 0;
4200 req->bEndRequest = FALSE;
4202 INTERNET_AsyncCall(&workRequest);
4204 * This is from windows.
4206 res = ERROR_IO_PENDING;
4208 else
4210 if (lpBuffersIn)
4211 res = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4212 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
4213 lpBuffersIn->dwBufferTotal, FALSE);
4214 else
4215 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
4218 lend:
4219 if ( lpwhr )
4220 WININET_Release( &lpwhr->hdr );
4222 TRACE("<---\n");
4223 SetLastError(res);
4224 return res == ERROR_SUCCESS;
4227 /***********************************************************************
4228 * HttpSendRequestW (WININET.@)
4230 * Sends the specified request to the HTTP server
4232 * RETURNS
4233 * TRUE on success
4234 * FALSE on failure
4237 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4238 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4240 http_request_t *lpwhr;
4241 http_session_t *lpwhs = NULL;
4242 appinfo_t *hIC = NULL;
4243 DWORD res = ERROR_SUCCESS;
4245 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4246 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4248 lpwhr = (http_request_t*) get_handle_object( hHttpRequest );
4249 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4251 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4252 goto lend;
4255 lpwhs = lpwhr->lpHttpSession;
4256 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
4258 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4259 goto lend;
4262 hIC = lpwhs->lpAppInfo;
4263 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
4265 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4266 goto lend;
4269 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4271 WORKREQUEST workRequest;
4272 struct WORKREQ_HTTPSENDREQUESTW *req;
4274 workRequest.asyncproc = AsyncHttpSendRequestProc;
4275 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4276 req = &workRequest.u.HttpSendRequestW;
4277 if (lpszHeaders)
4279 DWORD size;
4281 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4282 else size = dwHeaderLength * sizeof(WCHAR);
4284 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4285 memcpy(req->lpszHeader, lpszHeaders, size);
4287 else
4288 req->lpszHeader = 0;
4289 req->dwHeaderLength = dwHeaderLength;
4290 req->lpOptional = lpOptional;
4291 req->dwOptionalLength = dwOptionalLength;
4292 req->dwContentLength = dwOptionalLength;
4293 req->bEndRequest = TRUE;
4295 INTERNET_AsyncCall(&workRequest);
4297 * This is from windows.
4299 res = ERROR_IO_PENDING;
4301 else
4303 res = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
4304 dwHeaderLength, lpOptional, dwOptionalLength,
4305 dwOptionalLength, TRUE);
4307 lend:
4308 if( lpwhr )
4309 WININET_Release( &lpwhr->hdr );
4311 SetLastError(res);
4312 return res == ERROR_SUCCESS;
4315 /***********************************************************************
4316 * HttpSendRequestA (WININET.@)
4318 * Sends the specified request to the HTTP server
4320 * RETURNS
4321 * TRUE on success
4322 * FALSE on failure
4325 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4326 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4328 BOOL result;
4329 LPWSTR szHeaders=NULL;
4330 DWORD nLen=dwHeaderLength;
4331 if(lpszHeaders!=NULL)
4333 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4334 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4335 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4337 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4338 HeapFree(GetProcessHeap(),0,szHeaders);
4339 return result;
4342 /***********************************************************************
4343 * HTTPSESSION_Destroy (internal)
4345 * Deallocate session handle
4348 static void HTTPSESSION_Destroy(object_header_t *hdr)
4350 http_session_t *lpwhs = (http_session_t*) hdr;
4352 TRACE("%p\n", lpwhs);
4354 WININET_Release(&lpwhs->lpAppInfo->hdr);
4356 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4357 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4358 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4359 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4362 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4364 switch(option) {
4365 case INTERNET_OPTION_HANDLE_TYPE:
4366 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4368 if (*size < sizeof(ULONG))
4369 return ERROR_INSUFFICIENT_BUFFER;
4371 *size = sizeof(DWORD);
4372 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4373 return ERROR_SUCCESS;
4376 return INET_QueryOption(hdr, option, buffer, size, unicode);
4379 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4381 http_session_t *ses = (http_session_t*)hdr;
4383 switch(option) {
4384 case INTERNET_OPTION_USERNAME:
4386 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4387 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4388 return ERROR_SUCCESS;
4390 case INTERNET_OPTION_PASSWORD:
4392 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4393 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4394 return ERROR_SUCCESS;
4396 default: break;
4399 return ERROR_INTERNET_INVALID_OPTION;
4402 static const object_vtbl_t HTTPSESSIONVtbl = {
4403 HTTPSESSION_Destroy,
4404 NULL,
4405 HTTPSESSION_QueryOption,
4406 HTTPSESSION_SetOption,
4407 NULL,
4408 NULL,
4409 NULL,
4410 NULL,
4411 NULL
4415 /***********************************************************************
4416 * HTTP_Connect (internal)
4418 * Create http session handle
4420 * RETURNS
4421 * HINTERNET a session handle on success
4422 * NULL on failure
4425 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4426 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4427 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4428 DWORD dwInternalFlags, HINTERNET *ret)
4430 http_session_t *lpwhs = NULL;
4432 TRACE("-->\n");
4434 if (!lpszServerName || !lpszServerName[0])
4435 return ERROR_INVALID_PARAMETER;
4437 assert( hIC->hdr.htype == WH_HINIT );
4439 lpwhs = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
4440 if (!lpwhs)
4441 return ERROR_OUTOFMEMORY;
4444 * According to my tests. The name is not resolved until a request is sent
4447 lpwhs->hdr.htype = WH_HHTTPSESSION;
4448 lpwhs->hdr.dwFlags = dwFlags;
4449 lpwhs->hdr.dwContext = dwContext;
4450 lpwhs->hdr.dwInternalFlags |= dwInternalFlags;
4452 WININET_AddRef( &hIC->hdr );
4453 lpwhs->lpAppInfo = hIC;
4454 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4456 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4457 if(hIC->lpszProxyBypass)
4458 FIXME("Proxy bypass is ignored.\n");
4460 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4461 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4462 if (lpszUserName && lpszUserName[0])
4463 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4464 if (lpszPassword && lpszPassword[0])
4465 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4466 lpwhs->nServerPort = nServerPort;
4467 lpwhs->nHostPort = nServerPort;
4469 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4470 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4472 INTERNET_SendCallback(&hIC->hdr, dwContext,
4473 INTERNET_STATUS_HANDLE_CREATED, &lpwhs->hdr.hInternet,
4474 sizeof(HINTERNET));
4478 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4479 * windows
4482 TRACE("%p --> %p\n", hIC, lpwhs);
4484 *ret = lpwhs->hdr.hInternet;
4485 return ERROR_SUCCESS;
4489 /***********************************************************************
4490 * HTTP_OpenConnection (internal)
4492 * Connect to a web server
4494 * RETURNS
4496 * TRUE on success
4497 * FALSE on failure
4499 static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
4501 http_session_t *lpwhs;
4502 appinfo_t *hIC = NULL;
4503 char szaddr[INET6_ADDRSTRLEN];
4504 const void *addr;
4505 DWORD res = ERROR_SUCCESS;
4507 TRACE("-->\n");
4510 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4512 res = ERROR_INVALID_PARAMETER;
4513 goto lend;
4516 if (NETCON_connected(&lpwhr->netConnection))
4517 goto lend;
4518 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
4520 lpwhs = lpwhr->lpHttpSession;
4522 hIC = lpwhs->lpAppInfo;
4523 switch (lpwhs->socketAddress.ss_family)
4525 case AF_INET:
4526 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4527 break;
4528 case AF_INET6:
4529 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4530 break;
4531 default:
4532 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4533 return ERROR_INTERNET_NAME_NOT_RESOLVED;
4535 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4536 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4537 INTERNET_STATUS_CONNECTING_TO_SERVER,
4538 szaddr,
4539 strlen(szaddr)+1);
4541 res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
4542 if (res != ERROR_SUCCESS)
4544 WARN("Socket creation failed: %u\n", res);
4545 goto lend;
4548 res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4549 lpwhs->sa_len);
4550 if(res != ERROR_SUCCESS)
4551 goto lend;
4553 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4554 INTERNET_STATUS_CONNECTED_TO_SERVER,
4555 szaddr, strlen(szaddr)+1);
4557 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4559 /* Note: we differ from Microsoft's WinINet here. they seem to have
4560 * a bug that causes no status callbacks to be sent when starting
4561 * a tunnel to a proxy server using the CONNECT verb. i believe our
4562 * behaviour to be more correct and to not cause any incompatibilities
4563 * because using a secure connection through a proxy server is a rare
4564 * case that would be hard for anyone to depend on */
4565 if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS) {
4566 HTTPREQ_CloseConnection(&lpwhr->hdr);
4567 goto lend;
4570 res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
4571 if(res != ERROR_SUCCESS)
4573 WARN("Couldn't connect securely to host\n");
4575 if((lpwhr->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4576 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4577 || res == ERROR_INTERNET_INVALID_CA
4578 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4579 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4580 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4581 || res == ERROR_INTERNET_SEC_INVALID_CERT
4582 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4583 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4585 HTTPREQ_CloseConnection(&lpwhr->hdr);
4586 goto lend;
4591 lend:
4592 lpwhr->read_pos = lpwhr->read_size = 0;
4593 lpwhr->read_chunked = FALSE;
4595 TRACE("%d <--\n", res);
4596 return res;
4600 /***********************************************************************
4601 * HTTP_clear_response_headers (internal)
4603 * clear out any old response headers
4605 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4607 DWORD i;
4609 for( i=0; i<lpwhr->nCustHeaders; i++)
4611 if( !lpwhr->pCustHeaders[i].lpszField )
4612 continue;
4613 if( !lpwhr->pCustHeaders[i].lpszValue )
4614 continue;
4615 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4616 continue;
4617 HTTP_DeleteCustomHeader( lpwhr, i );
4618 i--;
4622 /***********************************************************************
4623 * HTTP_GetResponseHeaders (internal)
4625 * Read server response
4627 * RETURNS
4629 * TRUE on success
4630 * FALSE on error
4632 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4634 INT cbreaks = 0;
4635 WCHAR buffer[MAX_REPLY_LEN];
4636 DWORD buflen = MAX_REPLY_LEN;
4637 BOOL bSuccess = FALSE;
4638 INT rc = 0;
4639 char bufferA[MAX_REPLY_LEN];
4640 LPWSTR status_code = NULL, status_text = NULL;
4641 DWORD cchMaxRawHeaders = 1024;
4642 LPWSTR lpszRawHeaders = NULL;
4643 LPWSTR temp;
4644 DWORD cchRawHeaders = 0;
4645 BOOL codeHundred = FALSE;
4647 TRACE("-->\n");
4649 if (!NETCON_connected(&lpwhr->netConnection))
4650 goto lend;
4652 do {
4653 static const WCHAR szHundred[] = {'1','0','0',0};
4655 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4657 buflen = MAX_REPLY_LEN;
4658 if (!read_line(lpwhr, bufferA, &buflen))
4659 goto lend;
4661 /* clear old response headers (eg. from a redirect response) */
4662 if (clear) {
4663 HTTP_clear_response_headers( lpwhr );
4664 clear = FALSE;
4667 rc += buflen;
4668 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4669 /* check is this a status code line? */
4670 if (!strncmpW(buffer, g_szHttp1_0, 4))
4672 /* split the version from the status code */
4673 status_code = strchrW( buffer, ' ' );
4674 if( !status_code )
4675 goto lend;
4676 *status_code++=0;
4678 /* split the status code from the status text */
4679 status_text = strchrW( status_code, ' ' );
4680 if( !status_text )
4681 goto lend;
4682 *status_text++=0;
4684 TRACE("version [%s] status code [%s] status text [%s]\n",
4685 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4687 codeHundred = (!strcmpW(status_code, szHundred));
4689 else if (!codeHundred)
4691 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
4693 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
4694 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
4696 lpwhr->lpszVersion = heap_strdupW(g_szHttp1_0);
4697 lpwhr->lpszStatusText = heap_strdupW(szOK);
4699 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4700 lpwhr->lpszRawHeaders = heap_strdupW(szDefaultHeader);
4702 bSuccess = TRUE;
4703 goto lend;
4705 } while (codeHundred);
4707 /* Add status code */
4708 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4709 HTTP_ADDHDR_FLAG_REPLACE);
4711 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4712 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4714 lpwhr->lpszVersion = heap_strdupW(buffer);
4715 lpwhr->lpszStatusText = heap_strdupW(status_text);
4717 /* Restore the spaces */
4718 *(status_code-1) = ' ';
4719 *(status_text-1) = ' ';
4721 /* regenerate raw headers */
4722 lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4723 if (!lpszRawHeaders) goto lend;
4725 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4726 cchMaxRawHeaders *= 2;
4727 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4728 if (temp == NULL) goto lend;
4729 lpszRawHeaders = temp;
4730 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4731 cchRawHeaders += (buflen-1);
4732 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4733 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4734 lpszRawHeaders[cchRawHeaders] = '\0';
4736 /* Parse each response line */
4739 buflen = MAX_REPLY_LEN;
4740 if (read_line(lpwhr, bufferA, &buflen))
4742 LPWSTR * pFieldAndValue;
4744 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4746 if (!bufferA[0]) break;
4747 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4749 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4750 if (pFieldAndValue)
4752 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4753 cchMaxRawHeaders *= 2;
4754 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4755 if (temp == NULL) goto lend;
4756 lpszRawHeaders = temp;
4757 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4758 cchRawHeaders += (buflen-1);
4759 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4760 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4761 lpszRawHeaders[cchRawHeaders] = '\0';
4763 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4764 HTTP_ADDREQ_FLAG_ADD );
4766 HTTP_FreeTokens(pFieldAndValue);
4769 else
4771 cbreaks++;
4772 if (cbreaks >= 2)
4773 break;
4775 }while(1);
4777 /* make sure the response header is terminated with an empty line. Some apps really
4778 truly care about that empty line being there for some reason. Just add it to the
4779 header. */
4780 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4782 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4783 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4784 if (temp == NULL) goto lend;
4785 lpszRawHeaders = temp;
4788 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4790 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4791 lpwhr->lpszRawHeaders = lpszRawHeaders;
4792 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4793 bSuccess = TRUE;
4795 lend:
4797 TRACE("<--\n");
4798 if (bSuccess)
4799 return rc;
4800 else
4802 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4803 return 0;
4807 /***********************************************************************
4808 * HTTP_InterpretHttpHeader (internal)
4810 * Parse server response
4812 * RETURNS
4814 * Pointer to array of field, value, NULL on success.
4815 * NULL on error.
4817 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4819 LPWSTR * pTokenPair;
4820 LPWSTR pszColon;
4821 INT len;
4823 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4825 pszColon = strchrW(buffer, ':');
4826 /* must have two tokens */
4827 if (!pszColon)
4829 HTTP_FreeTokens(pTokenPair);
4830 if (buffer[0])
4831 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4832 return NULL;
4835 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4836 if (!pTokenPair[0])
4838 HTTP_FreeTokens(pTokenPair);
4839 return NULL;
4841 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4842 pTokenPair[0][pszColon - buffer] = '\0';
4844 /* skip colon */
4845 pszColon++;
4846 len = strlenW(pszColon);
4847 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4848 if (!pTokenPair[1])
4850 HTTP_FreeTokens(pTokenPair);
4851 return NULL;
4853 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4855 strip_spaces(pTokenPair[0]);
4856 strip_spaces(pTokenPair[1]);
4858 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4859 return pTokenPair;
4862 /***********************************************************************
4863 * HTTP_ProcessHeader (internal)
4865 * Stuff header into header tables according to <dwModifier>
4869 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4871 static DWORD HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4873 LPHTTPHEADERW lphttpHdr = NULL;
4874 INT index = -1;
4875 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4876 DWORD res = ERROR_HTTP_INVALID_HEADER;
4878 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4880 /* REPLACE wins out over ADD */
4881 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4882 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4884 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4885 index = -1;
4886 else
4887 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4889 if (index >= 0)
4891 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4892 return ERROR_HTTP_INVALID_HEADER;
4893 lphttpHdr = &lpwhr->pCustHeaders[index];
4895 else if (value)
4897 HTTPHEADERW hdr;
4899 hdr.lpszField = (LPWSTR)field;
4900 hdr.lpszValue = (LPWSTR)value;
4901 hdr.wFlags = hdr.wCount = 0;
4903 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4904 hdr.wFlags |= HDR_ISREQUEST;
4906 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4908 /* no value to delete */
4909 else return ERROR_SUCCESS;
4911 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4912 lphttpHdr->wFlags |= HDR_ISREQUEST;
4913 else
4914 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4916 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4918 HTTP_DeleteCustomHeader( lpwhr, index );
4920 if (value)
4922 HTTPHEADERW hdr;
4924 hdr.lpszField = (LPWSTR)field;
4925 hdr.lpszValue = (LPWSTR)value;
4926 hdr.wFlags = hdr.wCount = 0;
4928 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4929 hdr.wFlags |= HDR_ISREQUEST;
4931 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4934 return ERROR_SUCCESS;
4936 else if (dwModifier & COALESCEFLAGS)
4938 LPWSTR lpsztmp;
4939 WCHAR ch = 0;
4940 INT len = 0;
4941 INT origlen = strlenW(lphttpHdr->lpszValue);
4942 INT valuelen = strlenW(value);
4944 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4946 ch = ',';
4947 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4949 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4951 ch = ';';
4952 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4955 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4957 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4958 if (lpsztmp)
4960 lphttpHdr->lpszValue = lpsztmp;
4961 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4962 if (ch > 0)
4964 lphttpHdr->lpszValue[origlen] = ch;
4965 origlen++;
4966 lphttpHdr->lpszValue[origlen] = ' ';
4967 origlen++;
4970 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4971 lphttpHdr->lpszValue[len] = '\0';
4972 res = ERROR_SUCCESS;
4974 else
4976 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4977 res = ERROR_OUTOFMEMORY;
4980 TRACE("<-- %d\n", res);
4981 return res;
4985 /***********************************************************************
4986 * HTTP_FinishedReading (internal)
4988 * Called when all content from server has been read by client.
4991 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4993 BOOL keepalive = HTTP_KeepAlive(lpwhr);
4995 TRACE("\n");
4998 if (!keepalive)
5000 HTTPREQ_CloseConnection(&lpwhr->hdr);
5003 /* FIXME: store data in the URL cache here */
5005 return TRUE;
5009 /***********************************************************************
5010 * HTTP_GetCustomHeaderIndex (internal)
5012 * Return index of custom header from header array
5015 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
5016 int requested_index, BOOL request_only)
5018 DWORD index;
5020 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5022 for (index = 0; index < lpwhr->nCustHeaders; index++)
5024 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
5025 continue;
5027 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
5028 continue;
5030 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
5031 continue;
5033 if (requested_index == 0)
5034 break;
5035 requested_index --;
5038 if (index >= lpwhr->nCustHeaders)
5039 index = -1;
5041 TRACE("Return: %d\n", index);
5042 return index;
5046 /***********************************************************************
5047 * HTTP_InsertCustomHeader (internal)
5049 * Insert header into array
5052 static DWORD HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
5054 INT count;
5055 LPHTTPHEADERW lph = NULL;
5057 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5058 count = lpwhr->nCustHeaders + 1;
5059 if (count > 1)
5060 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
5061 else
5062 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
5064 if (!lph)
5065 return ERROR_OUTOFMEMORY;
5067 lpwhr->pCustHeaders = lph;
5068 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
5069 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
5070 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
5071 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
5072 lpwhr->nCustHeaders++;
5074 return ERROR_SUCCESS;
5078 /***********************************************************************
5079 * HTTP_DeleteCustomHeader (internal)
5081 * Delete header from array
5082 * If this function is called, the indexs may change.
5084 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
5086 if( lpwhr->nCustHeaders <= 0 )
5087 return FALSE;
5088 if( index >= lpwhr->nCustHeaders )
5089 return FALSE;
5090 lpwhr->nCustHeaders--;
5092 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
5093 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
5095 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
5096 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
5097 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
5099 return TRUE;
5103 /***********************************************************************
5104 * HTTP_VerifyValidHeader (internal)
5106 * Verify the given header is not invalid for the given http request
5109 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
5111 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5112 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
5113 return ERROR_HTTP_INVALID_HEADER;
5115 return ERROR_SUCCESS;
5118 /***********************************************************************
5119 * IsHostInProxyBypassList (@)
5121 * Undocumented
5124 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
5126 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
5127 return FALSE;
5130 /***********************************************************************
5131 * InternetShowSecurityInfoByURLA (@)
5133 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
5135 FIXME("stub: %s %p\n", url, window);
5136 return FALSE;
5139 /***********************************************************************
5140 * InternetShowSecurityInfoByURLW (@)
5142 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
5144 FIXME("stub: %s %p\n", debugstr_w(url), window);
5145 return FALSE;