push 38a8c0aff9170390b5a3ded41e7cf5b02f3a73d8
[wine/hacks.git] / dlls / wininet / http.c
blob19f854d846356d9a9ebf4df6b7ce239cd4b53931
1 /*
2 * Wininet - Http Implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2002 CodeWeavers Inc.
6 * Copyright 2002 TransGaming Technologies Inc.
7 * Copyright 2004 Mike McCormack for CodeWeavers
8 * Copyright 2005 Aric Stewart for CodeWeavers
9 * Copyright 2006 Robert Shearman for CodeWeavers
11 * Ulrich Czekalla
12 * David Hammerton
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
32 #if defined(__MINGW32__) || defined (_MSC_VER)
33 #include <ws2tcpip.h>
34 #endif
36 #include <sys/types.h>
37 #ifdef HAVE_SYS_SOCKET_H
38 # include <sys/socket.h>
39 #endif
40 #ifdef HAVE_ARPA_INET_H
41 # include <arpa/inet.h>
42 #endif
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #ifdef HAVE_UNISTD_H
47 # include <unistd.h>
48 #endif
49 #include <time.h>
50 #include <assert.h>
51 #ifdef HAVE_ZLIB
52 # include <zlib.h>
53 #endif
55 #include "windef.h"
56 #include "winbase.h"
57 #include "wininet.h"
58 #include "winerror.h"
59 #define NO_SHLWAPI_STREAM
60 #define NO_SHLWAPI_REG
61 #define NO_SHLWAPI_STRFCNS
62 #define NO_SHLWAPI_GDI
63 #include "shlwapi.h"
64 #include "sspi.h"
65 #include "wincrypt.h"
67 #include "internet.h"
68 #include "wine/debug.h"
69 #include "wine/exception.h"
70 #include "wine/unicode.h"
72 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
74 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
75 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
76 static const WCHAR hostW[] = { 'H','o','s','t',0 };
77 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
78 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
79 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
80 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
81 static const WCHAR szGET[] = { 'G','E','T', 0 };
82 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
83 static const WCHAR szCrLf[] = {'\r','\n', 0};
85 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
86 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
87 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
88 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
89 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
90 static const WCHAR szAge[] = { 'A','g','e',0 };
91 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
92 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
93 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
94 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
95 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
96 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
97 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
98 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
99 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
100 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
101 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
102 static const WCHAR szContent_Transfer_Encoding[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
103 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
104 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
105 static const WCHAR szDate[] = { 'D','a','t','e',0 };
106 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
107 static const WCHAR szETag[] = { 'E','T','a','g',0 };
108 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
109 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
110 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
111 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
112 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
113 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
114 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
115 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
116 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
117 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
118 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
119 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
120 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
121 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
122 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
123 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
124 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
125 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
126 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
127 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
128 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
129 static const WCHAR szUnless_Modified_Since[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
130 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
131 static const WCHAR szURI[] = { 'U','R','I',0 };
132 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
133 static const WCHAR szVary[] = { 'V','a','r','y',0 };
134 static const WCHAR szVia[] = { 'V','i','a',0 };
135 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
136 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
138 #define MAXHOSTNAME 100
139 #define MAX_FIELD_VALUE_LEN 256
140 #define MAX_FIELD_LEN 256
142 #define HTTP_REFERER szReferer
143 #define HTTP_ACCEPT szAccept
144 #define HTTP_USERAGENT szUser_Agent
146 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
147 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
148 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
149 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
150 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
151 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
152 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
154 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
156 struct HttpAuthInfo
158 LPWSTR scheme;
159 CredHandle cred;
160 CtxtHandle ctx;
161 TimeStamp exp;
162 ULONG attr;
163 ULONG max_token;
164 void *auth_data;
165 unsigned int auth_data_len;
166 BOOL finished; /* finished authenticating */
170 struct gzip_stream_t {
171 #ifdef HAVE_ZLIB
172 z_stream zstream;
173 #endif
174 BYTE buf[8192];
175 DWORD buf_size;
176 DWORD buf_pos;
177 BOOL end_of_data;
180 typedef struct _authorizationData
182 struct list entry;
184 LPWSTR lpszwHost;
185 LPWSTR lpszwRealm;
186 LPSTR lpszAuthorization;
187 UINT AuthorizationLen;
188 } authorizationData;
190 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
192 static CRITICAL_SECTION authcache_cs;
193 static CRITICAL_SECTION_DEBUG critsect_debug =
195 0, 0, &authcache_cs,
196 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
197 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
199 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
201 static DWORD HTTP_OpenConnection(http_request_t *req);
202 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
203 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
204 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
205 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
206 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
207 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
208 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
209 static BOOL HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
210 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
211 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
212 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
213 static void HTTP_DrainContent(http_request_t *req);
214 static BOOL HTTP_FinishedReading(http_request_t *req);
216 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
218 int HeaderIndex = 0;
219 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
220 if (HeaderIndex == -1)
221 return NULL;
222 else
223 return &req->pCustHeaders[HeaderIndex];
226 #ifdef HAVE_ZLIB
228 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
230 return HeapAlloc(GetProcessHeap(), 0, items*size);
233 static void wininet_zfree(voidpf opaque, voidpf address)
235 HeapFree(GetProcessHeap(), 0, address);
238 static void init_gzip_stream(http_request_t *req)
240 gzip_stream_t *gzip_stream;
241 int index, zres;
243 gzip_stream = HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t));
244 gzip_stream->zstream.zalloc = wininet_zalloc;
245 gzip_stream->zstream.zfree = wininet_zfree;
246 gzip_stream->zstream.opaque = NULL;
247 gzip_stream->zstream.next_in = NULL;
248 gzip_stream->zstream.avail_in = 0;
249 gzip_stream->zstream.next_out = NULL;
250 gzip_stream->zstream.avail_out = 0;
251 gzip_stream->buf_pos = 0;
252 gzip_stream->buf_size = 0;
253 gzip_stream->end_of_data = FALSE;
255 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
256 if(zres != Z_OK) {
257 ERR("inflateInit failed: %d\n", zres);
258 HeapFree(GetProcessHeap(), 0, gzip_stream);
259 return;
262 req->gzip_stream = gzip_stream;
264 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
265 if(index != -1)
266 HTTP_DeleteCustomHeader(req, index);
269 #else
271 static void init_gzip_stream(http_request_t *req)
273 ERR("gzip stream not supported, missing zlib.\n");
276 #endif
278 /* set the request content length based on the headers */
279 static DWORD set_content_length( http_request_t *lpwhr )
281 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
282 WCHAR encoding[20];
283 DWORD size;
285 size = sizeof(lpwhr->dwContentLength);
286 if (!HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
287 &lpwhr->dwContentLength, &size, NULL))
288 lpwhr->dwContentLength = ~0u;
290 size = sizeof(encoding);
291 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) &&
292 !strcmpiW(encoding, szChunked))
294 lpwhr->dwContentLength = ~0u;
295 lpwhr->read_chunked = TRUE;
298 if(lpwhr->decoding) {
299 int encoding_idx;
301 static const WCHAR gzipW[] = {'g','z','i','p',0};
303 encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
304 if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
305 init_gzip_stream(lpwhr);
308 return lpwhr->dwContentLength;
311 /***********************************************************************
312 * HTTP_Tokenize (internal)
314 * Tokenize a string, allocating memory for the tokens.
316 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
318 LPWSTR * token_array;
319 int tokens = 0;
320 int i;
321 LPCWSTR next_token;
323 if (string)
325 /* empty string has no tokens */
326 if (*string)
327 tokens++;
328 /* count tokens */
329 for (i = 0; string[i]; i++)
331 if (!strncmpW(string+i, token_string, strlenW(token_string)))
333 DWORD j;
334 tokens++;
335 /* we want to skip over separators, but not the null terminator */
336 for (j = 0; j < strlenW(token_string) - 1; j++)
337 if (!string[i+j])
338 break;
339 i += j;
344 /* add 1 for terminating NULL */
345 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
346 token_array[tokens] = NULL;
347 if (!tokens)
348 return token_array;
349 for (i = 0; i < tokens; i++)
351 int len;
352 next_token = strstrW(string, token_string);
353 if (!next_token) next_token = string+strlenW(string);
354 len = next_token - string;
355 token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
356 memcpy(token_array[i], string, len*sizeof(WCHAR));
357 token_array[i][len] = '\0';
358 string = next_token+strlenW(token_string);
360 return token_array;
363 /***********************************************************************
364 * HTTP_FreeTokens (internal)
366 * Frees memory returned from HTTP_Tokenize.
368 static void HTTP_FreeTokens(LPWSTR * token_array)
370 int i;
371 for (i = 0; token_array[i]; i++)
372 HeapFree(GetProcessHeap(), 0, token_array[i]);
373 HeapFree(GetProcessHeap(), 0, token_array);
376 static void HTTP_FixURL(http_request_t *lpwhr)
378 static const WCHAR szSlash[] = { '/',0 };
379 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
381 /* If we don't have a path we set it to root */
382 if (NULL == lpwhr->lpszPath)
383 lpwhr->lpszPath = heap_strdupW(szSlash);
384 else /* remove \r and \n*/
386 int nLen = strlenW(lpwhr->lpszPath);
387 while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
389 nLen--;
390 lpwhr->lpszPath[nLen]='\0';
392 /* Replace '\' with '/' */
393 while (nLen>0) {
394 nLen--;
395 if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
399 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
400 lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
401 && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
403 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
404 (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
405 *fixurl = '/';
406 strcpyW(fixurl + 1, lpwhr->lpszPath);
407 HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
408 lpwhr->lpszPath = fixurl;
412 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
414 LPWSTR requestString;
415 DWORD len, n;
416 LPCWSTR *req;
417 UINT i;
418 LPWSTR p;
420 static const WCHAR szSpace[] = { ' ',0 };
421 static const WCHAR szColon[] = { ':',' ',0 };
422 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
424 /* allocate space for an array of all the string pointers to be added */
425 len = (lpwhr->nCustHeaders)*4 + 10;
426 req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
428 /* add the verb, path and HTTP version string */
429 n = 0;
430 req[n++] = verb;
431 req[n++] = szSpace;
432 req[n++] = path;
433 req[n++] = szSpace;
434 req[n++] = version;
436 /* Append custom request headers */
437 for (i = 0; i < lpwhr->nCustHeaders; i++)
439 if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
441 req[n++] = szCrLf;
442 req[n++] = lpwhr->pCustHeaders[i].lpszField;
443 req[n++] = szColon;
444 req[n++] = lpwhr->pCustHeaders[i].lpszValue;
446 TRACE("Adding custom header %s (%s)\n",
447 debugstr_w(lpwhr->pCustHeaders[i].lpszField),
448 debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
452 if( n >= len )
453 ERR("oops. buffer overrun\n");
455 req[n] = NULL;
456 requestString = HTTP_build_req( req, 4 );
457 HeapFree( GetProcessHeap(), 0, req );
460 * Set (header) termination string for request
461 * Make sure there's exactly two new lines at the end of the request
463 p = &requestString[strlenW(requestString)-1];
464 while ( (*p == '\n') || (*p == '\r') )
465 p--;
466 strcpyW( p+1, sztwocrlf );
468 return requestString;
471 static void HTTP_ProcessCookies( http_request_t *lpwhr )
473 int HeaderIndex;
474 int numCookies = 0;
475 LPHTTPHEADERW setCookieHeader;
477 while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
479 setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
481 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
483 int len;
484 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
485 LPWSTR buf_url;
486 LPHTTPHEADERW Host;
488 Host = HTTP_GetHeader(lpwhr, hostW);
489 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
490 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
491 sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
492 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
494 HeapFree(GetProcessHeap(), 0, buf_url);
496 numCookies++;
500 static void strip_spaces(LPWSTR start)
502 LPWSTR str = start;
503 LPWSTR end;
505 while (*str == ' ' && *str != '\0')
506 str++;
508 if (str != start)
509 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
511 end = start + strlenW(start) - 1;
512 while (end >= start && *end == ' ')
514 *end = '\0';
515 end--;
519 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
521 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
522 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
523 BOOL is_basic;
524 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
525 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
526 if (is_basic && pszRealm)
528 LPCWSTR token;
529 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
530 LPCWSTR realm;
531 ptr++;
532 *pszRealm=NULL;
533 token = strchrW(ptr,'=');
534 if (!token)
535 return TRUE;
536 realm = ptr;
537 while (*realm == ' ' && *realm != '\0')
538 realm++;
539 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
540 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
542 token++;
543 while (*token == ' ' && *token != '\0')
544 token++;
545 if (*token == '\0')
546 return TRUE;
547 *pszRealm = heap_strdupW(token);
548 strip_spaces(*pszRealm);
552 return is_basic;
555 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
557 if (!authinfo) return;
559 if (SecIsValidHandle(&authinfo->ctx))
560 DeleteSecurityContext(&authinfo->ctx);
561 if (SecIsValidHandle(&authinfo->cred))
562 FreeCredentialsHandle(&authinfo->cred);
564 HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
565 HeapFree(GetProcessHeap(), 0, authinfo->scheme);
566 HeapFree(GetProcessHeap(), 0, authinfo);
569 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
571 authorizationData *ad;
572 UINT rc = 0;
574 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
576 EnterCriticalSection(&authcache_cs);
577 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, authorizationData, entry)
579 if (!strcmpiW(host,ad->lpszwHost) && !strcmpW(realm,ad->lpszwRealm))
581 TRACE("Authorization found in cache\n");
582 *auth_data = HeapAlloc(GetProcessHeap(),0,ad->AuthorizationLen);
583 memcpy(*auth_data,ad->lpszAuthorization,ad->AuthorizationLen);
584 rc = ad->AuthorizationLen;
585 break;
588 LeaveCriticalSection(&authcache_cs);
589 return rc;
592 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
594 struct list *cursor;
595 authorizationData* ad = NULL;
597 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
599 EnterCriticalSection(&authcache_cs);
600 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
602 authorizationData *check = LIST_ENTRY(cursor,authorizationData,entry);
603 if (!strcmpiW(host,check->lpszwHost) && !strcmpW(realm,check->lpszwRealm))
605 ad = check;
606 break;
610 if (ad)
612 TRACE("Found match in cache, replacing\n");
613 HeapFree(GetProcessHeap(),0,ad->lpszAuthorization);
614 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
615 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
616 ad->AuthorizationLen = auth_data_len;
618 else
620 ad = HeapAlloc(GetProcessHeap(),0,sizeof(authorizationData));
621 ad->lpszwHost = heap_strdupW(host);
622 ad->lpszwRealm = heap_strdupW(realm);
623 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
624 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
625 ad->AuthorizationLen = auth_data_len;
626 list_add_head(&basicAuthorizationCache,&ad->entry);
627 TRACE("authorization cached\n");
629 LeaveCriticalSection(&authcache_cs);
632 static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
633 struct HttpAuthInfo **ppAuthInfo,
634 LPWSTR domain_and_username, LPWSTR password,
635 LPWSTR host )
637 SECURITY_STATUS sec_status;
638 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
639 BOOL first = FALSE;
640 LPWSTR szRealm = NULL;
642 TRACE("%s\n", debugstr_w(pszAuthValue));
644 if (!pAuthInfo)
646 TimeStamp exp;
648 first = TRUE;
649 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
650 if (!pAuthInfo)
651 return FALSE;
653 SecInvalidateHandle(&pAuthInfo->cred);
654 SecInvalidateHandle(&pAuthInfo->ctx);
655 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
656 pAuthInfo->attr = 0;
657 pAuthInfo->auth_data = NULL;
658 pAuthInfo->auth_data_len = 0;
659 pAuthInfo->finished = FALSE;
661 if (is_basic_auth_value(pszAuthValue,NULL))
663 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
664 pAuthInfo->scheme = heap_strdupW(szBasic);
665 if (!pAuthInfo->scheme)
667 HeapFree(GetProcessHeap(), 0, pAuthInfo);
668 return FALSE;
671 else
673 PVOID pAuthData;
674 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
676 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
677 if (!pAuthInfo->scheme)
679 HeapFree(GetProcessHeap(), 0, pAuthInfo);
680 return FALSE;
683 if (domain_and_username)
685 WCHAR *user = strchrW(domain_and_username, '\\');
686 WCHAR *domain = domain_and_username;
688 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
690 pAuthData = &nt_auth_identity;
692 if (user) user++;
693 else
695 user = domain_and_username;
696 domain = NULL;
699 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
700 nt_auth_identity.User = user;
701 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
702 nt_auth_identity.Domain = domain;
703 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
704 nt_auth_identity.Password = password;
705 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
707 else
708 /* use default credentials */
709 pAuthData = NULL;
711 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
712 SECPKG_CRED_OUTBOUND, NULL,
713 pAuthData, NULL,
714 NULL, &pAuthInfo->cred,
715 &exp);
716 if (sec_status == SEC_E_OK)
718 PSecPkgInfoW sec_pkg_info;
719 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
720 if (sec_status == SEC_E_OK)
722 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
723 FreeContextBuffer(sec_pkg_info);
726 if (sec_status != SEC_E_OK)
728 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
729 debugstr_w(pAuthInfo->scheme), sec_status);
730 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
731 HeapFree(GetProcessHeap(), 0, pAuthInfo);
732 return FALSE;
735 *ppAuthInfo = pAuthInfo;
737 else if (pAuthInfo->finished)
738 return FALSE;
740 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
741 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
743 ERR("authentication scheme changed from %s to %s\n",
744 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
745 return FALSE;
748 if (is_basic_auth_value(pszAuthValue,&szRealm))
750 int userlen;
751 int passlen;
752 char *auth_data = NULL;
753 UINT auth_data_len = 0;
755 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
757 if (!domain_and_username)
759 if (host && szRealm)
760 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
761 if (auth_data_len == 0)
763 HeapFree(GetProcessHeap(),0,szRealm);
764 return FALSE;
767 else
769 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
770 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
772 /* length includes a nul terminator, which will be re-used for the ':' */
773 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
774 if (!auth_data)
776 HeapFree(GetProcessHeap(),0,szRealm);
777 return FALSE;
780 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
781 auth_data[userlen] = ':';
782 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
783 auth_data_len = userlen + 1 + passlen;
784 if (host && szRealm)
785 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
788 pAuthInfo->auth_data = auth_data;
789 pAuthInfo->auth_data_len = auth_data_len;
790 pAuthInfo->finished = TRUE;
791 HeapFree(GetProcessHeap(),0,szRealm);
793 return TRUE;
795 else
797 LPCWSTR pszAuthData;
798 SecBufferDesc out_desc, in_desc;
799 SecBuffer out, in;
800 unsigned char *buffer;
801 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
802 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
804 in.BufferType = SECBUFFER_TOKEN;
805 in.cbBuffer = 0;
806 in.pvBuffer = NULL;
808 in_desc.ulVersion = 0;
809 in_desc.cBuffers = 1;
810 in_desc.pBuffers = &in;
812 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
813 if (*pszAuthData == ' ')
815 pszAuthData++;
816 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
817 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
818 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
821 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
823 out.BufferType = SECBUFFER_TOKEN;
824 out.cbBuffer = pAuthInfo->max_token;
825 out.pvBuffer = buffer;
827 out_desc.ulVersion = 0;
828 out_desc.cBuffers = 1;
829 out_desc.pBuffers = &out;
831 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
832 first ? NULL : &pAuthInfo->ctx,
833 first ? lpwhr->lpHttpSession->lpszServerName : NULL,
834 context_req, 0, SECURITY_NETWORK_DREP,
835 in.pvBuffer ? &in_desc : NULL,
836 0, &pAuthInfo->ctx, &out_desc,
837 &pAuthInfo->attr, &pAuthInfo->exp);
838 if (sec_status == SEC_E_OK)
840 pAuthInfo->finished = TRUE;
841 pAuthInfo->auth_data = out.pvBuffer;
842 pAuthInfo->auth_data_len = out.cbBuffer;
843 TRACE("sending last auth packet\n");
845 else if (sec_status == SEC_I_CONTINUE_NEEDED)
847 pAuthInfo->auth_data = out.pvBuffer;
848 pAuthInfo->auth_data_len = out.cbBuffer;
849 TRACE("sending next auth packet\n");
851 else
853 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
854 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
855 destroy_authinfo(pAuthInfo);
856 *ppAuthInfo = NULL;
857 return FALSE;
861 return TRUE;
864 /***********************************************************************
865 * HTTP_HttpAddRequestHeadersW (internal)
867 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
868 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
870 LPWSTR lpszStart;
871 LPWSTR lpszEnd;
872 LPWSTR buffer;
873 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
875 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
877 if( dwHeaderLength == ~0U )
878 len = strlenW(lpszHeader);
879 else
880 len = dwHeaderLength;
881 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
882 lstrcpynW( buffer, lpszHeader, len + 1);
884 lpszStart = buffer;
888 LPWSTR * pFieldAndValue;
890 lpszEnd = lpszStart;
892 while (*lpszEnd != '\0')
894 if (*lpszEnd == '\r' || *lpszEnd == '\n')
895 break;
896 lpszEnd++;
899 if (*lpszStart == '\0')
900 break;
902 if (*lpszEnd == '\r' || *lpszEnd == '\n')
904 *lpszEnd = '\0';
905 lpszEnd++; /* Jump over newline */
907 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
908 if (*lpszStart == '\0')
910 /* Skip 0-length headers */
911 lpszStart = lpszEnd;
912 res = ERROR_SUCCESS;
913 continue;
915 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
916 if (pFieldAndValue)
918 res = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
919 if (res == ERROR_SUCCESS)
920 res = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
921 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
922 HTTP_FreeTokens(pFieldAndValue);
925 lpszStart = lpszEnd;
926 } while (res == ERROR_SUCCESS);
928 HeapFree(GetProcessHeap(), 0, buffer);
930 return res;
933 /***********************************************************************
934 * HttpAddRequestHeadersW (WININET.@)
936 * Adds one or more HTTP header to the request handler
938 * NOTE
939 * On Windows if dwHeaderLength includes the trailing '\0', then
940 * HttpAddRequestHeadersW() adds it too. However this results in an
941 * invalid Http header which is rejected by some servers so we probably
942 * don't need to match Windows on that point.
944 * RETURNS
945 * TRUE on success
946 * FALSE on failure
949 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
950 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
952 http_request_t *lpwhr;
953 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
955 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
957 if (!lpszHeader)
958 return TRUE;
960 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
961 if (lpwhr && lpwhr->hdr.htype == WH_HHTTPREQ)
962 res = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
963 if( lpwhr )
964 WININET_Release( &lpwhr->hdr );
966 if(res != ERROR_SUCCESS)
967 SetLastError(res);
968 return res == ERROR_SUCCESS;
971 /***********************************************************************
972 * HttpAddRequestHeadersA (WININET.@)
974 * Adds one or more HTTP header to the request handler
976 * RETURNS
977 * TRUE on success
978 * FALSE on failure
981 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
982 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
984 DWORD len;
985 LPWSTR hdr;
986 BOOL r;
988 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
990 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
991 hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
992 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
993 if( dwHeaderLength != ~0U )
994 dwHeaderLength = len;
996 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
998 HeapFree( GetProcessHeap(), 0, hdr );
1000 return r;
1003 /***********************************************************************
1004 * HttpOpenRequestA (WININET.@)
1006 * Open a HTTP request handle
1008 * RETURNS
1009 * HINTERNET a HTTP request handle on success
1010 * NULL on failure
1013 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1014 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1015 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1016 DWORD dwFlags, DWORD_PTR dwContext)
1018 LPWSTR szVerb = NULL, szObjectName = NULL;
1019 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1020 INT acceptTypesCount;
1021 HINTERNET rc = FALSE;
1022 LPCSTR *types;
1024 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1025 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1026 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1027 dwFlags, dwContext);
1029 if (lpszVerb)
1031 szVerb = heap_strdupAtoW(lpszVerb);
1032 if ( !szVerb )
1033 goto end;
1036 if (lpszObjectName)
1038 szObjectName = heap_strdupAtoW(lpszObjectName);
1039 if ( !szObjectName )
1040 goto end;
1043 if (lpszVersion)
1045 szVersion = heap_strdupAtoW(lpszVersion);
1046 if ( !szVersion )
1047 goto end;
1050 if (lpszReferrer)
1052 szReferrer = heap_strdupAtoW(lpszReferrer);
1053 if ( !szReferrer )
1054 goto end;
1057 if (lpszAcceptTypes)
1059 acceptTypesCount = 0;
1060 types = lpszAcceptTypes;
1061 while (*types)
1063 __TRY
1065 /* find out how many there are */
1066 if (*types && **types)
1068 TRACE("accept type: %s\n", debugstr_a(*types));
1069 acceptTypesCount++;
1072 __EXCEPT_PAGE_FAULT
1074 WARN("invalid accept type pointer\n");
1076 __ENDTRY;
1077 types++;
1079 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1080 if (!szAcceptTypes) goto end;
1082 acceptTypesCount = 0;
1083 types = lpszAcceptTypes;
1084 while (*types)
1086 __TRY
1088 if (*types && **types)
1089 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1091 __EXCEPT_PAGE_FAULT
1093 /* ignore invalid pointer */
1095 __ENDTRY;
1096 types++;
1098 szAcceptTypes[acceptTypesCount] = NULL;
1101 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1102 szVersion, szReferrer,
1103 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1105 end:
1106 if (szAcceptTypes)
1108 acceptTypesCount = 0;
1109 while (szAcceptTypes[acceptTypesCount])
1111 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1112 acceptTypesCount++;
1114 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1116 HeapFree(GetProcessHeap(), 0, szReferrer);
1117 HeapFree(GetProcessHeap(), 0, szVersion);
1118 HeapFree(GetProcessHeap(), 0, szObjectName);
1119 HeapFree(GetProcessHeap(), 0, szVerb);
1121 return rc;
1124 /***********************************************************************
1125 * HTTP_EncodeBase64
1127 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1129 UINT n = 0, x;
1130 static const CHAR HTTP_Base64Enc[] =
1131 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1133 while( len > 0 )
1135 /* first 6 bits, all from bin[0] */
1136 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1137 x = (bin[0] & 3) << 4;
1139 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1140 if( len == 1 )
1142 base64[n++] = HTTP_Base64Enc[x];
1143 base64[n++] = '=';
1144 base64[n++] = '=';
1145 break;
1147 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1148 x = ( bin[1] & 0x0f ) << 2;
1150 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1151 if( len == 2 )
1153 base64[n++] = HTTP_Base64Enc[x];
1154 base64[n++] = '=';
1155 break;
1157 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1159 /* last 6 bits, all from bin [2] */
1160 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1161 bin += 3;
1162 len -= 3;
1164 base64[n] = 0;
1165 return n;
1168 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1169 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1170 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1171 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1172 static const signed char HTTP_Base64Dec[256] =
1174 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1175 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1176 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1177 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1178 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1179 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1180 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1181 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1182 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1183 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1184 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1185 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1186 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1187 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1188 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1189 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1190 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1191 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1192 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1193 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1194 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1195 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1196 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1197 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1198 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1199 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1201 #undef CH
1203 /***********************************************************************
1204 * HTTP_DecodeBase64
1206 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1208 unsigned int n = 0;
1210 while(*base64)
1212 signed char in[4];
1214 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1215 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1216 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1217 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1219 WARN("invalid base64: %s\n", debugstr_w(base64));
1220 return 0;
1222 if (bin)
1223 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1224 n++;
1226 if ((base64[2] == '=') && (base64[3] == '='))
1227 break;
1228 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1229 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1231 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1232 return 0;
1234 if (bin)
1235 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1236 n++;
1238 if (base64[3] == '=')
1239 break;
1240 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1241 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1243 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1244 return 0;
1246 if (bin)
1247 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1248 n++;
1250 base64 += 4;
1253 return n;
1256 /***********************************************************************
1257 * HTTP_InsertAuthorization
1259 * Insert or delete the authorization field in the request header.
1261 static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1263 if (pAuthInfo)
1265 static const WCHAR wszSpace[] = {' ',0};
1266 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1267 unsigned int len;
1268 WCHAR *authorization = NULL;
1270 if (pAuthInfo->auth_data_len)
1272 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1273 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1274 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1275 if (!authorization)
1276 return FALSE;
1278 strcpyW(authorization, pAuthInfo->scheme);
1279 strcatW(authorization, wszSpace);
1280 HTTP_EncodeBase64(pAuthInfo->auth_data,
1281 pAuthInfo->auth_data_len,
1282 authorization+strlenW(authorization));
1284 /* clear the data as it isn't valid now that it has been sent to the
1285 * server, unless it's Basic authentication which doesn't do
1286 * connection tracking */
1287 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1289 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1290 pAuthInfo->auth_data = NULL;
1291 pAuthInfo->auth_data_len = 0;
1295 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1297 HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1299 HeapFree(GetProcessHeap(), 0, authorization);
1301 return TRUE;
1304 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1306 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1307 DWORD size;
1309 size = sizeof(new_location);
1310 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL))
1312 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1313 strcpyW( url, new_location );
1315 else
1317 static const WCHAR slash[] = { '/',0 };
1318 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1319 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1320 http_session_t *session = req->lpHttpSession;
1322 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1323 size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
1325 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1327 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1328 sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
1329 else
1330 sprintfW( url, format, session->lpszHostName, session->nHostPort );
1331 if (req->lpszPath[0] != '/') strcatW( url, slash );
1332 strcatW( url, req->lpszPath );
1334 TRACE("url=%s\n", debugstr_w(url));
1335 return url;
1338 /***********************************************************************
1339 * HTTP_DealWithProxy
1341 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
1343 WCHAR buf[MAXHOSTNAME];
1344 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1345 static WCHAR szNul[] = { 0 };
1346 URL_COMPONENTSW UrlComponents;
1347 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1348 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1350 memset( &UrlComponents, 0, sizeof UrlComponents );
1351 UrlComponents.dwStructSize = sizeof UrlComponents;
1352 UrlComponents.lpszHostName = buf;
1353 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1355 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1356 hIC->lpszProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1357 sprintfW(proxy, szFormat, hIC->lpszProxy);
1358 else
1359 strcpyW(proxy, hIC->lpszProxy);
1360 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1361 return FALSE;
1362 if( UrlComponents.dwHostNameLength == 0 )
1363 return FALSE;
1365 if( !lpwhr->lpszPath )
1366 lpwhr->lpszPath = szNul;
1368 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1369 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1371 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1372 lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1373 lpwhs->nServerPort = UrlComponents.nPort;
1375 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1376 return TRUE;
1379 #ifndef INET6_ADDRSTRLEN
1380 #define INET6_ADDRSTRLEN 46
1381 #endif
1383 static DWORD HTTP_ResolveName(http_request_t *lpwhr)
1385 char szaddr[INET6_ADDRSTRLEN];
1386 http_session_t *lpwhs = lpwhr->lpHttpSession;
1387 const void *addr;
1389 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1390 INTERNET_STATUS_RESOLVING_NAME,
1391 lpwhs->lpszServerName,
1392 (strlenW(lpwhs->lpszServerName)+1) * sizeof(WCHAR));
1394 lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1395 if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1396 (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1397 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1399 switch (lpwhs->socketAddress.ss_family)
1401 case AF_INET:
1402 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1403 break;
1404 case AF_INET6:
1405 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1406 break;
1407 default:
1408 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1409 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1411 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1412 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1413 INTERNET_STATUS_NAME_RESOLVED,
1414 szaddr, strlen(szaddr)+1);
1416 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1417 return ERROR_SUCCESS;
1421 /***********************************************************************
1422 * HTTPREQ_Destroy (internal)
1424 * Deallocate request handle
1427 static void HTTPREQ_Destroy(object_header_t *hdr)
1429 http_request_t *lpwhr = (http_request_t*) hdr;
1430 DWORD i;
1432 TRACE("\n");
1434 if(lpwhr->hCacheFile)
1435 CloseHandle(lpwhr->hCacheFile);
1437 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1439 DeleteCriticalSection( &lpwhr->read_section );
1440 WININET_Release(&lpwhr->lpHttpSession->hdr);
1442 destroy_authinfo(lpwhr->pAuthInfo);
1443 destroy_authinfo(lpwhr->pProxyAuthInfo);
1445 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1446 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1447 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1448 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1449 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1451 for (i = 0; i < lpwhr->nCustHeaders; i++)
1453 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1454 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1457 #ifdef HAVE_ZLIB
1458 if(lpwhr->gzip_stream) {
1459 if(!lpwhr->gzip_stream->end_of_data)
1460 inflateEnd(&lpwhr->gzip_stream->zstream);
1461 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1463 #endif
1465 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1466 HeapFree(GetProcessHeap(), 0, lpwhr);
1469 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1471 http_request_t *lpwhr = (http_request_t*) hdr;
1473 TRACE("%p\n",lpwhr);
1475 if (!NETCON_connected(&lpwhr->netConnection))
1476 return;
1478 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1479 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1481 NETCON_close(&lpwhr->netConnection);
1483 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1484 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1487 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1489 LPHTTPHEADERW host_header;
1491 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1493 host_header = HTTP_GetHeader(req, hostW);
1494 if(!host_header)
1495 return FALSE;
1497 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1498 return TRUE;
1501 static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
1503 WCHAR szVersion[10];
1504 WCHAR szConnectionResponse[20];
1505 DWORD dwBufferSize = sizeof(szVersion);
1506 BOOL keepalive = FALSE;
1508 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1509 * the connection is keep-alive by default */
1510 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion,
1511 &dwBufferSize, NULL) &&
1512 !strcmpiW(szVersion, g_szHttp1_1))
1514 keepalive = TRUE;
1517 dwBufferSize = sizeof(szConnectionResponse);
1518 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) ||
1519 HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL))
1521 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1524 return keepalive;
1527 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1529 http_request_t *req = (http_request_t*)hdr;
1531 switch(option) {
1532 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1534 http_session_t *lpwhs = req->lpHttpSession;
1535 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1537 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1539 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1540 return ERROR_INSUFFICIENT_BUFFER;
1541 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1542 /* FIXME: can't get a SOCKET from our connection since we don't use
1543 * winsock
1545 info->Socket = 0;
1546 /* FIXME: get source port from req->netConnection */
1547 info->SourcePort = 0;
1548 info->DestPort = lpwhs->nHostPort;
1549 info->Flags = 0;
1550 if (HTTP_KeepAlive(req))
1551 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1552 if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
1553 info->Flags |= IDSI_FLAG_PROXY;
1554 if (req->netConnection.useSSL)
1555 info->Flags |= IDSI_FLAG_SECURE;
1557 return ERROR_SUCCESS;
1560 case INTERNET_OPTION_SECURITY_FLAGS:
1562 http_session_t *lpwhs;
1563 lpwhs = req->lpHttpSession;
1565 if (*size < sizeof(ULONG))
1566 return ERROR_INSUFFICIENT_BUFFER;
1568 *size = sizeof(DWORD);
1569 if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1570 *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1571 else
1572 *(DWORD*)buffer = 0;
1573 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1574 return ERROR_SUCCESS;
1577 case INTERNET_OPTION_HANDLE_TYPE:
1578 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1580 if (*size < sizeof(ULONG))
1581 return ERROR_INSUFFICIENT_BUFFER;
1583 *size = sizeof(DWORD);
1584 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1585 return ERROR_SUCCESS;
1587 case INTERNET_OPTION_URL: {
1588 WCHAR url[INTERNET_MAX_URL_LENGTH];
1589 HTTPHEADERW *host;
1590 DWORD len;
1591 WCHAR *pch;
1593 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1595 TRACE("INTERNET_OPTION_URL\n");
1597 host = HTTP_GetHeader(req, hostW);
1598 strcpyW(url, httpW);
1599 strcatW(url, host->lpszValue);
1600 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1601 *pch = 0;
1602 strcatW(url, req->lpszPath);
1604 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1606 if(unicode) {
1607 len = (strlenW(url)+1) * sizeof(WCHAR);
1608 if(*size < len)
1609 return ERROR_INSUFFICIENT_BUFFER;
1611 *size = len;
1612 strcpyW(buffer, url);
1613 return ERROR_SUCCESS;
1614 }else {
1615 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1616 if(len > *size)
1617 return ERROR_INSUFFICIENT_BUFFER;
1619 *size = len;
1620 return ERROR_SUCCESS;
1624 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1625 INTERNET_CACHE_ENTRY_INFOW *info;
1626 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1627 WCHAR url[INTERNET_MAX_URL_LENGTH];
1628 DWORD nbytes, error;
1629 BOOL ret;
1631 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1633 if (*size < sizeof(*ts))
1635 *size = sizeof(*ts);
1636 return ERROR_INSUFFICIENT_BUFFER;
1638 nbytes = 0;
1639 HTTP_GetRequestURL(req, url);
1640 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1641 error = GetLastError();
1642 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1644 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1645 return ERROR_OUTOFMEMORY;
1647 GetUrlCacheEntryInfoW(url, info, &nbytes);
1649 ts->ftExpires = info->ExpireTime;
1650 ts->ftLastModified = info->LastModifiedTime;
1652 HeapFree(GetProcessHeap(), 0, info);
1653 *size = sizeof(*ts);
1654 return ERROR_SUCCESS;
1656 return error;
1659 case INTERNET_OPTION_DATAFILE_NAME: {
1660 DWORD req_size;
1662 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1664 if(!req->lpszCacheFile) {
1665 *size = 0;
1666 return ERROR_INTERNET_ITEM_NOT_FOUND;
1669 if(unicode) {
1670 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1671 if(*size < req_size)
1672 return ERROR_INSUFFICIENT_BUFFER;
1674 *size = req_size;
1675 memcpy(buffer, req->lpszCacheFile, *size);
1676 return ERROR_SUCCESS;
1677 }else {
1678 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1679 if (req_size > *size)
1680 return ERROR_INSUFFICIENT_BUFFER;
1682 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1683 -1, buffer, *size, NULL, NULL);
1684 return ERROR_SUCCESS;
1688 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1689 PCCERT_CONTEXT context;
1691 if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1692 *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1693 return ERROR_INSUFFICIENT_BUFFER;
1696 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1697 if(context) {
1698 INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1699 DWORD len;
1701 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1702 info->ftExpiry = context->pCertInfo->NotAfter;
1703 info->ftStart = context->pCertInfo->NotBefore;
1704 if(unicode) {
1705 len = CertNameToStrW(context->dwCertEncodingType,
1706 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1707 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1708 if(info->lpszSubjectInfo)
1709 CertNameToStrW(context->dwCertEncodingType,
1710 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1711 info->lpszSubjectInfo, len);
1712 len = CertNameToStrW(context->dwCertEncodingType,
1713 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1714 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1715 if (info->lpszIssuerInfo)
1716 CertNameToStrW(context->dwCertEncodingType,
1717 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1718 info->lpszIssuerInfo, len);
1719 }else {
1720 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1722 len = CertNameToStrA(context->dwCertEncodingType,
1723 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1724 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1725 if(infoA->lpszSubjectInfo)
1726 CertNameToStrA(context->dwCertEncodingType,
1727 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1728 infoA->lpszSubjectInfo, len);
1729 len = CertNameToStrA(context->dwCertEncodingType,
1730 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1731 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1732 if(infoA->lpszIssuerInfo)
1733 CertNameToStrA(context->dwCertEncodingType,
1734 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1735 infoA->lpszIssuerInfo, len);
1739 * Contrary to MSDN, these do not appear to be set.
1740 * lpszProtocolName
1741 * lpszSignatureAlgName
1742 * lpszEncryptionAlgName
1743 * dwKeySize
1745 CertFreeCertificateContext(context);
1746 return ERROR_SUCCESS;
1751 return INET_QueryOption(option, buffer, size, unicode);
1754 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1756 http_request_t *req = (http_request_t*)hdr;
1758 switch(option) {
1759 case INTERNET_OPTION_SEND_TIMEOUT:
1760 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1761 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1763 if (size != sizeof(DWORD))
1764 return ERROR_INVALID_PARAMETER;
1766 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1767 *(DWORD*)buffer);
1769 case INTERNET_OPTION_USERNAME:
1770 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1771 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1772 return ERROR_SUCCESS;
1774 case INTERNET_OPTION_PASSWORD:
1775 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1776 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1777 return ERROR_SUCCESS;
1778 case INTERNET_OPTION_HTTP_DECODING:
1779 if(size != sizeof(BOOL))
1780 return ERROR_INVALID_PARAMETER;
1781 req->decoding = *(BOOL*)buffer;
1782 return ERROR_SUCCESS;
1785 return ERROR_INTERNET_INVALID_OPTION;
1788 /* read some more data into the read buffer (the read section must be held) */
1789 static DWORD read_more_data( http_request_t *req, int maxlen )
1791 DWORD res;
1792 int len;
1794 if (req->read_pos)
1796 /* move existing data to the start of the buffer */
1797 if(req->read_size)
1798 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1799 req->read_pos = 0;
1802 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1804 res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1805 maxlen - req->read_size, 0, &len );
1806 if(res == ERROR_SUCCESS)
1807 req->read_size += len;
1809 return res;
1812 /* remove some amount of data from the read buffer (the read section must be held) */
1813 static void remove_data( http_request_t *req, int count )
1815 if (!(req->read_size -= count)) req->read_pos = 0;
1816 else req->read_pos += count;
1819 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1821 int count, bytes_read, pos = 0;
1822 DWORD res;
1824 EnterCriticalSection( &req->read_section );
1825 for (;;)
1827 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1829 if (eol)
1831 count = eol - (req->read_buf + req->read_pos);
1832 bytes_read = count + 1;
1834 else count = bytes_read = req->read_size;
1836 count = min( count, *len - pos );
1837 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1838 pos += count;
1839 remove_data( req, bytes_read );
1840 if (eol) break;
1842 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
1844 *len = 0;
1845 TRACE( "returning empty string\n" );
1846 LeaveCriticalSection( &req->read_section );
1847 INTERNET_SetLastError(res);
1848 return FALSE;
1851 LeaveCriticalSection( &req->read_section );
1853 if (pos < *len)
1855 if (pos && buffer[pos - 1] == '\r') pos--;
1856 *len = pos + 1;
1858 buffer[*len - 1] = 0;
1859 TRACE( "returning %s\n", debugstr_a(buffer));
1860 return TRUE;
1863 /* discard data contents until we reach end of line (the read section must be held) */
1864 static DWORD discard_eol( http_request_t *req )
1866 DWORD res;
1870 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1871 if (eol)
1873 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
1874 break;
1876 req->read_pos = req->read_size = 0; /* discard everything */
1877 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1878 } while (req->read_size);
1879 return ERROR_SUCCESS;
1882 /* read the size of the next chunk (the read section must be held) */
1883 static DWORD start_next_chunk( http_request_t *req )
1885 DWORD chunk_size = 0, res;
1887 if (!req->dwContentLength) return ERROR_SUCCESS;
1888 if (req->dwContentLength == req->dwContentRead)
1890 /* read terminator for the previous chunk */
1891 if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
1892 req->dwContentLength = ~0u;
1893 req->dwContentRead = 0;
1895 for (;;)
1897 while (req->read_size)
1899 char ch = req->read_buf[req->read_pos];
1900 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
1901 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
1902 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
1903 else if (ch == ';' || ch == '\r' || ch == '\n')
1905 TRACE( "reading %u byte chunk\n", chunk_size );
1906 req->dwContentLength = chunk_size;
1907 req->dwContentRead = 0;
1908 return discard_eol( req );
1910 remove_data( req, 1 );
1912 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1913 if (!req->read_size)
1915 req->dwContentLength = req->dwContentRead = 0;
1916 return ERROR_SUCCESS;
1921 /* check if we have reached the end of the data to read (the read section must be held) */
1922 static BOOL end_of_read_data( http_request_t *req )
1924 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
1925 if (req->read_chunked) return (req->dwContentLength == 0);
1926 if (req->dwContentLength == ~0u) return FALSE;
1927 return (req->dwContentLength == req->dwContentRead);
1930 /* fetch some more data into the read buffer (the read section must be held) */
1931 static DWORD refill_buffer( http_request_t *req )
1933 int len = sizeof(req->read_buf);
1934 DWORD res;
1936 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
1938 if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
1941 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
1942 if (len <= req->read_size) return ERROR_SUCCESS;
1944 if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
1945 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
1946 return ERROR_SUCCESS;
1949 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
1951 DWORD ret = ERROR_SUCCESS;
1952 int read = 0;
1954 #ifdef HAVE_ZLIB
1955 z_stream *zstream = &req->gzip_stream->zstream;
1956 int zres;
1958 while(read < size && !req->gzip_stream->end_of_data) {
1959 if(!req->read_size) {
1960 if(!sync || refill_buffer(req) != ERROR_SUCCESS)
1961 break;
1964 zstream->next_in = req->read_buf+req->read_pos;
1965 zstream->avail_in = req->read_size;
1966 zstream->next_out = buf+read;
1967 zstream->avail_out = size-read;
1968 zres = inflate(zstream, Z_FULL_FLUSH);
1969 read = size - zstream->avail_out;
1970 req->dwContentRead += req->read_size-zstream->avail_in;
1971 remove_data(req, req->read_size-zstream->avail_in);
1972 if(zres == Z_STREAM_END) {
1973 TRACE("end of data\n");
1974 req->gzip_stream->end_of_data = TRUE;
1975 inflateEnd(&req->gzip_stream->zstream);
1976 }else if(zres != Z_OK) {
1977 WARN("inflate failed %d\n", zres);
1978 if(!read)
1979 ret = ERROR_INTERNET_DECODING_FAILED;
1980 break;
1983 #endif
1985 *read_ret = read;
1986 return ret;
1989 static void refill_gzip_buffer(http_request_t *req)
1991 DWORD res;
1992 int len;
1994 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
1995 return;
1997 if(req->gzip_stream->buf_pos) {
1998 if(req->gzip_stream->buf_size)
1999 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2000 req->gzip_stream->buf_pos = 0;
2003 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2004 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2005 if(res == ERROR_SUCCESS)
2006 req->gzip_stream->buf_size += len;
2009 /* return the size of data available to be read immediately (the read section must be held) */
2010 static DWORD get_avail_data( http_request_t *req )
2012 if (req->gzip_stream) {
2013 refill_gzip_buffer(req);
2014 return req->gzip_stream->buf_size;
2016 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2017 return 0;
2018 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2021 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2023 INTERNET_ASYNC_RESULT iar;
2024 DWORD res;
2026 TRACE("%p\n", req);
2028 EnterCriticalSection( &req->read_section );
2029 if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
2030 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2031 iar.dwError = first_notif ? 0 : get_avail_data(req);
2032 }else {
2033 iar.dwResult = 0;
2034 iar.dwError = res;
2036 LeaveCriticalSection( &req->read_section );
2038 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2039 sizeof(INTERNET_ASYNC_RESULT));
2042 /* read data from the http connection (the read section must be held) */
2043 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2045 BOOL finished_reading = FALSE;
2046 int len, bytes_read = 0;
2047 DWORD ret = ERROR_SUCCESS;
2049 EnterCriticalSection( &req->read_section );
2051 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2053 if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
2056 if(req->gzip_stream) {
2057 if(req->gzip_stream->buf_size) {
2058 bytes_read = min(req->gzip_stream->buf_size, size);
2059 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2060 req->gzip_stream->buf_pos += bytes_read;
2061 req->gzip_stream->buf_size -= bytes_read;
2062 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2063 refill_buffer(req);
2066 if(size > bytes_read) {
2067 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2068 if(ret == ERROR_SUCCESS)
2069 bytes_read += len;
2072 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2073 }else {
2074 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2076 if (req->read_size) {
2077 bytes_read = min( req->read_size, size );
2078 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2079 remove_data( req, bytes_read );
2082 if (size > bytes_read && (!bytes_read || sync)) {
2083 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2084 sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2085 bytes_read += len;
2086 /* always return success, even if the network layer returns an error */
2089 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2090 req->dwContentRead += bytes_read;
2092 done:
2093 *read = bytes_read;
2095 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2096 LeaveCriticalSection( &req->read_section );
2098 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2099 BOOL res;
2100 DWORD dwBytesWritten;
2102 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2103 if(!res)
2104 WARN("WriteFile failed: %u\n", GetLastError());
2107 if(finished_reading)
2108 HTTP_FinishedReading(req);
2110 return ret;
2114 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2116 http_request_t *req = (http_request_t*)hdr;
2117 return HTTPREQ_Read(req, buffer, size, read, TRUE);
2120 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2122 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2123 http_request_t *req = (http_request_t*)workRequest->hdr;
2124 INTERNET_ASYNC_RESULT iar;
2125 DWORD res;
2127 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2129 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2130 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2132 iar.dwResult = res == ERROR_SUCCESS;
2133 iar.dwError = res;
2135 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2136 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2137 sizeof(INTERNET_ASYNC_RESULT));
2140 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2141 DWORD flags, DWORD_PTR context)
2143 http_request_t *req = (http_request_t*)hdr;
2144 DWORD res;
2146 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2147 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2149 if (buffers->dwStructSize != sizeof(*buffers))
2150 return ERROR_INVALID_PARAMETER;
2152 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2154 if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
2156 WORKREQUEST workRequest;
2158 if (TryEnterCriticalSection( &req->read_section ))
2160 if (get_avail_data(req))
2162 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2163 &buffers->dwBufferLength, FALSE);
2164 LeaveCriticalSection( &req->read_section );
2165 goto done;
2167 LeaveCriticalSection( &req->read_section );
2170 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2171 workRequest.hdr = WININET_AddRef(&req->hdr);
2172 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2174 INTERNET_AsyncCall(&workRequest);
2176 return ERROR_IO_PENDING;
2179 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2180 !(flags & IRF_NO_WAIT));
2182 done:
2183 if (res == ERROR_SUCCESS) {
2184 DWORD size = buffers->dwBufferLength;
2185 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2186 &size, sizeof(size));
2189 return res;
2192 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2194 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2195 http_request_t *req = (http_request_t*)workRequest->hdr;
2196 INTERNET_ASYNC_RESULT iar;
2197 DWORD res;
2199 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2201 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2202 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2204 iar.dwResult = res == ERROR_SUCCESS;
2205 iar.dwError = res;
2207 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2208 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2209 sizeof(INTERNET_ASYNC_RESULT));
2212 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2213 DWORD flags, DWORD_PTR context)
2216 http_request_t *req = (http_request_t*)hdr;
2217 DWORD res;
2219 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2220 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2222 if (buffers->dwStructSize != sizeof(*buffers))
2223 return ERROR_INVALID_PARAMETER;
2225 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2227 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2229 WORKREQUEST workRequest;
2231 if (TryEnterCriticalSection( &req->read_section ))
2233 if (get_avail_data(req))
2235 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2236 &buffers->dwBufferLength, FALSE);
2237 LeaveCriticalSection( &req->read_section );
2238 goto done;
2240 LeaveCriticalSection( &req->read_section );
2243 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2244 workRequest.hdr = WININET_AddRef(&req->hdr);
2245 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2247 INTERNET_AsyncCall(&workRequest);
2249 return ERROR_IO_PENDING;
2252 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2253 !(flags & IRF_NO_WAIT));
2255 done:
2256 if (res == ERROR_SUCCESS) {
2257 DWORD size = buffers->dwBufferLength;
2258 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2259 &size, sizeof(size));
2262 return res;
2265 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2267 DWORD res;
2268 http_request_t *lpwhr = (http_request_t*)hdr;
2270 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2272 *written = 0;
2273 res = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written);
2274 if (res == ERROR_SUCCESS)
2275 lpwhr->dwBytesWritten += *written;
2277 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2278 return res;
2281 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2283 http_request_t *req = (http_request_t*)workRequest->hdr;
2285 HTTP_ReceiveRequestData(req, FALSE);
2288 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2290 http_request_t *req = (http_request_t*)hdr;
2292 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2294 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2296 WORKREQUEST workRequest;
2298 /* never wait, if we can't enter the section we queue an async request right away */
2299 if (TryEnterCriticalSection( &req->read_section ))
2301 if ((*available = get_avail_data( req ))) goto done;
2302 if (end_of_read_data( req )) goto done;
2303 LeaveCriticalSection( &req->read_section );
2306 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2307 workRequest.hdr = WININET_AddRef( &req->hdr );
2309 INTERNET_AsyncCall(&workRequest);
2311 return ERROR_IO_PENDING;
2314 EnterCriticalSection( &req->read_section );
2316 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2318 refill_buffer( req );
2319 *available = get_avail_data( req );
2322 done:
2323 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2325 DWORD extra;
2326 if (NETCON_query_data_available(&req->netConnection, &extra))
2327 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2329 LeaveCriticalSection( &req->read_section );
2331 TRACE( "returning %u\n", *available );
2332 return ERROR_SUCCESS;
2335 static const object_vtbl_t HTTPREQVtbl = {
2336 HTTPREQ_Destroy,
2337 HTTPREQ_CloseConnection,
2338 HTTPREQ_QueryOption,
2339 HTTPREQ_SetOption,
2340 HTTPREQ_ReadFile,
2341 HTTPREQ_ReadFileExA,
2342 HTTPREQ_ReadFileExW,
2343 HTTPREQ_WriteFile,
2344 HTTPREQ_QueryDataAvailable,
2345 NULL
2348 /***********************************************************************
2349 * HTTP_HttpOpenRequestW (internal)
2351 * Open a HTTP request handle
2353 * RETURNS
2354 * HINTERNET a HTTP request handle on success
2355 * NULL on failure
2358 static DWORD HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2359 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2360 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2361 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2363 appinfo_t *hIC = NULL;
2364 http_request_t *lpwhr;
2365 LPWSTR lpszHostName = NULL;
2366 HINTERNET handle = NULL;
2367 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2368 DWORD len, res;
2370 TRACE("-->\n");
2372 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2373 hIC = lpwhs->lpAppInfo;
2375 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2376 if (NULL == lpwhr)
2378 res = ERROR_OUTOFMEMORY;
2379 goto lend;
2381 lpwhr->hdr.htype = WH_HHTTPREQ;
2382 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2383 lpwhr->hdr.dwFlags = dwFlags;
2384 lpwhr->hdr.dwContext = dwContext;
2385 lpwhr->hdr.refs = 1;
2386 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2387 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2388 lpwhr->dwContentLength = ~0u;
2389 InitializeCriticalSection( &lpwhr->read_section );
2391 WININET_AddRef( &lpwhs->hdr );
2392 lpwhr->lpHttpSession = lpwhs;
2393 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2395 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2396 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2397 if (NULL == lpszHostName)
2399 res = ERROR_OUTOFMEMORY;
2400 goto lend;
2403 handle = WININET_AllocHandle( &lpwhr->hdr );
2404 if (NULL == handle)
2406 res = ERROR_OUTOFMEMORY;
2407 goto lend;
2410 if ((res = NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2412 InternetCloseHandle( handle );
2413 handle = NULL;
2414 goto lend;
2417 if (lpszObjectName && *lpszObjectName) {
2418 HRESULT rc;
2420 len = 0;
2421 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2422 if (rc != E_POINTER)
2423 len = strlenW(lpszObjectName)+1;
2424 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2425 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2426 URL_ESCAPE_SPACES_ONLY);
2427 if (rc != S_OK)
2429 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2430 strcpyW(lpwhr->lpszPath,lpszObjectName);
2432 }else {
2433 static const WCHAR slashW[] = {'/',0};
2435 lpwhr->lpszPath = heap_strdupW(slashW);
2438 if (lpszReferrer && *lpszReferrer)
2439 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2441 if (lpszAcceptTypes)
2443 int i;
2444 for (i = 0; lpszAcceptTypes[i]; i++)
2446 if (!*lpszAcceptTypes[i]) continue;
2447 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2448 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2449 HTTP_ADDHDR_FLAG_REQ |
2450 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2454 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2455 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2457 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2458 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2459 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2461 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2462 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2463 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2465 else
2466 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2467 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2469 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2470 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2471 INTERNET_DEFAULT_HTTPS_PORT :
2472 INTERNET_DEFAULT_HTTP_PORT);
2474 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2475 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2476 INTERNET_DEFAULT_HTTPS_PORT :
2477 INTERNET_DEFAULT_HTTP_PORT);
2479 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2480 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2482 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2483 INTERNET_STATUS_HANDLE_CREATED, &handle,
2484 sizeof(handle));
2486 lend:
2487 HeapFree(GetProcessHeap(), 0, lpszHostName);
2488 if( lpwhr )
2489 WININET_Release( &lpwhr->hdr );
2491 TRACE("<-- %p (%p)\n", handle, lpwhr);
2492 *ret = handle;
2493 return res;
2496 /***********************************************************************
2497 * HttpOpenRequestW (WININET.@)
2499 * Open a HTTP request handle
2501 * RETURNS
2502 * HINTERNET a HTTP request handle on success
2503 * NULL on failure
2506 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2507 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2508 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2509 DWORD dwFlags, DWORD_PTR dwContext)
2511 http_session_t *lpwhs;
2512 HINTERNET handle = NULL;
2513 DWORD res;
2515 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2516 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2517 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2518 dwFlags, dwContext);
2519 if(lpszAcceptTypes!=NULL)
2521 int i;
2522 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2523 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2526 lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
2527 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
2529 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2530 goto lend;
2534 * My tests seem to show that the windows version does not
2535 * become asynchronous until after this point. And anyhow
2536 * if this call was asynchronous then how would you get the
2537 * necessary HINTERNET pointer returned by this function.
2540 res = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
2541 lpszVersion, lpszReferrer, lpszAcceptTypes,
2542 dwFlags, dwContext, &handle);
2543 lend:
2544 if( lpwhs )
2545 WININET_Release( &lpwhs->hdr );
2546 TRACE("returning %p\n", handle);
2547 if(res != ERROR_SUCCESS)
2548 SetLastError(res);
2549 return handle;
2552 /* read any content returned by the server so that the connection can be
2553 * reused */
2554 static void HTTP_DrainContent(http_request_t *req)
2556 DWORD bytes_read;
2558 if (!NETCON_connected(&req->netConnection)) return;
2560 if (req->dwContentLength == -1)
2562 NETCON_close(&req->netConnection);
2563 return;
2565 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2569 char buffer[2048];
2570 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2571 return;
2572 } while (bytes_read);
2575 static const LPCWSTR header_lookup[] = {
2576 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2577 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2578 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2579 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2580 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2581 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2582 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2583 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2584 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2585 szDate, /* HTTP_QUERY_DATE = 9 */
2586 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2587 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2588 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2589 szURI, /* HTTP_QUERY_URI = 13 */
2590 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2591 NULL, /* HTTP_QUERY_COST = 15 */
2592 NULL, /* HTTP_QUERY_LINK = 16 */
2593 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2594 NULL, /* HTTP_QUERY_VERSION = 18 */
2595 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2596 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2597 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2598 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2599 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2600 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2601 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2602 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2603 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2604 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2605 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2606 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2607 NULL, /* HTTP_QUERY_FROM = 31 */
2608 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2609 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2610 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2611 szReferer, /* HTTP_QUERY_REFERER = 35 */
2612 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2613 szServer, /* HTTP_QUERY_SERVER = 37 */
2614 NULL, /* HTTP_TITLE = 38 */
2615 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2616 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2617 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2618 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2619 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2620 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2621 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2622 NULL, /* HTTP_QUERY_REFRESH = 46 */
2623 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2624 szAge, /* HTTP_QUERY_AGE = 48 */
2625 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2626 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2627 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2628 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2629 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2630 szETag, /* HTTP_QUERY_ETAG = 54 */
2631 hostW, /* HTTP_QUERY_HOST = 55 */
2632 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2633 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2634 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2635 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2636 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2637 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2638 szRange, /* HTTP_QUERY_RANGE = 62 */
2639 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2640 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2641 szVary, /* HTTP_QUERY_VARY = 65 */
2642 szVia, /* HTTP_QUERY_VIA = 66 */
2643 szWarning, /* HTTP_QUERY_WARNING = 67 */
2644 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2645 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2646 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2649 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2651 /***********************************************************************
2652 * HTTP_HttpQueryInfoW (internal)
2654 static BOOL HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2655 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2657 LPHTTPHEADERW lphttpHdr = NULL;
2658 BOOL bSuccess = FALSE;
2659 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2660 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2661 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2662 INT index = -1;
2664 /* Find requested header structure */
2665 switch (level)
2667 case HTTP_QUERY_CUSTOM:
2668 if (!lpBuffer) return FALSE;
2669 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2670 break;
2671 case HTTP_QUERY_RAW_HEADERS_CRLF:
2673 LPWSTR headers;
2674 DWORD len = 0;
2675 BOOL ret = FALSE;
2677 if (request_only)
2678 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2679 else
2680 headers = lpwhr->lpszRawHeaders;
2682 if (headers)
2683 len = strlenW(headers) * sizeof(WCHAR);
2685 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2687 len += sizeof(WCHAR);
2688 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2689 ret = FALSE;
2691 else if (lpBuffer)
2693 if (headers)
2694 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2695 else
2697 len = strlenW(szCrLf) * sizeof(WCHAR);
2698 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2700 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2701 ret = TRUE;
2703 *lpdwBufferLength = len;
2705 if (request_only)
2706 HeapFree(GetProcessHeap(), 0, headers);
2707 return ret;
2709 case HTTP_QUERY_RAW_HEADERS:
2711 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2712 DWORD i, size = 0;
2713 LPWSTR pszString = lpBuffer;
2715 for (i = 0; ppszRawHeaderLines[i]; i++)
2716 size += strlenW(ppszRawHeaderLines[i]) + 1;
2718 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2720 HTTP_FreeTokens(ppszRawHeaderLines);
2721 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2722 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2723 return FALSE;
2725 if (pszString)
2727 for (i = 0; ppszRawHeaderLines[i]; i++)
2729 DWORD len = strlenW(ppszRawHeaderLines[i]);
2730 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2731 pszString += len+1;
2733 *pszString = '\0';
2734 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2736 *lpdwBufferLength = size * sizeof(WCHAR);
2737 HTTP_FreeTokens(ppszRawHeaderLines);
2739 return TRUE;
2741 case HTTP_QUERY_STATUS_TEXT:
2742 if (lpwhr->lpszStatusText)
2744 DWORD len = strlenW(lpwhr->lpszStatusText);
2745 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2747 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2748 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2749 return FALSE;
2751 if (lpBuffer)
2753 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2754 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2756 *lpdwBufferLength = len * sizeof(WCHAR);
2757 return TRUE;
2759 break;
2760 case HTTP_QUERY_VERSION:
2761 if (lpwhr->lpszVersion)
2763 DWORD len = strlenW(lpwhr->lpszVersion);
2764 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2766 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2767 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2768 return FALSE;
2770 if (lpBuffer)
2772 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2773 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2775 *lpdwBufferLength = len * sizeof(WCHAR);
2776 return TRUE;
2778 break;
2779 case HTTP_QUERY_CONTENT_ENCODING:
2780 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2781 requested_index,request_only);
2782 break;
2783 default:
2784 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2786 if (level < LAST_TABLE_HEADER && header_lookup[level])
2787 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2788 requested_index,request_only);
2791 if (index >= 0)
2792 lphttpHdr = &lpwhr->pCustHeaders[index];
2794 /* Ensure header satisfies requested attributes */
2795 if (!lphttpHdr ||
2796 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2797 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2799 INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
2800 return bSuccess;
2803 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2805 /* coalesce value to requested type */
2806 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2808 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2809 TRACE(" returning number: %d\n", *(int *)lpBuffer);
2810 bSuccess = TRUE;
2812 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2814 time_t tmpTime;
2815 struct tm tmpTM;
2816 SYSTEMTIME *STHook;
2818 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2820 tmpTM = *gmtime(&tmpTime);
2821 STHook = (SYSTEMTIME *)lpBuffer;
2822 STHook->wDay = tmpTM.tm_mday;
2823 STHook->wHour = tmpTM.tm_hour;
2824 STHook->wMilliseconds = 0;
2825 STHook->wMinute = tmpTM.tm_min;
2826 STHook->wDayOfWeek = tmpTM.tm_wday;
2827 STHook->wMonth = tmpTM.tm_mon + 1;
2828 STHook->wSecond = tmpTM.tm_sec;
2829 STHook->wYear = tmpTM.tm_year;
2830 bSuccess = TRUE;
2832 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
2833 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
2834 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
2836 else if (lphttpHdr->lpszValue)
2838 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
2840 if (len > *lpdwBufferLength)
2842 *lpdwBufferLength = len;
2843 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2844 return bSuccess;
2846 if (lpBuffer)
2848 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
2849 TRACE(" returning string: %s\n", debugstr_w(lpBuffer));
2851 *lpdwBufferLength = len - sizeof(WCHAR);
2852 bSuccess = TRUE;
2854 return bSuccess;
2857 /***********************************************************************
2858 * HttpQueryInfoW (WININET.@)
2860 * Queries for information about an HTTP request
2862 * RETURNS
2863 * TRUE on success
2864 * FALSE on failure
2867 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
2868 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2870 BOOL bSuccess = FALSE;
2871 http_request_t *lpwhr;
2873 if (TRACE_ON(wininet)) {
2874 #define FE(x) { x, #x }
2875 static const wininet_flag_info query_flags[] = {
2876 FE(HTTP_QUERY_MIME_VERSION),
2877 FE(HTTP_QUERY_CONTENT_TYPE),
2878 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
2879 FE(HTTP_QUERY_CONTENT_ID),
2880 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
2881 FE(HTTP_QUERY_CONTENT_LENGTH),
2882 FE(HTTP_QUERY_CONTENT_LANGUAGE),
2883 FE(HTTP_QUERY_ALLOW),
2884 FE(HTTP_QUERY_PUBLIC),
2885 FE(HTTP_QUERY_DATE),
2886 FE(HTTP_QUERY_EXPIRES),
2887 FE(HTTP_QUERY_LAST_MODIFIED),
2888 FE(HTTP_QUERY_MESSAGE_ID),
2889 FE(HTTP_QUERY_URI),
2890 FE(HTTP_QUERY_DERIVED_FROM),
2891 FE(HTTP_QUERY_COST),
2892 FE(HTTP_QUERY_LINK),
2893 FE(HTTP_QUERY_PRAGMA),
2894 FE(HTTP_QUERY_VERSION),
2895 FE(HTTP_QUERY_STATUS_CODE),
2896 FE(HTTP_QUERY_STATUS_TEXT),
2897 FE(HTTP_QUERY_RAW_HEADERS),
2898 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
2899 FE(HTTP_QUERY_CONNECTION),
2900 FE(HTTP_QUERY_ACCEPT),
2901 FE(HTTP_QUERY_ACCEPT_CHARSET),
2902 FE(HTTP_QUERY_ACCEPT_ENCODING),
2903 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
2904 FE(HTTP_QUERY_AUTHORIZATION),
2905 FE(HTTP_QUERY_CONTENT_ENCODING),
2906 FE(HTTP_QUERY_FORWARDED),
2907 FE(HTTP_QUERY_FROM),
2908 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
2909 FE(HTTP_QUERY_LOCATION),
2910 FE(HTTP_QUERY_ORIG_URI),
2911 FE(HTTP_QUERY_REFERER),
2912 FE(HTTP_QUERY_RETRY_AFTER),
2913 FE(HTTP_QUERY_SERVER),
2914 FE(HTTP_QUERY_TITLE),
2915 FE(HTTP_QUERY_USER_AGENT),
2916 FE(HTTP_QUERY_WWW_AUTHENTICATE),
2917 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
2918 FE(HTTP_QUERY_ACCEPT_RANGES),
2919 FE(HTTP_QUERY_SET_COOKIE),
2920 FE(HTTP_QUERY_COOKIE),
2921 FE(HTTP_QUERY_REQUEST_METHOD),
2922 FE(HTTP_QUERY_REFRESH),
2923 FE(HTTP_QUERY_CONTENT_DISPOSITION),
2924 FE(HTTP_QUERY_AGE),
2925 FE(HTTP_QUERY_CACHE_CONTROL),
2926 FE(HTTP_QUERY_CONTENT_BASE),
2927 FE(HTTP_QUERY_CONTENT_LOCATION),
2928 FE(HTTP_QUERY_CONTENT_MD5),
2929 FE(HTTP_QUERY_CONTENT_RANGE),
2930 FE(HTTP_QUERY_ETAG),
2931 FE(HTTP_QUERY_HOST),
2932 FE(HTTP_QUERY_IF_MATCH),
2933 FE(HTTP_QUERY_IF_NONE_MATCH),
2934 FE(HTTP_QUERY_IF_RANGE),
2935 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
2936 FE(HTTP_QUERY_MAX_FORWARDS),
2937 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
2938 FE(HTTP_QUERY_RANGE),
2939 FE(HTTP_QUERY_TRANSFER_ENCODING),
2940 FE(HTTP_QUERY_UPGRADE),
2941 FE(HTTP_QUERY_VARY),
2942 FE(HTTP_QUERY_VIA),
2943 FE(HTTP_QUERY_WARNING),
2944 FE(HTTP_QUERY_CUSTOM)
2946 static const wininet_flag_info modifier_flags[] = {
2947 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
2948 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
2949 FE(HTTP_QUERY_FLAG_NUMBER),
2950 FE(HTTP_QUERY_FLAG_COALESCE)
2952 #undef FE
2953 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
2954 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
2955 DWORD i;
2957 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
2958 TRACE(" Attribute:");
2959 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
2960 if (query_flags[i].val == info) {
2961 TRACE(" %s", query_flags[i].name);
2962 break;
2965 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
2966 TRACE(" Unknown (%08x)", info);
2969 TRACE(" Modifier:");
2970 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
2971 if (modifier_flags[i].val & info_mod) {
2972 TRACE(" %s", modifier_flags[i].name);
2973 info_mod &= ~ modifier_flags[i].val;
2977 if (info_mod) {
2978 TRACE(" Unknown (%08x)", info_mod);
2980 TRACE("\n");
2983 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
2984 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
2986 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2987 goto lend;
2990 if (lpBuffer == NULL)
2991 *lpdwBufferLength = 0;
2992 bSuccess = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
2993 lpBuffer, lpdwBufferLength, lpdwIndex);
2995 lend:
2996 if( lpwhr )
2997 WININET_Release( &lpwhr->hdr );
2999 TRACE("%d <--\n", bSuccess);
3000 return bSuccess;
3003 /***********************************************************************
3004 * HttpQueryInfoA (WININET.@)
3006 * Queries for information about an HTTP request
3008 * RETURNS
3009 * TRUE on success
3010 * FALSE on failure
3013 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3014 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3016 BOOL result;
3017 DWORD len;
3018 WCHAR* bufferW;
3020 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3021 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3023 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3024 lpdwBufferLength, lpdwIndex );
3027 if (lpBuffer)
3029 DWORD alloclen;
3030 len = (*lpdwBufferLength)*sizeof(WCHAR);
3031 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3033 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3034 if (alloclen < len)
3035 alloclen = len;
3037 else
3038 alloclen = len;
3039 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3040 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3041 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3042 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3043 } else
3045 bufferW = NULL;
3046 len = 0;
3049 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3050 &len, lpdwIndex );
3051 if( result )
3053 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3054 lpBuffer, *lpdwBufferLength, NULL, NULL );
3055 *lpdwBufferLength = len - 1;
3057 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3059 else
3060 /* since the strings being returned from HttpQueryInfoW should be
3061 * only ASCII characters, it is reasonable to assume that all of
3062 * the Unicode characters can be reduced to a single byte */
3063 *lpdwBufferLength = len / sizeof(WCHAR);
3065 HeapFree(GetProcessHeap(), 0, bufferW );
3067 return result;
3070 /***********************************************************************
3071 * HTTP_GetRedirectURL (internal)
3073 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3075 static WCHAR szHttp[] = {'h','t','t','p',0};
3076 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3077 http_session_t *lpwhs = lpwhr->lpHttpSession;
3078 URL_COMPONENTSW urlComponents;
3079 DWORD url_length = 0;
3080 LPWSTR orig_url;
3081 LPWSTR combined_url;
3083 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3084 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3085 urlComponents.dwSchemeLength = 0;
3086 urlComponents.lpszHostName = lpwhs->lpszHostName;
3087 urlComponents.dwHostNameLength = 0;
3088 urlComponents.nPort = lpwhs->nHostPort;
3089 urlComponents.lpszUserName = lpwhs->lpszUserName;
3090 urlComponents.dwUserNameLength = 0;
3091 urlComponents.lpszPassword = NULL;
3092 urlComponents.dwPasswordLength = 0;
3093 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3094 urlComponents.dwUrlPathLength = 0;
3095 urlComponents.lpszExtraInfo = NULL;
3096 urlComponents.dwExtraInfoLength = 0;
3098 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3099 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3100 return NULL;
3102 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3104 /* convert from bytes to characters */
3105 url_length = url_length / sizeof(WCHAR) - 1;
3106 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3108 HeapFree(GetProcessHeap(), 0, orig_url);
3109 return NULL;
3112 url_length = 0;
3113 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3114 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3116 HeapFree(GetProcessHeap(), 0, orig_url);
3117 return NULL;
3119 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3121 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3123 HeapFree(GetProcessHeap(), 0, orig_url);
3124 HeapFree(GetProcessHeap(), 0, combined_url);
3125 return NULL;
3127 HeapFree(GetProcessHeap(), 0, orig_url);
3128 return combined_url;
3132 /***********************************************************************
3133 * HTTP_HandleRedirect (internal)
3135 static DWORD HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3137 http_session_t *lpwhs = lpwhr->lpHttpSession;
3138 appinfo_t *hIC = lpwhs->lpAppInfo;
3139 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3140 WCHAR path[INTERNET_MAX_URL_LENGTH];
3141 int index;
3143 if(lpszUrl[0]=='/')
3145 /* if it's an absolute path, keep the same session info */
3146 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3148 else
3150 URL_COMPONENTSW urlComponents;
3151 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3152 static WCHAR szHttp[] = {'h','t','t','p',0};
3153 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3155 userName[0] = 0;
3156 hostName[0] = 0;
3157 protocol[0] = 0;
3159 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3160 urlComponents.lpszScheme = protocol;
3161 urlComponents.dwSchemeLength = 32;
3162 urlComponents.lpszHostName = hostName;
3163 urlComponents.dwHostNameLength = MAXHOSTNAME;
3164 urlComponents.lpszUserName = userName;
3165 urlComponents.dwUserNameLength = 1024;
3166 urlComponents.lpszPassword = NULL;
3167 urlComponents.dwPasswordLength = 0;
3168 urlComponents.lpszUrlPath = path;
3169 urlComponents.dwUrlPathLength = 2048;
3170 urlComponents.lpszExtraInfo = NULL;
3171 urlComponents.dwExtraInfoLength = 0;
3172 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3173 return INTERNET_GetLastError();
3175 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3176 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3178 TRACE("redirect from secure page to non-secure page\n");
3179 /* FIXME: warn about from secure redirect to non-secure page */
3180 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3182 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3183 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3185 TRACE("redirect from non-secure page to secure page\n");
3186 /* FIXME: notify about redirect to secure page */
3187 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3190 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3192 if (lstrlenW(protocol)>4) /*https*/
3193 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3194 else /*http*/
3195 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3198 #if 0
3200 * This upsets redirects to binary files on sourceforge.net
3201 * and gives an html page instead of the target file
3202 * Examination of the HTTP request sent by native wininet.dll
3203 * reveals that it doesn't send a referrer in that case.
3204 * Maybe there's a flag that enables this, or maybe a referrer
3205 * shouldn't be added in case of a redirect.
3208 /* consider the current host as the referrer */
3209 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3210 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3211 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3212 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3213 #endif
3215 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3216 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3217 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3219 int len;
3220 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3221 len = lstrlenW(hostName);
3222 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3223 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3224 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3226 else
3227 lpwhs->lpszHostName = heap_strdupW(hostName);
3229 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3231 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3232 lpwhs->lpszUserName = NULL;
3233 if (userName[0])
3234 lpwhs->lpszUserName = heap_strdupW(userName);
3236 if (!using_proxy)
3238 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3240 DWORD res;
3242 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3243 lpwhs->lpszServerName = heap_strdupW(hostName);
3244 lpwhs->nServerPort = urlComponents.nPort;
3246 NETCON_close(&lpwhr->netConnection);
3247 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS)
3248 return res;
3250 res = NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE);
3251 if (res != ERROR_SUCCESS)
3252 return res;
3254 lpwhr->read_pos = lpwhr->read_size = 0;
3255 lpwhr->read_chunked = FALSE;
3258 else
3259 TRACE("Redirect through proxy\n");
3262 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3263 lpwhr->lpszPath=NULL;
3264 if (*path)
3266 DWORD needed = 0;
3267 HRESULT rc;
3269 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3270 if (rc != E_POINTER)
3271 needed = strlenW(path)+1;
3272 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3273 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3274 URL_ESCAPE_SPACES_ONLY);
3275 if (rc != S_OK)
3277 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3278 strcpyW(lpwhr->lpszPath,path);
3282 /* Remove custom content-type/length headers on redirects. */
3283 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3284 if (0 <= index)
3285 HTTP_DeleteCustomHeader(lpwhr, index);
3286 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3287 if (0 <= index)
3288 HTTP_DeleteCustomHeader(lpwhr, index);
3290 return ERROR_SUCCESS;
3293 /***********************************************************************
3294 * HTTP_build_req (internal)
3296 * concatenate all the strings in the request together
3298 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3300 LPCWSTR *t;
3301 LPWSTR str;
3303 for( t = list; *t ; t++ )
3304 len += strlenW( *t );
3305 len++;
3307 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3308 *str = 0;
3310 for( t = list; *t ; t++ )
3311 strcatW( str, *t );
3313 return str;
3316 static DWORD HTTP_SecureProxyConnect(http_request_t *lpwhr)
3318 LPWSTR lpszPath;
3319 LPWSTR requestString;
3320 INT len;
3321 INT cnt;
3322 INT responseLen;
3323 char *ascii_req;
3324 DWORD res;
3325 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3326 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3327 http_session_t *lpwhs = lpwhr->lpHttpSession;
3329 TRACE("\n");
3331 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3332 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3333 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3334 HeapFree( GetProcessHeap(), 0, lpszPath );
3336 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3337 NULL, 0, NULL, NULL );
3338 len--; /* the nul terminator isn't needed */
3339 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3340 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3341 ascii_req, len, NULL, NULL );
3342 HeapFree( GetProcessHeap(), 0, requestString );
3344 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3346 res = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3347 HeapFree( GetProcessHeap(), 0, ascii_req );
3348 if (res != ERROR_SUCCESS)
3349 return res;
3351 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3352 if (!responseLen)
3353 return ERROR_HTTP_INVALID_HEADER;
3355 return ERROR_SUCCESS;
3358 static void HTTP_InsertCookies(http_request_t *lpwhr)
3360 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3361 LPWSTR lpszCookies, lpszUrl = NULL;
3362 DWORD nCookieSize, size;
3363 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3365 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3366 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3367 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3369 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3371 int cnt = 0;
3372 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3374 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3375 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3377 cnt += sprintfW(lpszCookies, szCookie);
3378 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3379 strcatW(lpszCookies, szCrLf);
3381 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3382 HeapFree(GetProcessHeap(), 0, lpszCookies);
3385 HeapFree(GetProcessHeap(), 0, lpszUrl);
3388 /***********************************************************************
3389 * HTTP_HttpSendRequestW (internal)
3391 * Sends the specified request to the HTTP server
3393 * RETURNS
3394 * TRUE on success
3395 * FALSE on failure
3398 static DWORD HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3399 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3400 DWORD dwContentLength, BOOL bEndRequest)
3402 INT cnt;
3403 BOOL redirected = FALSE;
3404 LPWSTR requestString = NULL;
3405 INT responseLen;
3406 BOOL loop_next;
3407 INTERNET_ASYNC_RESULT iar;
3408 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3409 static const WCHAR szContentLength[] =
3410 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3411 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3412 DWORD res;
3414 TRACE("--> %p\n", lpwhr);
3416 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3418 /* if the verb is NULL default to GET */
3419 if (!lpwhr->lpszVerb)
3420 lpwhr->lpszVerb = heap_strdupW(szGET);
3422 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3424 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3425 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3426 lpwhr->dwBytesToWrite = dwContentLength;
3428 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3430 WCHAR *agent_header;
3431 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3432 int len;
3434 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3435 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3436 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3438 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3439 HeapFree(GetProcessHeap(), 0, agent_header);
3441 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3443 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3444 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3446 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3448 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3449 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3450 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3455 DWORD len;
3456 char *ascii_req;
3458 loop_next = FALSE;
3460 /* like native, just in case the caller forgot to call InternetReadFile
3461 * for all the data */
3462 HTTP_DrainContent(lpwhr);
3463 lpwhr->dwContentRead = 0;
3465 if (TRACE_ON(wininet))
3467 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3468 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3471 HTTP_FixURL(lpwhr);
3472 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3474 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3476 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3477 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3479 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3480 HTTP_InsertCookies(lpwhr);
3482 /* add the headers the caller supplied */
3483 if( lpszHeaders && dwHeaderLength )
3485 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3486 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3489 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3491 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3492 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3493 HeapFree(GetProcessHeap(), 0, url);
3495 else
3496 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3499 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3501 /* Send the request and store the results */
3502 if ((res = HTTP_OpenConnection(lpwhr)) != ERROR_SUCCESS)
3503 goto lend;
3505 /* send the request as ASCII, tack on the optional data */
3506 if (!lpOptional || redirected)
3507 dwOptionalLength = 0;
3508 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3509 NULL, 0, NULL, NULL );
3510 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3511 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3512 ascii_req, len, NULL, NULL );
3513 if( lpOptional )
3514 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3515 len = (len + dwOptionalLength - 1);
3516 ascii_req[len] = 0;
3517 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3519 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3520 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3522 res = NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3523 HeapFree( GetProcessHeap(), 0, ascii_req );
3525 lpwhr->dwBytesWritten = dwOptionalLength;
3527 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3528 INTERNET_STATUS_REQUEST_SENT,
3529 &len, sizeof(DWORD));
3531 if (bEndRequest)
3533 DWORD dwBufferSize;
3534 DWORD dwStatusCode;
3536 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3537 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3539 if (res != ERROR_SUCCESS)
3540 goto lend;
3542 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3544 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3545 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3546 sizeof(DWORD));
3548 HTTP_ProcessCookies(lpwhr);
3550 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3552 dwBufferSize = sizeof(dwStatusCode);
3553 if (!HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3554 &dwStatusCode,&dwBufferSize,NULL))
3555 dwStatusCode = 0;
3557 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
3559 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3560 dwBufferSize=sizeof(szNewLocation);
3561 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
3562 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL))
3564 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3566 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3567 lpwhr->lpszVerb = heap_strdupW(szGET);
3569 HTTP_DrainContent(lpwhr);
3570 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3572 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3573 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3574 res = HTTP_HandleRedirect(lpwhr, new_url);
3575 if (res == ERROR_SUCCESS)
3577 HeapFree(GetProcessHeap(), 0, requestString);
3578 loop_next = TRUE;
3580 HeapFree( GetProcessHeap(), 0, new_url );
3582 redirected = TRUE;
3585 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
3587 WCHAR szAuthValue[2048];
3588 dwBufferSize=2048;
3589 if (dwStatusCode == HTTP_STATUS_DENIED)
3591 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3592 DWORD dwIndex = 0;
3593 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
3595 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3596 &lpwhr->pAuthInfo,
3597 lpwhr->lpHttpSession->lpszUserName,
3598 lpwhr->lpHttpSession->lpszPassword,
3599 Host->lpszValue))
3601 loop_next = TRUE;
3602 break;
3606 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3608 DWORD dwIndex = 0;
3609 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
3611 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3612 &lpwhr->pProxyAuthInfo,
3613 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3614 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
3615 NULL))
3617 loop_next = TRUE;
3618 break;
3624 else
3625 res = ERROR_SUCCESS;
3627 while (loop_next);
3629 if(res == ERROR_SUCCESS) {
3630 WCHAR url[INTERNET_MAX_URL_LENGTH];
3631 WCHAR cacheFileName[MAX_PATH+1];
3632 BOOL b;
3634 b = HTTP_GetRequestURL(lpwhr, url);
3635 if(!b) {
3636 WARN("Could not get URL\n");
3637 goto lend;
3640 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3641 if(b) {
3642 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
3643 CloseHandle(lpwhr->hCacheFile);
3645 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3646 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3647 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3648 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3649 WARN("Could not create file: %u\n", GetLastError());
3650 lpwhr->hCacheFile = NULL;
3652 }else {
3653 WARN("Could not create cache entry: %08x\n", GetLastError());
3657 lend:
3659 HeapFree(GetProcessHeap(), 0, requestString);
3661 /* TODO: send notification for P3P header */
3663 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3665 if (res == ERROR_SUCCESS && lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite)
3666 HTTP_ReceiveRequestData(lpwhr, TRUE);
3667 else
3669 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3670 iar.dwError = res;
3672 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3673 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3674 sizeof(INTERNET_ASYNC_RESULT));
3678 TRACE("<--\n");
3679 return res;
3682 /***********************************************************************
3684 * Helper functions for the HttpSendRequest(Ex) functions
3687 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
3689 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
3690 http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
3692 TRACE("%p\n", lpwhr);
3694 HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
3695 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
3696 req->dwContentLength, req->bEndRequest);
3698 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
3702 static DWORD HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
3704 INT responseLen;
3705 DWORD dwBufferSize;
3706 INTERNET_ASYNC_RESULT iar;
3707 DWORD res = ERROR_SUCCESS;
3709 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3710 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3712 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3713 if (!responseLen)
3714 res = ERROR_HTTP_HEADER_NOT_FOUND;
3716 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3717 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
3719 /* process cookies here. Is this right? */
3720 HTTP_ProcessCookies(lpwhr);
3722 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3724 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
3726 DWORD dwCode,dwCodeLength = sizeof(DWORD);
3727 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) &&
3728 (dwCode == 302 || dwCode == 301 || dwCode == 303))
3730 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3731 dwBufferSize=sizeof(szNewLocation);
3732 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL))
3734 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3736 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3737 lpwhr->lpszVerb = heap_strdupW(szGET);
3739 HTTP_DrainContent(lpwhr);
3740 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3742 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3743 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3744 res = HTTP_HandleRedirect(lpwhr, new_url);
3745 if (res == ERROR_SUCCESS)
3746 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
3747 HeapFree( GetProcessHeap(), 0, new_url );
3753 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3754 iar.dwError = res;
3756 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3757 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3758 sizeof(INTERNET_ASYNC_RESULT));
3759 return res;
3762 /***********************************************************************
3763 * HttpEndRequestA (WININET.@)
3765 * Ends an HTTP request that was started by HttpSendRequestEx
3767 * RETURNS
3768 * TRUE if successful
3769 * FALSE on failure
3772 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
3773 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3775 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
3777 if (lpBuffersOut)
3779 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3780 return FALSE;
3783 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
3786 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
3788 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
3789 http_request_t *lpwhr = (http_request_t*)work->hdr;
3791 TRACE("%p\n", lpwhr);
3793 HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
3796 /***********************************************************************
3797 * HttpEndRequestW (WININET.@)
3799 * Ends an HTTP request that was started by HttpSendRequestEx
3801 * RETURNS
3802 * TRUE if successful
3803 * FALSE on failure
3806 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
3807 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3809 http_request_t *lpwhr;
3810 DWORD res;
3812 TRACE("-->\n");
3814 if (lpBuffersOut)
3816 SetLastError(ERROR_INVALID_PARAMETER);
3817 return FALSE;
3820 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3822 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3824 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3825 if (lpwhr)
3826 WININET_Release( &lpwhr->hdr );
3827 return FALSE;
3829 lpwhr->hdr.dwFlags |= dwFlags;
3831 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3833 WORKREQUEST work;
3834 struct WORKREQ_HTTPENDREQUESTW *request;
3836 work.asyncproc = AsyncHttpEndRequestProc;
3837 work.hdr = WININET_AddRef( &lpwhr->hdr );
3839 request = &work.u.HttpEndRequestW;
3840 request->dwFlags = dwFlags;
3841 request->dwContext = dwContext;
3843 INTERNET_AsyncCall(&work);
3844 res = ERROR_IO_PENDING;
3846 else
3847 res = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
3849 WININET_Release( &lpwhr->hdr );
3850 TRACE("%u <--\n", res);
3851 if(res != ERROR_SUCCESS)
3852 SetLastError(res);
3853 return res == ERROR_SUCCESS;
3856 /***********************************************************************
3857 * HttpSendRequestExA (WININET.@)
3859 * Sends the specified request to the HTTP server and allows chunked
3860 * transfers.
3862 * RETURNS
3863 * Success: TRUE
3864 * Failure: FALSE, call GetLastError() for more information.
3866 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3867 LPINTERNET_BUFFERSA lpBuffersIn,
3868 LPINTERNET_BUFFERSA lpBuffersOut,
3869 DWORD dwFlags, DWORD_PTR dwContext)
3871 INTERNET_BUFFERSW BuffersInW;
3872 BOOL rc = FALSE;
3873 DWORD headerlen;
3874 LPWSTR header = NULL;
3876 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3877 lpBuffersOut, dwFlags, dwContext);
3879 if (lpBuffersIn)
3881 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3882 if (lpBuffersIn->lpcszHeader)
3884 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3885 lpBuffersIn->dwHeadersLength,0,0);
3886 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3887 if (!(BuffersInW.lpcszHeader = header))
3889 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3890 return FALSE;
3892 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3893 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3894 header, headerlen);
3896 else
3897 BuffersInW.lpcszHeader = NULL;
3898 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3899 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3900 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3901 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3902 BuffersInW.Next = NULL;
3905 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3907 HeapFree(GetProcessHeap(),0,header);
3909 return rc;
3912 /***********************************************************************
3913 * HttpSendRequestExW (WININET.@)
3915 * Sends the specified request to the HTTP server and allows chunked
3916 * transfers
3918 * RETURNS
3919 * Success: TRUE
3920 * Failure: FALSE, call GetLastError() for more information.
3922 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3923 LPINTERNET_BUFFERSW lpBuffersIn,
3924 LPINTERNET_BUFFERSW lpBuffersOut,
3925 DWORD dwFlags, DWORD_PTR dwContext)
3927 http_request_t *lpwhr;
3928 http_session_t *lpwhs;
3929 appinfo_t *hIC;
3930 DWORD res;
3932 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3933 lpBuffersOut, dwFlags, dwContext);
3935 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3937 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3939 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3940 goto lend;
3943 lpwhs = lpwhr->lpHttpSession;
3944 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3945 hIC = lpwhs->lpAppInfo;
3946 assert(hIC->hdr.htype == WH_HINIT);
3948 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3950 WORKREQUEST workRequest;
3951 struct WORKREQ_HTTPSENDREQUESTW *req;
3953 workRequest.asyncproc = AsyncHttpSendRequestProc;
3954 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3955 req = &workRequest.u.HttpSendRequestW;
3956 if (lpBuffersIn)
3958 DWORD size = 0;
3960 if (lpBuffersIn->lpcszHeader)
3962 if (lpBuffersIn->dwHeadersLength == ~0u)
3963 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
3964 else
3965 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
3967 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
3968 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
3970 else req->lpszHeader = NULL;
3972 req->dwHeaderLength = size / sizeof(WCHAR);
3973 req->lpOptional = lpBuffersIn->lpvBuffer;
3974 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3975 req->dwContentLength = lpBuffersIn->dwBufferTotal;
3977 else
3979 req->lpszHeader = NULL;
3980 req->dwHeaderLength = 0;
3981 req->lpOptional = NULL;
3982 req->dwOptionalLength = 0;
3983 req->dwContentLength = 0;
3986 req->bEndRequest = FALSE;
3988 INTERNET_AsyncCall(&workRequest);
3990 * This is from windows.
3992 res = ERROR_IO_PENDING;
3994 else
3996 if (lpBuffersIn)
3997 res = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3998 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
3999 lpBuffersIn->dwBufferTotal, FALSE);
4000 else
4001 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
4004 lend:
4005 if ( lpwhr )
4006 WININET_Release( &lpwhr->hdr );
4008 TRACE("<---\n");
4009 if(res != ERROR_SUCCESS)
4010 SetLastError(res);
4011 return res == ERROR_SUCCESS;
4014 /***********************************************************************
4015 * HttpSendRequestW (WININET.@)
4017 * Sends the specified request to the HTTP server
4019 * RETURNS
4020 * TRUE on success
4021 * FALSE on failure
4024 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4025 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4027 http_request_t *lpwhr;
4028 http_session_t *lpwhs = NULL;
4029 appinfo_t *hIC = NULL;
4030 DWORD res = ERROR_SUCCESS;
4032 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4033 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4035 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
4036 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4038 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4039 goto lend;
4042 lpwhs = lpwhr->lpHttpSession;
4043 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
4045 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4046 goto lend;
4049 hIC = lpwhs->lpAppInfo;
4050 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
4052 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4053 goto lend;
4056 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4058 WORKREQUEST workRequest;
4059 struct WORKREQ_HTTPSENDREQUESTW *req;
4061 workRequest.asyncproc = AsyncHttpSendRequestProc;
4062 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4063 req = &workRequest.u.HttpSendRequestW;
4064 if (lpszHeaders)
4066 DWORD size;
4068 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4069 else size = dwHeaderLength * sizeof(WCHAR);
4071 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4072 memcpy(req->lpszHeader, lpszHeaders, size);
4074 else
4075 req->lpszHeader = 0;
4076 req->dwHeaderLength = dwHeaderLength;
4077 req->lpOptional = lpOptional;
4078 req->dwOptionalLength = dwOptionalLength;
4079 req->dwContentLength = dwOptionalLength;
4080 req->bEndRequest = TRUE;
4082 INTERNET_AsyncCall(&workRequest);
4084 * This is from windows.
4086 res = ERROR_IO_PENDING;
4088 else
4090 res = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
4091 dwHeaderLength, lpOptional, dwOptionalLength,
4092 dwOptionalLength, TRUE);
4094 lend:
4095 if( lpwhr )
4096 WININET_Release( &lpwhr->hdr );
4098 if(res != ERROR_SUCCESS)
4099 SetLastError(res);
4100 return res == ERROR_SUCCESS;
4103 /***********************************************************************
4104 * HttpSendRequestA (WININET.@)
4106 * Sends the specified request to the HTTP server
4108 * RETURNS
4109 * TRUE on success
4110 * FALSE on failure
4113 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4114 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4116 BOOL result;
4117 LPWSTR szHeaders=NULL;
4118 DWORD nLen=dwHeaderLength;
4119 if(lpszHeaders!=NULL)
4121 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4122 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4123 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4125 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4126 HeapFree(GetProcessHeap(),0,szHeaders);
4127 return result;
4130 /***********************************************************************
4131 * HTTPSESSION_Destroy (internal)
4133 * Deallocate session handle
4136 static void HTTPSESSION_Destroy(object_header_t *hdr)
4138 http_session_t *lpwhs = (http_session_t*) hdr;
4140 TRACE("%p\n", lpwhs);
4142 WININET_Release(&lpwhs->lpAppInfo->hdr);
4144 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4145 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4146 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4147 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4148 HeapFree(GetProcessHeap(), 0, lpwhs);
4151 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4153 switch(option) {
4154 case INTERNET_OPTION_HANDLE_TYPE:
4155 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4157 if (*size < sizeof(ULONG))
4158 return ERROR_INSUFFICIENT_BUFFER;
4160 *size = sizeof(DWORD);
4161 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4162 return ERROR_SUCCESS;
4165 return INET_QueryOption(option, buffer, size, unicode);
4168 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4170 http_session_t *ses = (http_session_t*)hdr;
4172 switch(option) {
4173 case INTERNET_OPTION_USERNAME:
4175 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4176 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4177 return ERROR_SUCCESS;
4179 case INTERNET_OPTION_PASSWORD:
4181 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4182 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4183 return ERROR_SUCCESS;
4185 default: break;
4188 return ERROR_INTERNET_INVALID_OPTION;
4191 static const object_vtbl_t HTTPSESSIONVtbl = {
4192 HTTPSESSION_Destroy,
4193 NULL,
4194 HTTPSESSION_QueryOption,
4195 HTTPSESSION_SetOption,
4196 NULL,
4197 NULL,
4198 NULL,
4199 NULL,
4200 NULL
4204 /***********************************************************************
4205 * HTTP_Connect (internal)
4207 * Create http session handle
4209 * RETURNS
4210 * HINTERNET a session handle on success
4211 * NULL on failure
4214 HINTERNET HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4215 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4216 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4217 DWORD dwInternalFlags)
4219 http_session_t *lpwhs = NULL;
4220 HINTERNET handle = NULL;
4222 TRACE("-->\n");
4224 if (!lpszServerName || !lpszServerName[0])
4226 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4227 goto lerror;
4230 assert( hIC->hdr.htype == WH_HINIT );
4232 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4233 if (NULL == lpwhs)
4235 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4236 goto lerror;
4240 * According to my tests. The name is not resolved until a request is sent
4243 lpwhs->hdr.htype = WH_HHTTPSESSION;
4244 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4245 lpwhs->hdr.dwFlags = dwFlags;
4246 lpwhs->hdr.dwContext = dwContext;
4247 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4248 lpwhs->hdr.refs = 1;
4249 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4251 WININET_AddRef( &hIC->hdr );
4252 lpwhs->lpAppInfo = hIC;
4253 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4255 handle = WININET_AllocHandle( &lpwhs->hdr );
4256 if (NULL == handle)
4258 ERR("Failed to alloc handle\n");
4259 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4260 goto lerror;
4263 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4264 if(strchrW(hIC->lpszProxy, ' '))
4265 FIXME("Several proxies not implemented.\n");
4266 if(hIC->lpszProxyBypass)
4267 FIXME("Proxy bypass is ignored.\n");
4269 if (lpszServerName && lpszServerName[0])
4271 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4272 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4274 if (lpszUserName && lpszUserName[0])
4275 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4276 if (lpszPassword && lpszPassword[0])
4277 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4278 lpwhs->nServerPort = nServerPort;
4279 lpwhs->nHostPort = nServerPort;
4281 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4282 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4284 INTERNET_SendCallback(&hIC->hdr, dwContext,
4285 INTERNET_STATUS_HANDLE_CREATED, &handle,
4286 sizeof(handle));
4289 lerror:
4290 if( lpwhs )
4291 WININET_Release( &lpwhs->hdr );
4294 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4295 * windows
4298 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4299 return handle;
4303 /***********************************************************************
4304 * HTTP_OpenConnection (internal)
4306 * Connect to a web server
4308 * RETURNS
4310 * TRUE on success
4311 * FALSE on failure
4313 static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
4315 http_session_t *lpwhs;
4316 appinfo_t *hIC = NULL;
4317 char szaddr[INET6_ADDRSTRLEN];
4318 const void *addr;
4319 DWORD res = ERROR_SUCCESS;
4321 TRACE("-->\n");
4324 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4326 res = ERROR_INVALID_PARAMETER;
4327 goto lend;
4330 if (NETCON_connected(&lpwhr->netConnection))
4331 goto lend;
4332 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
4334 lpwhs = lpwhr->lpHttpSession;
4336 hIC = lpwhs->lpAppInfo;
4337 switch (lpwhs->socketAddress.ss_family)
4339 case AF_INET:
4340 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4341 break;
4342 case AF_INET6:
4343 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4344 break;
4345 default:
4346 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4347 return ERROR_INTERNET_NAME_NOT_RESOLVED;
4349 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4350 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4351 INTERNET_STATUS_CONNECTING_TO_SERVER,
4352 szaddr,
4353 strlen(szaddr)+1);
4355 res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
4356 if (res != ERROR_SUCCESS)
4358 WARN("Socket creation failed: %u\n", res);
4359 goto lend;
4362 res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4363 lpwhs->sa_len);
4364 if(res != ERROR_SUCCESS)
4365 goto lend;
4367 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4369 /* Note: we differ from Microsoft's WinINet here. they seem to have
4370 * a bug that causes no status callbacks to be sent when starting
4371 * a tunnel to a proxy server using the CONNECT verb. i believe our
4372 * behaviour to be more correct and to not cause any incompatibilities
4373 * because using a secure connection through a proxy server is a rare
4374 * case that would be hard for anyone to depend on */
4375 if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS)
4376 goto lend;
4378 res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
4379 if(res != ERROR_SUCCESS)
4381 WARN("Couldn't connect securely to host\n");
4382 goto lend;
4386 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4387 INTERNET_STATUS_CONNECTED_TO_SERVER,
4388 szaddr, strlen(szaddr)+1);
4390 lend:
4391 lpwhr->read_pos = lpwhr->read_size = 0;
4392 lpwhr->read_chunked = FALSE;
4394 TRACE("%d <--\n", res);
4395 return res;
4399 /***********************************************************************
4400 * HTTP_clear_response_headers (internal)
4402 * clear out any old response headers
4404 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4406 DWORD i;
4408 for( i=0; i<lpwhr->nCustHeaders; i++)
4410 if( !lpwhr->pCustHeaders[i].lpszField )
4411 continue;
4412 if( !lpwhr->pCustHeaders[i].lpszValue )
4413 continue;
4414 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4415 continue;
4416 HTTP_DeleteCustomHeader( lpwhr, i );
4417 i--;
4421 /***********************************************************************
4422 * HTTP_GetResponseHeaders (internal)
4424 * Read server response
4426 * RETURNS
4428 * TRUE on success
4429 * FALSE on error
4431 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4433 INT cbreaks = 0;
4434 WCHAR buffer[MAX_REPLY_LEN];
4435 DWORD buflen = MAX_REPLY_LEN;
4436 BOOL bSuccess = FALSE;
4437 INT rc = 0;
4438 char bufferA[MAX_REPLY_LEN];
4439 LPWSTR status_code = NULL, status_text = NULL;
4440 DWORD cchMaxRawHeaders = 1024;
4441 LPWSTR lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4442 LPWSTR temp;
4443 DWORD cchRawHeaders = 0;
4444 BOOL codeHundred = FALSE;
4446 TRACE("-->\n");
4448 /* clear old response headers (eg. from a redirect response) */
4449 if (clear) HTTP_clear_response_headers( lpwhr );
4451 if (!NETCON_connected(&lpwhr->netConnection))
4452 goto lend;
4454 do {
4455 static const WCHAR szHundred[] = {'1','0','0',0};
4457 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4459 buflen = MAX_REPLY_LEN;
4460 if (!read_line(lpwhr, bufferA, &buflen))
4461 goto lend;
4462 rc += buflen;
4463 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4464 /* check is this a status code line? */
4465 if (!strncmpW(buffer, g_szHttp1_0, 4))
4467 /* split the version from the status code */
4468 status_code = strchrW( buffer, ' ' );
4469 if( !status_code )
4470 goto lend;
4471 *status_code++=0;
4473 /* split the status code from the status text */
4474 status_text = strchrW( status_code, ' ' );
4475 if( !status_text )
4476 goto lend;
4477 *status_text++=0;
4479 TRACE("version [%s] status code [%s] status text [%s]\n",
4480 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4482 codeHundred = (!strcmpW(status_code, szHundred));
4484 else if (!codeHundred)
4486 FIXME("Non status line at head of response (%s)\n",debugstr_w(buffer));
4487 goto lend;
4489 } while (codeHundred);
4491 /* Add status code */
4492 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4493 HTTP_ADDHDR_FLAG_REPLACE);
4495 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4496 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4498 lpwhr->lpszVersion = heap_strdupW(buffer);
4499 lpwhr->lpszStatusText = heap_strdupW(status_text);
4501 /* Restore the spaces */
4502 *(status_code-1) = ' ';
4503 *(status_text-1) = ' ';
4505 /* regenerate raw headers */
4506 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4507 cchMaxRawHeaders *= 2;
4508 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4509 if (temp == NULL) goto lend;
4510 lpszRawHeaders = temp;
4511 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4512 cchRawHeaders += (buflen-1);
4513 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4514 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4515 lpszRawHeaders[cchRawHeaders] = '\0';
4517 /* Parse each response line */
4520 buflen = MAX_REPLY_LEN;
4521 if (read_line(lpwhr, bufferA, &buflen))
4523 LPWSTR * pFieldAndValue;
4525 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4527 if (!bufferA[0]) break;
4528 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4530 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4531 if (pFieldAndValue)
4533 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4534 cchMaxRawHeaders *= 2;
4535 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4536 if (temp == NULL) goto lend;
4537 lpszRawHeaders = temp;
4538 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4539 cchRawHeaders += (buflen-1);
4540 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4541 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4542 lpszRawHeaders[cchRawHeaders] = '\0';
4544 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4545 HTTP_ADDREQ_FLAG_ADD );
4547 HTTP_FreeTokens(pFieldAndValue);
4550 else
4552 cbreaks++;
4553 if (cbreaks >= 2)
4554 break;
4556 }while(1);
4558 /* make sure the response header is terminated with an empty line. Some apps really
4559 truly care about that empty line being there for some reason. Just add it to the
4560 header. */
4561 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4563 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4564 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4565 if (temp == NULL) goto lend;
4566 lpszRawHeaders = temp;
4569 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4571 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4572 lpwhr->lpszRawHeaders = lpszRawHeaders;
4573 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4574 bSuccess = TRUE;
4576 lend:
4578 TRACE("<--\n");
4579 if (bSuccess)
4580 return rc;
4581 else
4583 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4584 return 0;
4588 /***********************************************************************
4589 * HTTP_InterpretHttpHeader (internal)
4591 * Parse server response
4593 * RETURNS
4595 * Pointer to array of field, value, NULL on success.
4596 * NULL on error.
4598 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4600 LPWSTR * pTokenPair;
4601 LPWSTR pszColon;
4602 INT len;
4604 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4606 pszColon = strchrW(buffer, ':');
4607 /* must have two tokens */
4608 if (!pszColon)
4610 HTTP_FreeTokens(pTokenPair);
4611 if (buffer[0])
4612 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4613 return NULL;
4616 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4617 if (!pTokenPair[0])
4619 HTTP_FreeTokens(pTokenPair);
4620 return NULL;
4622 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4623 pTokenPair[0][pszColon - buffer] = '\0';
4625 /* skip colon */
4626 pszColon++;
4627 len = strlenW(pszColon);
4628 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4629 if (!pTokenPair[1])
4631 HTTP_FreeTokens(pTokenPair);
4632 return NULL;
4634 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4636 strip_spaces(pTokenPair[0]);
4637 strip_spaces(pTokenPair[1]);
4639 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4640 return pTokenPair;
4643 /***********************************************************************
4644 * HTTP_ProcessHeader (internal)
4646 * Stuff header into header tables according to <dwModifier>
4650 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4652 static DWORD HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4654 LPHTTPHEADERW lphttpHdr = NULL;
4655 INT index = -1;
4656 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4657 DWORD res = ERROR_HTTP_INVALID_HEADER;
4659 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4661 /* REPLACE wins out over ADD */
4662 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4663 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4665 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4666 index = -1;
4667 else
4668 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4670 if (index >= 0)
4672 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4673 return ERROR_HTTP_INVALID_HEADER;
4674 lphttpHdr = &lpwhr->pCustHeaders[index];
4676 else if (value)
4678 HTTPHEADERW hdr;
4680 hdr.lpszField = (LPWSTR)field;
4681 hdr.lpszValue = (LPWSTR)value;
4682 hdr.wFlags = hdr.wCount = 0;
4684 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4685 hdr.wFlags |= HDR_ISREQUEST;
4687 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4689 /* no value to delete */
4690 else return ERROR_SUCCESS;
4692 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4693 lphttpHdr->wFlags |= HDR_ISREQUEST;
4694 else
4695 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4697 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4699 HTTP_DeleteCustomHeader( lpwhr, index );
4701 if (value)
4703 HTTPHEADERW hdr;
4705 hdr.lpszField = (LPWSTR)field;
4706 hdr.lpszValue = (LPWSTR)value;
4707 hdr.wFlags = hdr.wCount = 0;
4709 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4710 hdr.wFlags |= HDR_ISREQUEST;
4712 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4715 return ERROR_SUCCESS;
4717 else if (dwModifier & COALESCEFLAGS)
4719 LPWSTR lpsztmp;
4720 WCHAR ch = 0;
4721 INT len = 0;
4722 INT origlen = strlenW(lphttpHdr->lpszValue);
4723 INT valuelen = strlenW(value);
4725 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4727 ch = ',';
4728 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4730 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4732 ch = ';';
4733 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4736 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4738 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4739 if (lpsztmp)
4741 lphttpHdr->lpszValue = lpsztmp;
4742 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4743 if (ch > 0)
4745 lphttpHdr->lpszValue[origlen] = ch;
4746 origlen++;
4747 lphttpHdr->lpszValue[origlen] = ' ';
4748 origlen++;
4751 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4752 lphttpHdr->lpszValue[len] = '\0';
4753 res = ERROR_SUCCESS;
4755 else
4757 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4758 res = ERROR_OUTOFMEMORY;
4761 TRACE("<-- %d\n", res);
4762 return res;
4766 /***********************************************************************
4767 * HTTP_FinishedReading (internal)
4769 * Called when all content from server has been read by client.
4772 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4774 BOOL keepalive = HTTP_KeepAlive(lpwhr);
4776 TRACE("\n");
4779 if (!keepalive)
4781 HTTPREQ_CloseConnection(&lpwhr->hdr);
4784 /* FIXME: store data in the URL cache here */
4786 return TRUE;
4790 /***********************************************************************
4791 * HTTP_GetCustomHeaderIndex (internal)
4793 * Return index of custom header from header array
4796 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
4797 int requested_index, BOOL request_only)
4799 DWORD index;
4801 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
4803 for (index = 0; index < lpwhr->nCustHeaders; index++)
4805 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
4806 continue;
4808 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4809 continue;
4811 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4812 continue;
4814 if (requested_index == 0)
4815 break;
4816 requested_index --;
4819 if (index >= lpwhr->nCustHeaders)
4820 index = -1;
4822 TRACE("Return: %d\n", index);
4823 return index;
4827 /***********************************************************************
4828 * HTTP_InsertCustomHeader (internal)
4830 * Insert header into array
4833 static DWORD HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
4835 INT count;
4836 LPHTTPHEADERW lph = NULL;
4838 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
4839 count = lpwhr->nCustHeaders + 1;
4840 if (count > 1)
4841 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
4842 else
4843 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
4845 if (!lph)
4846 return ERROR_OUTOFMEMORY;
4848 lpwhr->pCustHeaders = lph;
4849 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
4850 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
4851 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
4852 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
4853 lpwhr->nCustHeaders++;
4855 return ERROR_SUCCESS;
4859 /***********************************************************************
4860 * HTTP_DeleteCustomHeader (internal)
4862 * Delete header from array
4863 * If this function is called, the indexs may change.
4865 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
4867 if( lpwhr->nCustHeaders <= 0 )
4868 return FALSE;
4869 if( index >= lpwhr->nCustHeaders )
4870 return FALSE;
4871 lpwhr->nCustHeaders--;
4873 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
4874 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
4876 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
4877 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
4878 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
4880 return TRUE;
4884 /***********************************************************************
4885 * HTTP_VerifyValidHeader (internal)
4887 * Verify the given header is not invalid for the given http request
4890 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
4892 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4893 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
4894 return ERROR_HTTP_INVALID_HEADER;
4896 return ERROR_SUCCESS;
4899 /***********************************************************************
4900 * IsHostInProxyBypassList (@)
4902 * Undocumented
4905 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
4907 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
4908 return FALSE;