dxgi: Implement IDXGIFactory::GetParent().
[wine/testsucceed.git] / dlls / wininet / http.c
blob7e28c2e4ee976b833ce96c4055f27c18aa270362
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 BOOL HTTP_HandleRedirect(http_request_t *req, LPCWSTR lpszUrl);
212 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
213 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
214 static void HTTP_DrainContent(http_request_t *req);
215 static BOOL HTTP_FinishedReading(http_request_t *req);
217 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
219 int HeaderIndex = 0;
220 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
221 if (HeaderIndex == -1)
222 return NULL;
223 else
224 return &req->pCustHeaders[HeaderIndex];
227 #ifdef HAVE_ZLIB
229 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
231 return HeapAlloc(GetProcessHeap(), 0, items*size);
234 static void wininet_zfree(voidpf opaque, voidpf address)
236 HeapFree(GetProcessHeap(), 0, address);
239 static void init_gzip_stream(http_request_t *req)
241 gzip_stream_t *gzip_stream;
242 int index, zres;
244 gzip_stream = HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t));
245 gzip_stream->zstream.zalloc = wininet_zalloc;
246 gzip_stream->zstream.zfree = wininet_zfree;
247 gzip_stream->zstream.opaque = NULL;
248 gzip_stream->zstream.next_in = NULL;
249 gzip_stream->zstream.avail_in = 0;
250 gzip_stream->zstream.next_out = NULL;
251 gzip_stream->zstream.avail_out = 0;
252 gzip_stream->buf_pos = 0;
253 gzip_stream->buf_size = 0;
254 gzip_stream->end_of_data = FALSE;
256 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
257 if(zres != Z_OK) {
258 ERR("inflateInit failed: %d\n", zres);
259 HeapFree(GetProcessHeap(), 0, gzip_stream);
260 return;
263 req->gzip_stream = gzip_stream;
265 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
266 if(index != -1)
267 HTTP_DeleteCustomHeader(req, index);
270 #else
272 static void init_gzip_stream(http_request_t *req)
274 ERR("gzip stream not supported, missing zlib.\n");
277 #endif
279 /* set the request content length based on the headers */
280 static DWORD set_content_length( http_request_t *lpwhr )
282 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
283 WCHAR encoding[20];
284 DWORD size;
286 size = sizeof(lpwhr->dwContentLength);
287 if (!HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
288 &lpwhr->dwContentLength, &size, NULL))
289 lpwhr->dwContentLength = ~0u;
291 size = sizeof(encoding);
292 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) &&
293 !strcmpiW(encoding, szChunked))
295 lpwhr->dwContentLength = ~0u;
296 lpwhr->read_chunked = TRUE;
299 if(lpwhr->decoding) {
300 int encoding_idx;
302 static const WCHAR gzipW[] = {'g','z','i','p',0};
304 encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
305 if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
306 init_gzip_stream(lpwhr);
309 return lpwhr->dwContentLength;
312 /***********************************************************************
313 * HTTP_Tokenize (internal)
315 * Tokenize a string, allocating memory for the tokens.
317 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
319 LPWSTR * token_array;
320 int tokens = 0;
321 int i;
322 LPCWSTR next_token;
324 if (string)
326 /* empty string has no tokens */
327 if (*string)
328 tokens++;
329 /* count tokens */
330 for (i = 0; string[i]; i++)
332 if (!strncmpW(string+i, token_string, strlenW(token_string)))
334 DWORD j;
335 tokens++;
336 /* we want to skip over separators, but not the null terminator */
337 for (j = 0; j < strlenW(token_string) - 1; j++)
338 if (!string[i+j])
339 break;
340 i += j;
345 /* add 1 for terminating NULL */
346 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
347 token_array[tokens] = NULL;
348 if (!tokens)
349 return token_array;
350 for (i = 0; i < tokens; i++)
352 int len;
353 next_token = strstrW(string, token_string);
354 if (!next_token) next_token = string+strlenW(string);
355 len = next_token - string;
356 token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
357 memcpy(token_array[i], string, len*sizeof(WCHAR));
358 token_array[i][len] = '\0';
359 string = next_token+strlenW(token_string);
361 return token_array;
364 /***********************************************************************
365 * HTTP_FreeTokens (internal)
367 * Frees memory returned from HTTP_Tokenize.
369 static void HTTP_FreeTokens(LPWSTR * token_array)
371 int i;
372 for (i = 0; token_array[i]; i++)
373 HeapFree(GetProcessHeap(), 0, token_array[i]);
374 HeapFree(GetProcessHeap(), 0, token_array);
377 static void HTTP_FixURL(http_request_t *lpwhr)
379 static const WCHAR szSlash[] = { '/',0 };
380 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
382 /* If we don't have a path we set it to root */
383 if (NULL == lpwhr->lpszPath)
384 lpwhr->lpszPath = heap_strdupW(szSlash);
385 else /* remove \r and \n*/
387 int nLen = strlenW(lpwhr->lpszPath);
388 while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
390 nLen--;
391 lpwhr->lpszPath[nLen]='\0';
393 /* Replace '\' with '/' */
394 while (nLen>0) {
395 nLen--;
396 if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
400 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
401 lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
402 && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
404 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
405 (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
406 *fixurl = '/';
407 strcpyW(fixurl + 1, lpwhr->lpszPath);
408 HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
409 lpwhr->lpszPath = fixurl;
413 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
415 LPWSTR requestString;
416 DWORD len, n;
417 LPCWSTR *req;
418 UINT i;
419 LPWSTR p;
421 static const WCHAR szSpace[] = { ' ',0 };
422 static const WCHAR szColon[] = { ':',' ',0 };
423 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
425 /* allocate space for an array of all the string pointers to be added */
426 len = (lpwhr->nCustHeaders)*4 + 10;
427 req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
429 /* add the verb, path and HTTP version string */
430 n = 0;
431 req[n++] = verb;
432 req[n++] = szSpace;
433 req[n++] = path;
434 req[n++] = szSpace;
435 req[n++] = version;
437 /* Append custom request headers */
438 for (i = 0; i < lpwhr->nCustHeaders; i++)
440 if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
442 req[n++] = szCrLf;
443 req[n++] = lpwhr->pCustHeaders[i].lpszField;
444 req[n++] = szColon;
445 req[n++] = lpwhr->pCustHeaders[i].lpszValue;
447 TRACE("Adding custom header %s (%s)\n",
448 debugstr_w(lpwhr->pCustHeaders[i].lpszField),
449 debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
453 if( n >= len )
454 ERR("oops. buffer overrun\n");
456 req[n] = NULL;
457 requestString = HTTP_build_req( req, 4 );
458 HeapFree( GetProcessHeap(), 0, req );
461 * Set (header) termination string for request
462 * Make sure there's exactly two new lines at the end of the request
464 p = &requestString[strlenW(requestString)-1];
465 while ( (*p == '\n') || (*p == '\r') )
466 p--;
467 strcpyW( p+1, sztwocrlf );
469 return requestString;
472 static void HTTP_ProcessCookies( http_request_t *lpwhr )
474 int HeaderIndex;
475 int numCookies = 0;
476 LPHTTPHEADERW setCookieHeader;
478 while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
480 setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
482 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
484 int len;
485 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
486 LPWSTR buf_url;
487 LPHTTPHEADERW Host;
489 Host = HTTP_GetHeader(lpwhr, hostW);
490 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
491 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
492 sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
493 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
495 HeapFree(GetProcessHeap(), 0, buf_url);
497 numCookies++;
501 static void strip_spaces(LPWSTR start)
503 LPWSTR str = start;
504 LPWSTR end;
506 while (*str == ' ' && *str != '\0')
507 str++;
509 if (str != start)
510 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
512 end = start + strlenW(start) - 1;
513 while (end >= start && *end == ' ')
515 *end = '\0';
516 end--;
520 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
522 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
523 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
524 BOOL is_basic;
525 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
526 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
527 if (is_basic && pszRealm)
529 LPCWSTR token;
530 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
531 LPCWSTR realm;
532 ptr++;
533 *pszRealm=NULL;
534 token = strchrW(ptr,'=');
535 if (!token)
536 return TRUE;
537 realm = ptr;
538 while (*realm == ' ' && *realm != '\0')
539 realm++;
540 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
541 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
543 token++;
544 while (*token == ' ' && *token != '\0')
545 token++;
546 if (*token == '\0')
547 return TRUE;
548 *pszRealm = heap_strdupW(token);
549 strip_spaces(*pszRealm);
553 return is_basic;
556 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
558 if (!authinfo) return;
560 if (SecIsValidHandle(&authinfo->ctx))
561 DeleteSecurityContext(&authinfo->ctx);
562 if (SecIsValidHandle(&authinfo->cred))
563 FreeCredentialsHandle(&authinfo->cred);
565 HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
566 HeapFree(GetProcessHeap(), 0, authinfo->scheme);
567 HeapFree(GetProcessHeap(), 0, authinfo);
570 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
572 authorizationData *ad;
573 UINT rc = 0;
575 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
577 EnterCriticalSection(&authcache_cs);
578 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, authorizationData, entry)
580 if (!strcmpiW(host,ad->lpszwHost) && !strcmpW(realm,ad->lpszwRealm))
582 TRACE("Authorization found in cache\n");
583 *auth_data = HeapAlloc(GetProcessHeap(),0,ad->AuthorizationLen);
584 memcpy(*auth_data,ad->lpszAuthorization,ad->AuthorizationLen);
585 rc = ad->AuthorizationLen;
586 break;
589 LeaveCriticalSection(&authcache_cs);
590 return rc;
593 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
595 struct list *cursor;
596 authorizationData* ad = NULL;
598 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
600 EnterCriticalSection(&authcache_cs);
601 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
603 authorizationData *check = LIST_ENTRY(cursor,authorizationData,entry);
604 if (!strcmpiW(host,check->lpszwHost) && !strcmpW(realm,check->lpszwRealm))
606 ad = check;
607 break;
611 if (ad)
613 TRACE("Found match in cache, replacing\n");
614 HeapFree(GetProcessHeap(),0,ad->lpszAuthorization);
615 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
616 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
617 ad->AuthorizationLen = auth_data_len;
619 else
621 ad = HeapAlloc(GetProcessHeap(),0,sizeof(authorizationData));
622 ad->lpszwHost = heap_strdupW(host);
623 ad->lpszwRealm = heap_strdupW(realm);
624 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
625 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
626 ad->AuthorizationLen = auth_data_len;
627 list_add_head(&basicAuthorizationCache,&ad->entry);
628 TRACE("authorization cached\n");
630 LeaveCriticalSection(&authcache_cs);
633 static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
634 struct HttpAuthInfo **ppAuthInfo,
635 LPWSTR domain_and_username, LPWSTR password,
636 LPWSTR host )
638 SECURITY_STATUS sec_status;
639 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
640 BOOL first = FALSE;
641 LPWSTR szRealm = NULL;
643 TRACE("%s\n", debugstr_w(pszAuthValue));
645 if (!pAuthInfo)
647 TimeStamp exp;
649 first = TRUE;
650 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
651 if (!pAuthInfo)
652 return FALSE;
654 SecInvalidateHandle(&pAuthInfo->cred);
655 SecInvalidateHandle(&pAuthInfo->ctx);
656 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
657 pAuthInfo->attr = 0;
658 pAuthInfo->auth_data = NULL;
659 pAuthInfo->auth_data_len = 0;
660 pAuthInfo->finished = FALSE;
662 if (is_basic_auth_value(pszAuthValue,NULL))
664 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
665 pAuthInfo->scheme = heap_strdupW(szBasic);
666 if (!pAuthInfo->scheme)
668 HeapFree(GetProcessHeap(), 0, pAuthInfo);
669 return FALSE;
672 else
674 PVOID pAuthData;
675 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
677 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
678 if (!pAuthInfo->scheme)
680 HeapFree(GetProcessHeap(), 0, pAuthInfo);
681 return FALSE;
684 if (domain_and_username)
686 WCHAR *user = strchrW(domain_and_username, '\\');
687 WCHAR *domain = domain_and_username;
689 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
691 pAuthData = &nt_auth_identity;
693 if (user) user++;
694 else
696 user = domain_and_username;
697 domain = NULL;
700 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
701 nt_auth_identity.User = user;
702 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
703 nt_auth_identity.Domain = domain;
704 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
705 nt_auth_identity.Password = password;
706 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
708 else
709 /* use default credentials */
710 pAuthData = NULL;
712 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
713 SECPKG_CRED_OUTBOUND, NULL,
714 pAuthData, NULL,
715 NULL, &pAuthInfo->cred,
716 &exp);
717 if (sec_status == SEC_E_OK)
719 PSecPkgInfoW sec_pkg_info;
720 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
721 if (sec_status == SEC_E_OK)
723 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
724 FreeContextBuffer(sec_pkg_info);
727 if (sec_status != SEC_E_OK)
729 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
730 debugstr_w(pAuthInfo->scheme), sec_status);
731 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
732 HeapFree(GetProcessHeap(), 0, pAuthInfo);
733 return FALSE;
736 *ppAuthInfo = pAuthInfo;
738 else if (pAuthInfo->finished)
739 return FALSE;
741 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
742 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
744 ERR("authentication scheme changed from %s to %s\n",
745 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
746 return FALSE;
749 if (is_basic_auth_value(pszAuthValue,&szRealm))
751 int userlen;
752 int passlen;
753 char *auth_data = NULL;
754 UINT auth_data_len = 0;
756 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
758 if (!domain_and_username)
760 if (host && szRealm)
761 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
762 if (auth_data_len == 0)
764 HeapFree(GetProcessHeap(),0,szRealm);
765 return FALSE;
768 else
770 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
771 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
773 /* length includes a nul terminator, which will be re-used for the ':' */
774 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
775 if (!auth_data)
777 HeapFree(GetProcessHeap(),0,szRealm);
778 return FALSE;
781 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
782 auth_data[userlen] = ':';
783 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
784 auth_data_len = userlen + 1 + passlen;
785 if (host && szRealm)
786 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
789 pAuthInfo->auth_data = auth_data;
790 pAuthInfo->auth_data_len = auth_data_len;
791 pAuthInfo->finished = TRUE;
792 HeapFree(GetProcessHeap(),0,szRealm);
794 return TRUE;
796 else
798 LPCWSTR pszAuthData;
799 SecBufferDesc out_desc, in_desc;
800 SecBuffer out, in;
801 unsigned char *buffer;
802 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
803 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
805 in.BufferType = SECBUFFER_TOKEN;
806 in.cbBuffer = 0;
807 in.pvBuffer = NULL;
809 in_desc.ulVersion = 0;
810 in_desc.cBuffers = 1;
811 in_desc.pBuffers = &in;
813 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
814 if (*pszAuthData == ' ')
816 pszAuthData++;
817 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
818 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
819 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
822 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
824 out.BufferType = SECBUFFER_TOKEN;
825 out.cbBuffer = pAuthInfo->max_token;
826 out.pvBuffer = buffer;
828 out_desc.ulVersion = 0;
829 out_desc.cBuffers = 1;
830 out_desc.pBuffers = &out;
832 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
833 first ? NULL : &pAuthInfo->ctx,
834 first ? lpwhr->lpHttpSession->lpszServerName : NULL,
835 context_req, 0, SECURITY_NETWORK_DREP,
836 in.pvBuffer ? &in_desc : NULL,
837 0, &pAuthInfo->ctx, &out_desc,
838 &pAuthInfo->attr, &pAuthInfo->exp);
839 if (sec_status == SEC_E_OK)
841 pAuthInfo->finished = TRUE;
842 pAuthInfo->auth_data = out.pvBuffer;
843 pAuthInfo->auth_data_len = out.cbBuffer;
844 TRACE("sending last auth packet\n");
846 else if (sec_status == SEC_I_CONTINUE_NEEDED)
848 pAuthInfo->auth_data = out.pvBuffer;
849 pAuthInfo->auth_data_len = out.cbBuffer;
850 TRACE("sending next auth packet\n");
852 else
854 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
855 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
856 destroy_authinfo(pAuthInfo);
857 *ppAuthInfo = NULL;
858 return FALSE;
862 return TRUE;
865 /***********************************************************************
866 * HTTP_HttpAddRequestHeadersW (internal)
868 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
869 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
871 LPWSTR lpszStart;
872 LPWSTR lpszEnd;
873 LPWSTR buffer;
874 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
876 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
878 if( dwHeaderLength == ~0U )
879 len = strlenW(lpszHeader);
880 else
881 len = dwHeaderLength;
882 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
883 lstrcpynW( buffer, lpszHeader, len + 1);
885 lpszStart = buffer;
889 LPWSTR * pFieldAndValue;
891 lpszEnd = lpszStart;
893 while (*lpszEnd != '\0')
895 if (*lpszEnd == '\r' || *lpszEnd == '\n')
896 break;
897 lpszEnd++;
900 if (*lpszStart == '\0')
901 break;
903 if (*lpszEnd == '\r' || *lpszEnd == '\n')
905 *lpszEnd = '\0';
906 lpszEnd++; /* Jump over newline */
908 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
909 if (*lpszStart == '\0')
911 /* Skip 0-length headers */
912 lpszStart = lpszEnd;
913 res = ERROR_SUCCESS;
914 continue;
916 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
917 if (pFieldAndValue)
919 res = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
920 if (res == ERROR_SUCCESS)
921 res = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
922 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
923 HTTP_FreeTokens(pFieldAndValue);
926 lpszStart = lpszEnd;
927 } while (res == ERROR_SUCCESS);
929 HeapFree(GetProcessHeap(), 0, buffer);
931 return res;
934 /***********************************************************************
935 * HttpAddRequestHeadersW (WININET.@)
937 * Adds one or more HTTP header to the request handler
939 * NOTE
940 * On Windows if dwHeaderLength includes the trailing '\0', then
941 * HttpAddRequestHeadersW() adds it too. However this results in an
942 * invalid Http header which is rejected by some servers so we probably
943 * don't need to match Windows on that point.
945 * RETURNS
946 * TRUE on success
947 * FALSE on failure
950 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
951 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
953 http_request_t *lpwhr;
954 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
956 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
958 if (!lpszHeader)
959 return TRUE;
961 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
962 if (lpwhr && lpwhr->hdr.htype == WH_HHTTPREQ)
963 res = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
964 if( lpwhr )
965 WININET_Release( &lpwhr->hdr );
967 if(res != ERROR_SUCCESS)
968 SetLastError(res);
969 return res == ERROR_SUCCESS;
972 /***********************************************************************
973 * HttpAddRequestHeadersA (WININET.@)
975 * Adds one or more HTTP header to the request handler
977 * RETURNS
978 * TRUE on success
979 * FALSE on failure
982 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
983 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
985 DWORD len;
986 LPWSTR hdr;
987 BOOL r;
989 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
991 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
992 hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
993 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
994 if( dwHeaderLength != ~0U )
995 dwHeaderLength = len;
997 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
999 HeapFree( GetProcessHeap(), 0, hdr );
1001 return r;
1004 /***********************************************************************
1005 * HttpOpenRequestA (WININET.@)
1007 * Open a HTTP request handle
1009 * RETURNS
1010 * HINTERNET a HTTP request handle on success
1011 * NULL on failure
1014 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1015 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1016 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1017 DWORD dwFlags, DWORD_PTR dwContext)
1019 LPWSTR szVerb = NULL, szObjectName = NULL;
1020 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1021 INT acceptTypesCount;
1022 HINTERNET rc = FALSE;
1023 LPCSTR *types;
1025 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1026 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1027 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1028 dwFlags, dwContext);
1030 if (lpszVerb)
1032 szVerb = heap_strdupAtoW(lpszVerb);
1033 if ( !szVerb )
1034 goto end;
1037 if (lpszObjectName)
1039 szObjectName = heap_strdupAtoW(lpszObjectName);
1040 if ( !szObjectName )
1041 goto end;
1044 if (lpszVersion)
1046 szVersion = heap_strdupAtoW(lpszVersion);
1047 if ( !szVersion )
1048 goto end;
1051 if (lpszReferrer)
1053 szReferrer = heap_strdupAtoW(lpszReferrer);
1054 if ( !szReferrer )
1055 goto end;
1058 if (lpszAcceptTypes)
1060 acceptTypesCount = 0;
1061 types = lpszAcceptTypes;
1062 while (*types)
1064 __TRY
1066 /* find out how many there are */
1067 if (*types && **types)
1069 TRACE("accept type: %s\n", debugstr_a(*types));
1070 acceptTypesCount++;
1073 __EXCEPT_PAGE_FAULT
1075 WARN("invalid accept type pointer\n");
1077 __ENDTRY;
1078 types++;
1080 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1081 if (!szAcceptTypes) goto end;
1083 acceptTypesCount = 0;
1084 types = lpszAcceptTypes;
1085 while (*types)
1087 __TRY
1089 if (*types && **types)
1090 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1092 __EXCEPT_PAGE_FAULT
1094 /* ignore invalid pointer */
1096 __ENDTRY;
1097 types++;
1099 szAcceptTypes[acceptTypesCount] = NULL;
1102 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1103 szVersion, szReferrer,
1104 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1106 end:
1107 if (szAcceptTypes)
1109 acceptTypesCount = 0;
1110 while (szAcceptTypes[acceptTypesCount])
1112 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1113 acceptTypesCount++;
1115 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1117 HeapFree(GetProcessHeap(), 0, szReferrer);
1118 HeapFree(GetProcessHeap(), 0, szVersion);
1119 HeapFree(GetProcessHeap(), 0, szObjectName);
1120 HeapFree(GetProcessHeap(), 0, szVerb);
1122 return rc;
1125 /***********************************************************************
1126 * HTTP_EncodeBase64
1128 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1130 UINT n = 0, x;
1131 static const CHAR HTTP_Base64Enc[] =
1132 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1134 while( len > 0 )
1136 /* first 6 bits, all from bin[0] */
1137 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1138 x = (bin[0] & 3) << 4;
1140 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1141 if( len == 1 )
1143 base64[n++] = HTTP_Base64Enc[x];
1144 base64[n++] = '=';
1145 base64[n++] = '=';
1146 break;
1148 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1149 x = ( bin[1] & 0x0f ) << 2;
1151 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1152 if( len == 2 )
1154 base64[n++] = HTTP_Base64Enc[x];
1155 base64[n++] = '=';
1156 break;
1158 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1160 /* last 6 bits, all from bin [2] */
1161 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1162 bin += 3;
1163 len -= 3;
1165 base64[n] = 0;
1166 return n;
1169 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1170 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1171 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1172 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1173 static const signed char HTTP_Base64Dec[256] =
1175 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1176 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1177 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1178 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1179 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1180 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1181 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1182 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1183 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1184 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1185 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1186 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1187 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1188 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1189 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1190 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1191 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1192 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1193 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1194 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1195 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1196 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1197 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1198 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1199 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1200 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1202 #undef CH
1204 /***********************************************************************
1205 * HTTP_DecodeBase64
1207 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1209 unsigned int n = 0;
1211 while(*base64)
1213 signed char in[4];
1215 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1216 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1217 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1218 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1220 WARN("invalid base64: %s\n", debugstr_w(base64));
1221 return 0;
1223 if (bin)
1224 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1225 n++;
1227 if ((base64[2] == '=') && (base64[3] == '='))
1228 break;
1229 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1230 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1232 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1233 return 0;
1235 if (bin)
1236 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1237 n++;
1239 if (base64[3] == '=')
1240 break;
1241 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1242 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1244 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1245 return 0;
1247 if (bin)
1248 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1249 n++;
1251 base64 += 4;
1254 return n;
1257 /***********************************************************************
1258 * HTTP_InsertAuthorization
1260 * Insert or delete the authorization field in the request header.
1262 static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1264 if (pAuthInfo)
1266 static const WCHAR wszSpace[] = {' ',0};
1267 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1268 unsigned int len;
1269 WCHAR *authorization = NULL;
1271 if (pAuthInfo->auth_data_len)
1273 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1274 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1275 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1276 if (!authorization)
1277 return FALSE;
1279 strcpyW(authorization, pAuthInfo->scheme);
1280 strcatW(authorization, wszSpace);
1281 HTTP_EncodeBase64(pAuthInfo->auth_data,
1282 pAuthInfo->auth_data_len,
1283 authorization+strlenW(authorization));
1285 /* clear the data as it isn't valid now that it has been sent to the
1286 * server, unless it's Basic authentication which doesn't do
1287 * connection tracking */
1288 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1290 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1291 pAuthInfo->auth_data = NULL;
1292 pAuthInfo->auth_data_len = 0;
1296 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1298 HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1300 HeapFree(GetProcessHeap(), 0, authorization);
1302 return TRUE;
1305 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1307 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1308 DWORD size;
1310 size = sizeof(new_location);
1311 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL))
1313 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1314 strcpyW( url, new_location );
1316 else
1318 static const WCHAR slash[] = { '/',0 };
1319 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1320 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1321 http_session_t *session = req->lpHttpSession;
1323 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1324 size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
1326 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1328 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1329 sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
1330 else
1331 sprintfW( url, format, session->lpszHostName, session->nHostPort );
1332 if (req->lpszPath[0] != '/') strcatW( url, slash );
1333 strcatW( url, req->lpszPath );
1335 TRACE("url=%s\n", debugstr_w(url));
1336 return url;
1339 /***********************************************************************
1340 * HTTP_DealWithProxy
1342 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
1344 WCHAR buf[MAXHOSTNAME];
1345 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1346 static WCHAR szNul[] = { 0 };
1347 URL_COMPONENTSW UrlComponents;
1348 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1349 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1351 memset( &UrlComponents, 0, sizeof UrlComponents );
1352 UrlComponents.dwStructSize = sizeof UrlComponents;
1353 UrlComponents.lpszHostName = buf;
1354 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1356 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1357 hIC->lpszProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1358 sprintfW(proxy, szFormat, hIC->lpszProxy);
1359 else
1360 strcpyW(proxy, hIC->lpszProxy);
1361 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1362 return FALSE;
1363 if( UrlComponents.dwHostNameLength == 0 )
1364 return FALSE;
1366 if( !lpwhr->lpszPath )
1367 lpwhr->lpszPath = szNul;
1369 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1370 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1372 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1373 lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1374 lpwhs->nServerPort = UrlComponents.nPort;
1376 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1377 return TRUE;
1380 #ifndef INET6_ADDRSTRLEN
1381 #define INET6_ADDRSTRLEN 46
1382 #endif
1384 static DWORD HTTP_ResolveName(http_request_t *lpwhr)
1386 char szaddr[INET6_ADDRSTRLEN];
1387 http_session_t *lpwhs = lpwhr->lpHttpSession;
1388 const void *addr;
1390 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1391 INTERNET_STATUS_RESOLVING_NAME,
1392 lpwhs->lpszServerName,
1393 strlenW(lpwhs->lpszServerName)+1);
1395 lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1396 if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1397 (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1398 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1400 switch (lpwhs->socketAddress.ss_family)
1402 case AF_INET:
1403 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1404 break;
1405 case AF_INET6:
1406 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1407 break;
1408 default:
1409 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1410 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1412 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1413 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1414 INTERNET_STATUS_NAME_RESOLVED,
1415 szaddr, strlen(szaddr)+1);
1417 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1418 return ERROR_SUCCESS;
1422 /***********************************************************************
1423 * HTTPREQ_Destroy (internal)
1425 * Deallocate request handle
1428 static void HTTPREQ_Destroy(object_header_t *hdr)
1430 http_request_t *lpwhr = (http_request_t*) hdr;
1431 DWORD i;
1433 TRACE("\n");
1435 if(lpwhr->hCacheFile)
1436 CloseHandle(lpwhr->hCacheFile);
1438 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1440 DeleteCriticalSection( &lpwhr->read_section );
1441 WININET_Release(&lpwhr->lpHttpSession->hdr);
1443 destroy_authinfo(lpwhr->pAuthInfo);
1444 destroy_authinfo(lpwhr->pProxyAuthInfo);
1446 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1447 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1448 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1449 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1450 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1452 for (i = 0; i < lpwhr->nCustHeaders; i++)
1454 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1455 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1458 #ifdef HAVE_ZLIB
1459 if(lpwhr->gzip_stream) {
1460 if(!lpwhr->gzip_stream->end_of_data)
1461 inflateEnd(&lpwhr->gzip_stream->zstream);
1462 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1464 #endif
1466 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1467 HeapFree(GetProcessHeap(), 0, lpwhr);
1470 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1472 http_request_t *lpwhr = (http_request_t*) hdr;
1474 TRACE("%p\n",lpwhr);
1476 if (!NETCON_connected(&lpwhr->netConnection))
1477 return;
1479 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1480 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1482 NETCON_close(&lpwhr->netConnection);
1484 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1485 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1488 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1490 LPHTTPHEADERW host_header;
1492 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1494 host_header = HTTP_GetHeader(req, hostW);
1495 if(!host_header)
1496 return FALSE;
1498 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1499 return TRUE;
1502 static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
1504 WCHAR szVersion[10];
1505 WCHAR szConnectionResponse[20];
1506 DWORD dwBufferSize = sizeof(szVersion);
1507 BOOL keepalive = FALSE;
1509 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1510 * the connection is keep-alive by default */
1511 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion,
1512 &dwBufferSize, NULL) &&
1513 !strcmpiW(szVersion, g_szHttp1_1))
1515 keepalive = TRUE;
1518 dwBufferSize = sizeof(szConnectionResponse);
1519 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) ||
1520 HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL))
1522 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1525 return keepalive;
1528 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1530 http_request_t *req = (http_request_t*)hdr;
1532 switch(option) {
1533 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1535 http_session_t *lpwhs = req->lpHttpSession;
1536 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1538 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1540 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1541 return ERROR_INSUFFICIENT_BUFFER;
1542 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1543 /* FIXME: can't get a SOCKET from our connection since we don't use
1544 * winsock
1546 info->Socket = 0;
1547 /* FIXME: get source port from req->netConnection */
1548 info->SourcePort = 0;
1549 info->DestPort = lpwhs->nHostPort;
1550 info->Flags = 0;
1551 if (HTTP_KeepAlive(req))
1552 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1553 if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
1554 info->Flags |= IDSI_FLAG_PROXY;
1555 if (req->netConnection.useSSL)
1556 info->Flags |= IDSI_FLAG_SECURE;
1558 return ERROR_SUCCESS;
1561 case INTERNET_OPTION_SECURITY_FLAGS:
1563 http_session_t *lpwhs;
1564 lpwhs = req->lpHttpSession;
1566 if (*size < sizeof(ULONG))
1567 return ERROR_INSUFFICIENT_BUFFER;
1569 *size = sizeof(DWORD);
1570 if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1571 *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1572 else
1573 *(DWORD*)buffer = 0;
1574 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1575 return ERROR_SUCCESS;
1578 case INTERNET_OPTION_HANDLE_TYPE:
1579 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1581 if (*size < sizeof(ULONG))
1582 return ERROR_INSUFFICIENT_BUFFER;
1584 *size = sizeof(DWORD);
1585 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1586 return ERROR_SUCCESS;
1588 case INTERNET_OPTION_URL: {
1589 WCHAR url[INTERNET_MAX_URL_LENGTH];
1590 HTTPHEADERW *host;
1591 DWORD len;
1592 WCHAR *pch;
1594 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1596 TRACE("INTERNET_OPTION_URL\n");
1598 host = HTTP_GetHeader(req, hostW);
1599 strcpyW(url, httpW);
1600 strcatW(url, host->lpszValue);
1601 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1602 *pch = 0;
1603 strcatW(url, req->lpszPath);
1605 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1607 if(unicode) {
1608 len = (strlenW(url)+1) * sizeof(WCHAR);
1609 if(*size < len)
1610 return ERROR_INSUFFICIENT_BUFFER;
1612 *size = len;
1613 strcpyW(buffer, url);
1614 return ERROR_SUCCESS;
1615 }else {
1616 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1617 if(len > *size)
1618 return ERROR_INSUFFICIENT_BUFFER;
1620 *size = len;
1621 return ERROR_SUCCESS;
1625 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1626 INTERNET_CACHE_ENTRY_INFOW *info;
1627 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1628 WCHAR url[INTERNET_MAX_URL_LENGTH];
1629 DWORD nbytes, error;
1630 BOOL ret;
1632 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1634 if (*size < sizeof(*ts))
1636 *size = sizeof(*ts);
1637 return ERROR_INSUFFICIENT_BUFFER;
1639 nbytes = 0;
1640 HTTP_GetRequestURL(req, url);
1641 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1642 error = GetLastError();
1643 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1645 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1646 return ERROR_OUTOFMEMORY;
1648 GetUrlCacheEntryInfoW(url, info, &nbytes);
1650 ts->ftExpires = info->ExpireTime;
1651 ts->ftLastModified = info->LastModifiedTime;
1653 HeapFree(GetProcessHeap(), 0, info);
1654 *size = sizeof(*ts);
1655 return ERROR_SUCCESS;
1657 return error;
1660 case INTERNET_OPTION_DATAFILE_NAME: {
1661 DWORD req_size;
1663 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1665 if(!req->lpszCacheFile) {
1666 *size = 0;
1667 return ERROR_INTERNET_ITEM_NOT_FOUND;
1670 if(unicode) {
1671 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1672 if(*size < req_size)
1673 return ERROR_INSUFFICIENT_BUFFER;
1675 *size = req_size;
1676 memcpy(buffer, req->lpszCacheFile, *size);
1677 return ERROR_SUCCESS;
1678 }else {
1679 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1680 if (req_size > *size)
1681 return ERROR_INSUFFICIENT_BUFFER;
1683 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1684 -1, buffer, *size, NULL, NULL);
1685 return ERROR_SUCCESS;
1689 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1690 PCCERT_CONTEXT context;
1692 if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1693 *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1694 return ERROR_INSUFFICIENT_BUFFER;
1697 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1698 if(context) {
1699 INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1700 DWORD len;
1702 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1703 info->ftExpiry = context->pCertInfo->NotAfter;
1704 info->ftStart = context->pCertInfo->NotBefore;
1705 if(unicode) {
1706 len = CertNameToStrW(context->dwCertEncodingType,
1707 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1708 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1709 if(info->lpszSubjectInfo)
1710 CertNameToStrW(context->dwCertEncodingType,
1711 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1712 info->lpszSubjectInfo, len);
1713 len = CertNameToStrW(context->dwCertEncodingType,
1714 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1715 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1716 if (info->lpszIssuerInfo)
1717 CertNameToStrW(context->dwCertEncodingType,
1718 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1719 info->lpszIssuerInfo, len);
1720 }else {
1721 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1723 len = CertNameToStrA(context->dwCertEncodingType,
1724 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1725 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1726 if(infoA->lpszSubjectInfo)
1727 CertNameToStrA(context->dwCertEncodingType,
1728 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1729 infoA->lpszSubjectInfo, len);
1730 len = CertNameToStrA(context->dwCertEncodingType,
1731 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1732 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1733 if(infoA->lpszIssuerInfo)
1734 CertNameToStrA(context->dwCertEncodingType,
1735 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1736 infoA->lpszIssuerInfo, len);
1740 * Contrary to MSDN, these do not appear to be set.
1741 * lpszProtocolName
1742 * lpszSignatureAlgName
1743 * lpszEncryptionAlgName
1744 * dwKeySize
1746 CertFreeCertificateContext(context);
1747 return ERROR_SUCCESS;
1752 return INET_QueryOption(option, buffer, size, unicode);
1755 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1757 http_request_t *req = (http_request_t*)hdr;
1759 switch(option) {
1760 case INTERNET_OPTION_SEND_TIMEOUT:
1761 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1762 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1764 if (size != sizeof(DWORD))
1765 return ERROR_INVALID_PARAMETER;
1767 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1768 *(DWORD*)buffer);
1770 case INTERNET_OPTION_USERNAME:
1771 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1772 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1773 return ERROR_SUCCESS;
1775 case INTERNET_OPTION_PASSWORD:
1776 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1777 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1778 return ERROR_SUCCESS;
1779 case INTERNET_OPTION_HTTP_DECODING:
1780 if(size != sizeof(BOOL))
1781 return ERROR_INVALID_PARAMETER;
1782 req->decoding = *(BOOL*)buffer;
1783 return ERROR_SUCCESS;
1786 return ERROR_INTERNET_INVALID_OPTION;
1789 /* read some more data into the read buffer (the read section must be held) */
1790 static DWORD read_more_data( http_request_t *req, int maxlen )
1792 DWORD res;
1793 int len;
1795 if (req->read_pos)
1797 /* move existing data to the start of the buffer */
1798 if(req->read_size)
1799 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1800 req->read_pos = 0;
1803 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1805 res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1806 maxlen - req->read_size, 0, &len );
1807 if(res == ERROR_SUCCESS)
1808 req->read_size += len;
1810 return res;
1813 /* remove some amount of data from the read buffer (the read section must be held) */
1814 static void remove_data( http_request_t *req, int count )
1816 if (!(req->read_size -= count)) req->read_pos = 0;
1817 else req->read_pos += count;
1820 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1822 int count, bytes_read, pos = 0;
1823 DWORD res;
1825 EnterCriticalSection( &req->read_section );
1826 for (;;)
1828 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1830 if (eol)
1832 count = eol - (req->read_buf + req->read_pos);
1833 bytes_read = count + 1;
1835 else count = bytes_read = req->read_size;
1837 count = min( count, *len - pos );
1838 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1839 pos += count;
1840 remove_data( req, bytes_read );
1841 if (eol) break;
1843 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
1845 *len = 0;
1846 TRACE( "returning empty string\n" );
1847 LeaveCriticalSection( &req->read_section );
1848 INTERNET_SetLastError(res);
1849 return FALSE;
1852 LeaveCriticalSection( &req->read_section );
1854 if (pos < *len)
1856 if (pos && buffer[pos - 1] == '\r') pos--;
1857 *len = pos + 1;
1859 buffer[*len - 1] = 0;
1860 TRACE( "returning %s\n", debugstr_a(buffer));
1861 return TRUE;
1864 /* discard data contents until we reach end of line (the read section must be held) */
1865 static DWORD discard_eol( http_request_t *req )
1867 DWORD res;
1871 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1872 if (eol)
1874 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
1875 break;
1877 req->read_pos = req->read_size = 0; /* discard everything */
1878 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1879 } while (req->read_size);
1880 return ERROR_SUCCESS;
1883 /* read the size of the next chunk (the read section must be held) */
1884 static DWORD start_next_chunk( http_request_t *req )
1886 DWORD chunk_size = 0, res;
1888 if (!req->dwContentLength) return ERROR_SUCCESS;
1889 if (req->dwContentLength == req->dwContentRead)
1891 /* read terminator for the previous chunk */
1892 if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
1893 req->dwContentLength = ~0u;
1894 req->dwContentRead = 0;
1896 for (;;)
1898 while (req->read_size)
1900 char ch = req->read_buf[req->read_pos];
1901 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
1902 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
1903 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
1904 else if (ch == ';' || ch == '\r' || ch == '\n')
1906 TRACE( "reading %u byte chunk\n", chunk_size );
1907 req->dwContentLength = chunk_size;
1908 req->dwContentRead = 0;
1909 return discard_eol( req );
1911 remove_data( req, 1 );
1913 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1914 if (!req->read_size)
1916 req->dwContentLength = req->dwContentRead = 0;
1917 return ERROR_SUCCESS;
1922 /* check if we have reached the end of the data to read (the read section must be held) */
1923 static BOOL end_of_read_data( http_request_t *req )
1925 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
1926 if (req->read_chunked) return (req->dwContentLength == 0);
1927 if (req->dwContentLength == ~0u) return FALSE;
1928 return (req->dwContentLength == req->dwContentRead);
1931 /* fetch some more data into the read buffer (the read section must be held) */
1932 static DWORD refill_buffer( http_request_t *req )
1934 int len = sizeof(req->read_buf);
1935 DWORD res;
1937 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
1939 if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
1942 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
1943 if (len <= req->read_size) return ERROR_SUCCESS;
1945 if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
1946 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
1947 return ERROR_SUCCESS;
1950 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
1952 DWORD ret = ERROR_SUCCESS;
1953 int read = 0;
1955 #ifdef HAVE_ZLIB
1956 z_stream *zstream = &req->gzip_stream->zstream;
1957 int zres;
1959 while(read < size && !req->gzip_stream->end_of_data) {
1960 if(!req->read_size) {
1961 if(!sync || refill_buffer(req) != ERROR_SUCCESS)
1962 break;
1965 zstream->next_in = req->read_buf+req->read_pos;
1966 zstream->avail_in = req->read_size;
1967 zstream->next_out = buf+read;
1968 zstream->avail_out = size-read;
1969 zres = inflate(zstream, Z_FULL_FLUSH);
1970 read = size - zstream->avail_out;
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;
2091 done:
2092 req->dwContentRead += bytes_read;
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, dwInfoLevel);
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 BOOL 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 FALSE;
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 INTERNET_SetLastError(res);
3249 return FALSE;
3251 res = NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE);
3252 if (res != ERROR_SUCCESS) {
3253 INTERNET_SetLastError(res);
3254 return FALSE;
3256 lpwhr->read_pos = lpwhr->read_size = 0;
3257 lpwhr->read_chunked = FALSE;
3260 else
3261 TRACE("Redirect through proxy\n");
3264 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3265 lpwhr->lpszPath=NULL;
3266 if (*path)
3268 DWORD needed = 0;
3269 HRESULT rc;
3271 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3272 if (rc != E_POINTER)
3273 needed = strlenW(path)+1;
3274 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3275 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3276 URL_ESCAPE_SPACES_ONLY);
3277 if (rc != S_OK)
3279 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3280 strcpyW(lpwhr->lpszPath,path);
3284 /* Remove custom content-type/length headers on redirects. */
3285 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3286 if (0 <= index)
3287 HTTP_DeleteCustomHeader(lpwhr, index);
3288 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3289 if (0 <= index)
3290 HTTP_DeleteCustomHeader(lpwhr, index);
3292 return TRUE;
3295 /***********************************************************************
3296 * HTTP_build_req (internal)
3298 * concatenate all the strings in the request together
3300 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3302 LPCWSTR *t;
3303 LPWSTR str;
3305 for( t = list; *t ; t++ )
3306 len += strlenW( *t );
3307 len++;
3309 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3310 *str = 0;
3312 for( t = list; *t ; t++ )
3313 strcatW( str, *t );
3315 return str;
3318 static DWORD HTTP_SecureProxyConnect(http_request_t *lpwhr)
3320 LPWSTR lpszPath;
3321 LPWSTR requestString;
3322 INT len;
3323 INT cnt;
3324 INT responseLen;
3325 char *ascii_req;
3326 DWORD res;
3327 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3328 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3329 http_session_t *lpwhs = lpwhr->lpHttpSession;
3331 TRACE("\n");
3333 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3334 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3335 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3336 HeapFree( GetProcessHeap(), 0, lpszPath );
3338 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3339 NULL, 0, NULL, NULL );
3340 len--; /* the nul terminator isn't needed */
3341 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3342 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3343 ascii_req, len, NULL, NULL );
3344 HeapFree( GetProcessHeap(), 0, requestString );
3346 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3348 res = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3349 HeapFree( GetProcessHeap(), 0, ascii_req );
3350 if (res != ERROR_SUCCESS)
3351 return res;
3353 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3354 if (!responseLen)
3355 return ERROR_HTTP_INVALID_HEADER;
3357 return ERROR_SUCCESS;
3360 static void HTTP_InsertCookies(http_request_t *lpwhr)
3362 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3363 LPWSTR lpszCookies, lpszUrl = NULL;
3364 DWORD nCookieSize, size;
3365 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3367 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3368 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3369 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3371 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3373 int cnt = 0;
3374 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3376 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3377 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3379 cnt += sprintfW(lpszCookies, szCookie);
3380 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3381 strcatW(lpszCookies, szCrLf);
3383 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3384 HeapFree(GetProcessHeap(), 0, lpszCookies);
3387 HeapFree(GetProcessHeap(), 0, lpszUrl);
3390 /***********************************************************************
3391 * HTTP_HttpSendRequestW (internal)
3393 * Sends the specified request to the HTTP server
3395 * RETURNS
3396 * TRUE on success
3397 * FALSE on failure
3400 BOOL HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3401 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3402 DWORD dwContentLength, BOOL bEndRequest)
3404 INT cnt;
3405 BOOL bSuccess = FALSE, redirected = FALSE;
3406 LPWSTR requestString = NULL;
3407 INT responseLen;
3408 BOOL loop_next;
3409 INTERNET_ASYNC_RESULT iar;
3410 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3411 static const WCHAR szContentLength[] =
3412 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3413 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3414 DWORD res;
3416 TRACE("--> %p\n", lpwhr);
3418 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3420 /* if the verb is NULL default to GET */
3421 if (!lpwhr->lpszVerb)
3422 lpwhr->lpszVerb = heap_strdupW(szGET);
3424 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3426 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3427 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3428 lpwhr->dwBytesToWrite = dwContentLength;
3430 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3432 WCHAR *agent_header;
3433 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3434 int len;
3436 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3437 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3438 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3440 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3441 HeapFree(GetProcessHeap(), 0, agent_header);
3443 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3445 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3446 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3448 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3450 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3451 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3452 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3457 DWORD len;
3458 char *ascii_req;
3460 loop_next = FALSE;
3462 /* like native, just in case the caller forgot to call InternetReadFile
3463 * for all the data */
3464 HTTP_DrainContent(lpwhr);
3465 lpwhr->dwContentRead = 0;
3467 if (TRACE_ON(wininet))
3469 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3470 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3473 HTTP_FixURL(lpwhr);
3474 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3476 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3478 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3479 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3481 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3482 HTTP_InsertCookies(lpwhr);
3484 /* add the headers the caller supplied */
3485 if( lpszHeaders && dwHeaderLength )
3487 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3488 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3491 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3493 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3494 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3495 HeapFree(GetProcessHeap(), 0, url);
3497 else
3498 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3501 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3503 /* Send the request and store the results */
3504 if ((res = HTTP_OpenConnection(lpwhr)) != ERROR_SUCCESS) {
3505 INTERNET_SetLastError(res);
3506 goto lend;
3509 /* send the request as ASCII, tack on the optional data */
3510 if (!lpOptional || redirected)
3511 dwOptionalLength = 0;
3512 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3513 NULL, 0, NULL, NULL );
3514 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3515 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3516 ascii_req, len, NULL, NULL );
3517 if( lpOptional )
3518 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3519 len = (len + dwOptionalLength - 1);
3520 ascii_req[len] = 0;
3521 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3523 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3524 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3526 NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3527 HeapFree( GetProcessHeap(), 0, ascii_req );
3529 lpwhr->dwBytesWritten = dwOptionalLength;
3531 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3532 INTERNET_STATUS_REQUEST_SENT,
3533 &len, sizeof(DWORD));
3535 if (bEndRequest)
3537 DWORD dwBufferSize;
3538 DWORD dwStatusCode;
3540 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3541 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3543 if (cnt < 0)
3544 goto lend;
3546 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3547 if (responseLen)
3548 bSuccess = TRUE;
3550 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3551 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3552 sizeof(DWORD));
3554 HTTP_ProcessCookies(lpwhr);
3556 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3558 dwBufferSize = sizeof(dwStatusCode);
3559 if (!HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3560 &dwStatusCode,&dwBufferSize,NULL))
3561 dwStatusCode = 0;
3563 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && bSuccess)
3565 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3566 dwBufferSize=sizeof(szNewLocation);
3567 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
3568 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL))
3570 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3572 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3573 lpwhr->lpszVerb = heap_strdupW(szGET);
3575 HTTP_DrainContent(lpwhr);
3576 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3578 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3579 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3580 bSuccess = HTTP_HandleRedirect(lpwhr, new_url);
3581 if (bSuccess)
3583 HeapFree(GetProcessHeap(), 0, requestString);
3584 loop_next = TRUE;
3586 HeapFree( GetProcessHeap(), 0, new_url );
3588 redirected = TRUE;
3591 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && bSuccess)
3593 WCHAR szAuthValue[2048];
3594 dwBufferSize=2048;
3595 if (dwStatusCode == HTTP_STATUS_DENIED)
3597 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3598 DWORD dwIndex = 0;
3599 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
3601 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3602 &lpwhr->pAuthInfo,
3603 lpwhr->lpHttpSession->lpszUserName,
3604 lpwhr->lpHttpSession->lpszPassword,
3605 Host->lpszValue))
3607 loop_next = TRUE;
3608 break;
3612 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3614 DWORD dwIndex = 0;
3615 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
3617 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3618 &lpwhr->pProxyAuthInfo,
3619 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3620 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
3621 NULL))
3623 loop_next = TRUE;
3624 break;
3630 else
3631 bSuccess = TRUE;
3633 while (loop_next);
3635 if(bSuccess) {
3636 WCHAR url[INTERNET_MAX_URL_LENGTH];
3637 WCHAR cacheFileName[MAX_PATH+1];
3638 BOOL b;
3640 b = HTTP_GetRequestURL(lpwhr, url);
3641 if(!b) {
3642 WARN("Could not get URL\n");
3643 goto lend;
3646 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3647 if(b) {
3648 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3649 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3650 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3651 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3652 WARN("Could not create file: %u\n", GetLastError());
3653 lpwhr->hCacheFile = NULL;
3655 }else {
3656 WARN("Could not create cache entry: %08x\n", GetLastError());
3660 lend:
3662 HeapFree(GetProcessHeap(), 0, requestString);
3664 /* TODO: send notification for P3P header */
3666 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3668 if (bSuccess)
3670 if (lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite) HTTP_ReceiveRequestData(lpwhr, TRUE);
3671 else
3673 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3674 iar.dwError = 0;
3676 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3677 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3678 sizeof(INTERNET_ASYNC_RESULT));
3681 else
3683 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3684 iar.dwError = INTERNET_GetLastError();
3686 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3687 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3688 sizeof(INTERNET_ASYNC_RESULT));
3692 TRACE("<--\n");
3693 if (bSuccess) INTERNET_SetLastError(ERROR_SUCCESS);
3694 return bSuccess;
3697 /***********************************************************************
3699 * Helper functions for the HttpSendRequest(Ex) functions
3702 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
3704 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
3705 http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
3707 TRACE("%p\n", lpwhr);
3709 HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
3710 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
3711 req->dwContentLength, req->bEndRequest);
3713 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
3717 static BOOL HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
3719 BOOL rc = FALSE;
3720 INT responseLen;
3721 DWORD dwBufferSize;
3722 INTERNET_ASYNC_RESULT iar;
3724 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3725 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3727 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3728 if (responseLen)
3729 rc = TRUE;
3731 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3732 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
3734 /* process cookies here. Is this right? */
3735 HTTP_ProcessCookies(lpwhr);
3737 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3739 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
3741 DWORD dwCode,dwCodeLength = sizeof(DWORD);
3742 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) &&
3743 (dwCode == 302 || dwCode == 301 || dwCode == 303))
3745 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3746 dwBufferSize=sizeof(szNewLocation);
3747 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL))
3749 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3751 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3752 lpwhr->lpszVerb = heap_strdupW(szGET);
3754 HTTP_DrainContent(lpwhr);
3755 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3757 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3758 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3759 rc = HTTP_HandleRedirect(lpwhr, new_url);
3760 if (rc)
3761 rc = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
3762 HeapFree( GetProcessHeap(), 0, new_url );
3768 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3769 iar.dwError = rc ? 0 : INTERNET_GetLastError();
3771 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3772 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3773 sizeof(INTERNET_ASYNC_RESULT));
3774 return rc;
3777 /***********************************************************************
3778 * HttpEndRequestA (WININET.@)
3780 * Ends an HTTP request that was started by HttpSendRequestEx
3782 * RETURNS
3783 * TRUE if successful
3784 * FALSE on failure
3787 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
3788 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3790 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
3792 if (lpBuffersOut)
3794 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3795 return FALSE;
3798 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
3801 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
3803 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
3804 http_request_t *lpwhr = (http_request_t*)work->hdr;
3806 TRACE("%p\n", lpwhr);
3808 HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
3811 /***********************************************************************
3812 * HttpEndRequestW (WININET.@)
3814 * Ends an HTTP request that was started by HttpSendRequestEx
3816 * RETURNS
3817 * TRUE if successful
3818 * FALSE on failure
3821 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
3822 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3824 BOOL rc = FALSE;
3825 http_request_t *lpwhr;
3827 TRACE("-->\n");
3829 if (lpBuffersOut)
3831 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3832 return FALSE;
3835 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3837 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3839 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3840 if (lpwhr)
3841 WININET_Release( &lpwhr->hdr );
3842 return FALSE;
3844 lpwhr->hdr.dwFlags |= dwFlags;
3846 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3848 WORKREQUEST work;
3849 struct WORKREQ_HTTPENDREQUESTW *request;
3851 work.asyncproc = AsyncHttpEndRequestProc;
3852 work.hdr = WININET_AddRef( &lpwhr->hdr );
3854 request = &work.u.HttpEndRequestW;
3855 request->dwFlags = dwFlags;
3856 request->dwContext = dwContext;
3858 INTERNET_AsyncCall(&work);
3859 INTERNET_SetLastError(ERROR_IO_PENDING);
3861 else
3862 rc = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
3864 WININET_Release( &lpwhr->hdr );
3865 TRACE("%i <--\n",rc);
3866 return rc;
3869 /***********************************************************************
3870 * HttpSendRequestExA (WININET.@)
3872 * Sends the specified request to the HTTP server and allows chunked
3873 * transfers.
3875 * RETURNS
3876 * Success: TRUE
3877 * Failure: FALSE, call GetLastError() for more information.
3879 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3880 LPINTERNET_BUFFERSA lpBuffersIn,
3881 LPINTERNET_BUFFERSA lpBuffersOut,
3882 DWORD dwFlags, DWORD_PTR dwContext)
3884 INTERNET_BUFFERSW BuffersInW;
3885 BOOL rc = FALSE;
3886 DWORD headerlen;
3887 LPWSTR header = NULL;
3889 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3890 lpBuffersOut, dwFlags, dwContext);
3892 if (lpBuffersIn)
3894 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3895 if (lpBuffersIn->lpcszHeader)
3897 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3898 lpBuffersIn->dwHeadersLength,0,0);
3899 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3900 if (!(BuffersInW.lpcszHeader = header))
3902 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3903 return FALSE;
3905 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3906 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3907 header, headerlen);
3909 else
3910 BuffersInW.lpcszHeader = NULL;
3911 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3912 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3913 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3914 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3915 BuffersInW.Next = NULL;
3918 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3920 HeapFree(GetProcessHeap(),0,header);
3922 return rc;
3925 /***********************************************************************
3926 * HttpSendRequestExW (WININET.@)
3928 * Sends the specified request to the HTTP server and allows chunked
3929 * transfers
3931 * RETURNS
3932 * Success: TRUE
3933 * Failure: FALSE, call GetLastError() for more information.
3935 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3936 LPINTERNET_BUFFERSW lpBuffersIn,
3937 LPINTERNET_BUFFERSW lpBuffersOut,
3938 DWORD dwFlags, DWORD_PTR dwContext)
3940 BOOL ret = FALSE;
3941 http_request_t *lpwhr;
3942 http_session_t *lpwhs;
3943 appinfo_t *hIC;
3945 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3946 lpBuffersOut, dwFlags, dwContext);
3948 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3950 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3952 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3953 goto lend;
3956 lpwhs = lpwhr->lpHttpSession;
3957 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3958 hIC = lpwhs->lpAppInfo;
3959 assert(hIC->hdr.htype == WH_HINIT);
3961 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3963 WORKREQUEST workRequest;
3964 struct WORKREQ_HTTPSENDREQUESTW *req;
3966 workRequest.asyncproc = AsyncHttpSendRequestProc;
3967 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3968 req = &workRequest.u.HttpSendRequestW;
3969 if (lpBuffersIn)
3971 DWORD size = 0;
3973 if (lpBuffersIn->lpcszHeader)
3975 if (lpBuffersIn->dwHeadersLength == ~0u)
3976 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
3977 else
3978 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
3980 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
3981 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
3983 else req->lpszHeader = NULL;
3985 req->dwHeaderLength = size / sizeof(WCHAR);
3986 req->lpOptional = lpBuffersIn->lpvBuffer;
3987 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3988 req->dwContentLength = lpBuffersIn->dwBufferTotal;
3990 else
3992 req->lpszHeader = NULL;
3993 req->dwHeaderLength = 0;
3994 req->lpOptional = NULL;
3995 req->dwOptionalLength = 0;
3996 req->dwContentLength = 0;
3999 req->bEndRequest = FALSE;
4001 INTERNET_AsyncCall(&workRequest);
4003 * This is from windows.
4005 INTERNET_SetLastError(ERROR_IO_PENDING);
4007 else
4009 if (lpBuffersIn)
4010 ret = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4011 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
4012 lpBuffersIn->dwBufferTotal, FALSE);
4013 else
4014 ret = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
4017 lend:
4018 if ( lpwhr )
4019 WININET_Release( &lpwhr->hdr );
4021 TRACE("<---\n");
4022 return ret;
4025 /***********************************************************************
4026 * HttpSendRequestW (WININET.@)
4028 * Sends the specified request to the HTTP server
4030 * RETURNS
4031 * TRUE on success
4032 * FALSE on failure
4035 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4036 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4038 http_request_t *lpwhr;
4039 http_session_t *lpwhs = NULL;
4040 appinfo_t *hIC = NULL;
4041 DWORD res = ERROR_SUCCESS;
4043 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4044 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4046 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
4047 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4049 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4050 goto lend;
4053 lpwhs = lpwhr->lpHttpSession;
4054 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
4056 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4057 goto lend;
4060 hIC = lpwhs->lpAppInfo;
4061 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
4063 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4064 goto lend;
4067 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4069 WORKREQUEST workRequest;
4070 struct WORKREQ_HTTPSENDREQUESTW *req;
4072 workRequest.asyncproc = AsyncHttpSendRequestProc;
4073 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4074 req = &workRequest.u.HttpSendRequestW;
4075 if (lpszHeaders)
4077 DWORD size;
4079 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4080 else size = dwHeaderLength * sizeof(WCHAR);
4082 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4083 memcpy(req->lpszHeader, lpszHeaders, size);
4085 else
4086 req->lpszHeader = 0;
4087 req->dwHeaderLength = dwHeaderLength;
4088 req->lpOptional = lpOptional;
4089 req->dwOptionalLength = dwOptionalLength;
4090 req->dwContentLength = dwOptionalLength;
4091 req->bEndRequest = TRUE;
4093 INTERNET_AsyncCall(&workRequest);
4095 * This is from windows.
4097 res = ERROR_IO_PENDING;
4099 else
4101 BOOL r = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
4102 dwHeaderLength, lpOptional, dwOptionalLength,
4103 dwOptionalLength, TRUE);
4104 if(!r)
4105 res = INTERNET_GetLastError();
4107 lend:
4108 if( lpwhr )
4109 WININET_Release( &lpwhr->hdr );
4111 if(res != ERROR_SUCCESS)
4112 SetLastError(res);
4113 return res == ERROR_SUCCESS;
4116 /***********************************************************************
4117 * HttpSendRequestA (WININET.@)
4119 * Sends the specified request to the HTTP server
4121 * RETURNS
4122 * TRUE on success
4123 * FALSE on failure
4126 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4127 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4129 BOOL result;
4130 LPWSTR szHeaders=NULL;
4131 DWORD nLen=dwHeaderLength;
4132 if(lpszHeaders!=NULL)
4134 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4135 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4136 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4138 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4139 HeapFree(GetProcessHeap(),0,szHeaders);
4140 return result;
4143 /***********************************************************************
4144 * HTTPSESSION_Destroy (internal)
4146 * Deallocate session handle
4149 static void HTTPSESSION_Destroy(object_header_t *hdr)
4151 http_session_t *lpwhs = (http_session_t*) hdr;
4153 TRACE("%p\n", lpwhs);
4155 WININET_Release(&lpwhs->lpAppInfo->hdr);
4157 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4158 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4159 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4160 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4161 HeapFree(GetProcessHeap(), 0, lpwhs);
4164 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4166 switch(option) {
4167 case INTERNET_OPTION_HANDLE_TYPE:
4168 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4170 if (*size < sizeof(ULONG))
4171 return ERROR_INSUFFICIENT_BUFFER;
4173 *size = sizeof(DWORD);
4174 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4175 return ERROR_SUCCESS;
4178 return INET_QueryOption(option, buffer, size, unicode);
4181 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4183 http_session_t *ses = (http_session_t*)hdr;
4185 switch(option) {
4186 case INTERNET_OPTION_USERNAME:
4188 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4189 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4190 return ERROR_SUCCESS;
4192 case INTERNET_OPTION_PASSWORD:
4194 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4195 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4196 return ERROR_SUCCESS;
4198 default: break;
4201 return ERROR_INTERNET_INVALID_OPTION;
4204 static const object_vtbl_t HTTPSESSIONVtbl = {
4205 HTTPSESSION_Destroy,
4206 NULL,
4207 HTTPSESSION_QueryOption,
4208 HTTPSESSION_SetOption,
4209 NULL,
4210 NULL,
4211 NULL,
4212 NULL,
4213 NULL
4217 /***********************************************************************
4218 * HTTP_Connect (internal)
4220 * Create http session handle
4222 * RETURNS
4223 * HINTERNET a session handle on success
4224 * NULL on failure
4227 HINTERNET HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4228 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4229 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4230 DWORD dwInternalFlags)
4232 http_session_t *lpwhs = NULL;
4233 HINTERNET handle = NULL;
4235 TRACE("-->\n");
4237 if (!lpszServerName || !lpszServerName[0])
4239 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4240 goto lerror;
4243 assert( hIC->hdr.htype == WH_HINIT );
4245 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4246 if (NULL == lpwhs)
4248 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4249 goto lerror;
4253 * According to my tests. The name is not resolved until a request is sent
4256 lpwhs->hdr.htype = WH_HHTTPSESSION;
4257 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4258 lpwhs->hdr.dwFlags = dwFlags;
4259 lpwhs->hdr.dwContext = dwContext;
4260 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4261 lpwhs->hdr.refs = 1;
4262 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4264 WININET_AddRef( &hIC->hdr );
4265 lpwhs->lpAppInfo = hIC;
4266 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4268 handle = WININET_AllocHandle( &lpwhs->hdr );
4269 if (NULL == handle)
4271 ERR("Failed to alloc handle\n");
4272 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4273 goto lerror;
4276 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4277 if(strchrW(hIC->lpszProxy, ' '))
4278 FIXME("Several proxies not implemented.\n");
4279 if(hIC->lpszProxyBypass)
4280 FIXME("Proxy bypass is ignored.\n");
4282 if (lpszServerName && lpszServerName[0])
4284 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4285 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4287 if (lpszUserName && lpszUserName[0])
4288 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4289 if (lpszPassword && lpszPassword[0])
4290 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4291 lpwhs->nServerPort = nServerPort;
4292 lpwhs->nHostPort = nServerPort;
4294 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4295 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4297 INTERNET_SendCallback(&hIC->hdr, dwContext,
4298 INTERNET_STATUS_HANDLE_CREATED, &handle,
4299 sizeof(handle));
4302 lerror:
4303 if( lpwhs )
4304 WININET_Release( &lpwhs->hdr );
4307 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4308 * windows
4311 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4312 return handle;
4316 /***********************************************************************
4317 * HTTP_OpenConnection (internal)
4319 * Connect to a web server
4321 * RETURNS
4323 * TRUE on success
4324 * FALSE on failure
4326 static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
4328 http_session_t *lpwhs;
4329 appinfo_t *hIC = NULL;
4330 char szaddr[INET6_ADDRSTRLEN];
4331 const void *addr;
4332 DWORD res = ERROR_SUCCESS;
4334 TRACE("-->\n");
4337 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4339 res = ERROR_INVALID_PARAMETER;
4340 goto lend;
4343 if (NETCON_connected(&lpwhr->netConnection))
4344 goto lend;
4345 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
4347 lpwhs = lpwhr->lpHttpSession;
4349 hIC = lpwhs->lpAppInfo;
4350 switch (lpwhs->socketAddress.ss_family)
4352 case AF_INET:
4353 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4354 break;
4355 case AF_INET6:
4356 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4357 break;
4358 default:
4359 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4360 return ERROR_INTERNET_NAME_NOT_RESOLVED;
4362 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4363 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4364 INTERNET_STATUS_CONNECTING_TO_SERVER,
4365 szaddr,
4366 strlen(szaddr)+1);
4368 res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
4369 if (res != ERROR_SUCCESS)
4371 WARN("Socket creation failed: %u\n", res);
4372 goto lend;
4375 res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4376 lpwhs->sa_len);
4377 if(res != ERROR_SUCCESS)
4378 goto lend;
4380 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4382 /* Note: we differ from Microsoft's WinINet here. they seem to have
4383 * a bug that causes no status callbacks to be sent when starting
4384 * a tunnel to a proxy server using the CONNECT verb. i believe our
4385 * behaviour to be more correct and to not cause any incompatibilities
4386 * because using a secure connection through a proxy server is a rare
4387 * case that would be hard for anyone to depend on */
4388 if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS)
4389 goto lend;
4391 res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
4392 if(res != ERROR_SUCCESS)
4394 WARN("Couldn't connect securely to host\n");
4395 goto lend;
4399 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4400 INTERNET_STATUS_CONNECTED_TO_SERVER,
4401 szaddr, strlen(szaddr)+1);
4403 lend:
4404 lpwhr->read_pos = lpwhr->read_size = 0;
4405 lpwhr->read_chunked = FALSE;
4407 TRACE("%d <--\n", res);
4408 return res;
4412 /***********************************************************************
4413 * HTTP_clear_response_headers (internal)
4415 * clear out any old response headers
4417 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4419 DWORD i;
4421 for( i=0; i<lpwhr->nCustHeaders; i++)
4423 if( !lpwhr->pCustHeaders[i].lpszField )
4424 continue;
4425 if( !lpwhr->pCustHeaders[i].lpszValue )
4426 continue;
4427 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4428 continue;
4429 HTTP_DeleteCustomHeader( lpwhr, i );
4430 i--;
4434 /***********************************************************************
4435 * HTTP_GetResponseHeaders (internal)
4437 * Read server response
4439 * RETURNS
4441 * TRUE on success
4442 * FALSE on error
4444 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4446 INT cbreaks = 0;
4447 WCHAR buffer[MAX_REPLY_LEN];
4448 DWORD buflen = MAX_REPLY_LEN;
4449 BOOL bSuccess = FALSE;
4450 INT rc = 0;
4451 char bufferA[MAX_REPLY_LEN];
4452 LPWSTR status_code = NULL, status_text = NULL;
4453 DWORD cchMaxRawHeaders = 1024;
4454 LPWSTR lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4455 LPWSTR temp;
4456 DWORD cchRawHeaders = 0;
4457 BOOL codeHundred = FALSE;
4459 TRACE("-->\n");
4461 /* clear old response headers (eg. from a redirect response) */
4462 if (clear) HTTP_clear_response_headers( lpwhr );
4464 if (!NETCON_connected(&lpwhr->netConnection))
4465 goto lend;
4467 do {
4468 static const WCHAR szHundred[] = {'1','0','0',0};
4470 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4472 buflen = MAX_REPLY_LEN;
4473 if (!read_line(lpwhr, bufferA, &buflen))
4474 goto lend;
4475 rc += buflen;
4476 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4477 /* check is this a status code line? */
4478 if (!strncmpW(buffer, g_szHttp1_0, 4))
4480 /* split the version from the status code */
4481 status_code = strchrW( buffer, ' ' );
4482 if( !status_code )
4483 goto lend;
4484 *status_code++=0;
4486 /* split the status code from the status text */
4487 status_text = strchrW( status_code, ' ' );
4488 if( !status_text )
4489 goto lend;
4490 *status_text++=0;
4492 TRACE("version [%s] status code [%s] status text [%s]\n",
4493 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4495 codeHundred = (!strcmpW(status_code, szHundred));
4497 else if (!codeHundred)
4499 FIXME("Non status line at head of response (%s)\n",debugstr_w(buffer));
4500 goto lend;
4502 } while (codeHundred);
4504 /* Add status code */
4505 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4506 HTTP_ADDHDR_FLAG_REPLACE);
4508 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4509 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4511 lpwhr->lpszVersion = heap_strdupW(buffer);
4512 lpwhr->lpszStatusText = heap_strdupW(status_text);
4514 /* Restore the spaces */
4515 *(status_code-1) = ' ';
4516 *(status_text-1) = ' ';
4518 /* regenerate raw headers */
4519 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4520 cchMaxRawHeaders *= 2;
4521 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4522 if (temp == NULL) goto lend;
4523 lpszRawHeaders = temp;
4524 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4525 cchRawHeaders += (buflen-1);
4526 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4527 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4528 lpszRawHeaders[cchRawHeaders] = '\0';
4530 /* Parse each response line */
4533 buflen = MAX_REPLY_LEN;
4534 if (read_line(lpwhr, bufferA, &buflen))
4536 LPWSTR * pFieldAndValue;
4538 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4540 if (!bufferA[0]) break;
4541 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4543 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4544 if (pFieldAndValue)
4546 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4547 cchMaxRawHeaders *= 2;
4548 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4549 if (temp == NULL) goto lend;
4550 lpszRawHeaders = temp;
4551 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4552 cchRawHeaders += (buflen-1);
4553 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4554 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4555 lpszRawHeaders[cchRawHeaders] = '\0';
4557 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4558 HTTP_ADDREQ_FLAG_ADD );
4560 HTTP_FreeTokens(pFieldAndValue);
4563 else
4565 cbreaks++;
4566 if (cbreaks >= 2)
4567 break;
4569 }while(1);
4571 /* make sure the response header is terminated with an empty line. Some apps really
4572 truly care about that empty line being there for some reason. Just add it to the
4573 header. */
4574 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4576 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4577 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4578 if (temp == NULL) goto lend;
4579 lpszRawHeaders = temp;
4582 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4584 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4585 lpwhr->lpszRawHeaders = lpszRawHeaders;
4586 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4587 bSuccess = TRUE;
4589 lend:
4591 TRACE("<--\n");
4592 if (bSuccess)
4593 return rc;
4594 else
4596 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4597 return 0;
4601 /***********************************************************************
4602 * HTTP_InterpretHttpHeader (internal)
4604 * Parse server response
4606 * RETURNS
4608 * Pointer to array of field, value, NULL on success.
4609 * NULL on error.
4611 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4613 LPWSTR * pTokenPair;
4614 LPWSTR pszColon;
4615 INT len;
4617 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4619 pszColon = strchrW(buffer, ':');
4620 /* must have two tokens */
4621 if (!pszColon)
4623 HTTP_FreeTokens(pTokenPair);
4624 if (buffer[0])
4625 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4626 return NULL;
4629 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4630 if (!pTokenPair[0])
4632 HTTP_FreeTokens(pTokenPair);
4633 return NULL;
4635 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4636 pTokenPair[0][pszColon - buffer] = '\0';
4638 /* skip colon */
4639 pszColon++;
4640 len = strlenW(pszColon);
4641 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4642 if (!pTokenPair[1])
4644 HTTP_FreeTokens(pTokenPair);
4645 return NULL;
4647 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4649 strip_spaces(pTokenPair[0]);
4650 strip_spaces(pTokenPair[1]);
4652 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4653 return pTokenPair;
4656 /***********************************************************************
4657 * HTTP_ProcessHeader (internal)
4659 * Stuff header into header tables according to <dwModifier>
4663 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4665 static DWORD HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4667 LPHTTPHEADERW lphttpHdr = NULL;
4668 INT index = -1;
4669 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4670 DWORD res = ERROR_HTTP_INVALID_HEADER;
4672 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4674 /* REPLACE wins out over ADD */
4675 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4676 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4678 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4679 index = -1;
4680 else
4681 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4683 if (index >= 0)
4685 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4686 return ERROR_HTTP_INVALID_HEADER;
4687 lphttpHdr = &lpwhr->pCustHeaders[index];
4689 else if (value)
4691 HTTPHEADERW hdr;
4693 hdr.lpszField = (LPWSTR)field;
4694 hdr.lpszValue = (LPWSTR)value;
4695 hdr.wFlags = hdr.wCount = 0;
4697 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4698 hdr.wFlags |= HDR_ISREQUEST;
4700 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4702 /* no value to delete */
4703 else return ERROR_SUCCESS;
4705 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4706 lphttpHdr->wFlags |= HDR_ISREQUEST;
4707 else
4708 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4710 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4712 HTTP_DeleteCustomHeader( lpwhr, index );
4714 if (value)
4716 HTTPHEADERW hdr;
4718 hdr.lpszField = (LPWSTR)field;
4719 hdr.lpszValue = (LPWSTR)value;
4720 hdr.wFlags = hdr.wCount = 0;
4722 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4723 hdr.wFlags |= HDR_ISREQUEST;
4725 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4728 return ERROR_SUCCESS;
4730 else if (dwModifier & COALESCEFLAGS)
4732 LPWSTR lpsztmp;
4733 WCHAR ch = 0;
4734 INT len = 0;
4735 INT origlen = strlenW(lphttpHdr->lpszValue);
4736 INT valuelen = strlenW(value);
4738 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4740 ch = ',';
4741 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4743 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4745 ch = ';';
4746 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4749 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4751 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4752 if (lpsztmp)
4754 lphttpHdr->lpszValue = lpsztmp;
4755 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4756 if (ch > 0)
4758 lphttpHdr->lpszValue[origlen] = ch;
4759 origlen++;
4760 lphttpHdr->lpszValue[origlen] = ' ';
4761 origlen++;
4764 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4765 lphttpHdr->lpszValue[len] = '\0';
4766 res = ERROR_SUCCESS;
4768 else
4770 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4771 res = ERROR_OUTOFMEMORY;
4774 TRACE("<-- %d\n", res);
4775 return res;
4779 /***********************************************************************
4780 * HTTP_FinishedReading (internal)
4782 * Called when all content from server has been read by client.
4785 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4787 BOOL keepalive = HTTP_KeepAlive(lpwhr);
4789 TRACE("\n");
4792 if (!keepalive)
4794 HTTPREQ_CloseConnection(&lpwhr->hdr);
4797 /* FIXME: store data in the URL cache here */
4799 return TRUE;
4803 /***********************************************************************
4804 * HTTP_GetCustomHeaderIndex (internal)
4806 * Return index of custom header from header array
4809 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
4810 int requested_index, BOOL request_only)
4812 DWORD index;
4814 TRACE("%s\n", debugstr_w(lpszField));
4816 for (index = 0; index < lpwhr->nCustHeaders; index++)
4818 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
4819 continue;
4821 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4822 continue;
4824 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4825 continue;
4827 if (requested_index == 0)
4828 break;
4829 requested_index --;
4832 if (index >= lpwhr->nCustHeaders)
4833 index = -1;
4835 TRACE("Return: %d\n", index);
4836 return index;
4840 /***********************************************************************
4841 * HTTP_InsertCustomHeader (internal)
4843 * Insert header into array
4846 static DWORD HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
4848 INT count;
4849 LPHTTPHEADERW lph = NULL;
4851 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
4852 count = lpwhr->nCustHeaders + 1;
4853 if (count > 1)
4854 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
4855 else
4856 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
4858 if (!lph)
4859 return ERROR_OUTOFMEMORY;
4861 lpwhr->pCustHeaders = lph;
4862 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
4863 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
4864 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
4865 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
4866 lpwhr->nCustHeaders++;
4868 return ERROR_SUCCESS;
4872 /***********************************************************************
4873 * HTTP_DeleteCustomHeader (internal)
4875 * Delete header from array
4876 * If this function is called, the indexs may change.
4878 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
4880 if( lpwhr->nCustHeaders <= 0 )
4881 return FALSE;
4882 if( index >= lpwhr->nCustHeaders )
4883 return FALSE;
4884 lpwhr->nCustHeaders--;
4886 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
4887 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
4889 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
4890 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
4891 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
4893 return TRUE;
4897 /***********************************************************************
4898 * HTTP_VerifyValidHeader (internal)
4900 * Verify the given header is not invalid for the given http request
4903 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
4905 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4906 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
4907 return ERROR_HTTP_INVALID_HEADER;
4909 return ERROR_SUCCESS;
4912 /***********************************************************************
4913 * IsHostInProxyBypassList (@)
4915 * Undocumented
4918 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
4920 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
4921 return FALSE;