push 149f0a5527ac85057a8ef03858d34d91c36f97e8
[wine/hacks.git] / dlls / wininet / http.c
blob024d5d816df914c75ade348cba83a4835989cdc3
1 /*
2 * Wininet - Http Implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2002 CodeWeavers Inc.
6 * Copyright 2002 TransGaming Technologies Inc.
7 * Copyright 2004 Mike McCormack for CodeWeavers
8 * Copyright 2005 Aric Stewart for CodeWeavers
9 * Copyright 2006 Robert Shearman for CodeWeavers
11 * Ulrich Czekalla
12 * David Hammerton
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
32 #if defined(__MINGW32__) || defined (_MSC_VER)
33 #include <ws2tcpip.h>
34 #endif
36 #include <sys/types.h>
37 #ifdef HAVE_SYS_SOCKET_H
38 # include <sys/socket.h>
39 #endif
40 #ifdef HAVE_ARPA_INET_H
41 # include <arpa/inet.h>
42 #endif
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #ifdef HAVE_UNISTD_H
47 # include <unistd.h>
48 #endif
49 #include <time.h>
50 #include <assert.h>
51 #ifdef HAVE_ZLIB
52 # include <zlib.h>
53 #endif
55 #include "windef.h"
56 #include "winbase.h"
57 #include "wininet.h"
58 #include "winerror.h"
59 #define NO_SHLWAPI_STREAM
60 #define NO_SHLWAPI_REG
61 #define NO_SHLWAPI_STRFCNS
62 #define NO_SHLWAPI_GDI
63 #include "shlwapi.h"
64 #include "sspi.h"
65 #include "wincrypt.h"
67 #include "internet.h"
68 #include "wine/debug.h"
69 #include "wine/exception.h"
70 #include "wine/unicode.h"
72 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
74 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
75 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
76 static const WCHAR hostW[] = { 'H','o','s','t',0 };
77 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
78 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
79 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
80 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
81 static const WCHAR szGET[] = { 'G','E','T', 0 };
82 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
83 static const WCHAR szCrLf[] = {'\r','\n', 0};
85 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
86 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
87 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
88 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
89 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
90 static const WCHAR szAge[] = { 'A','g','e',0 };
91 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
92 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
93 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
94 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
95 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
96 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
97 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
98 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
99 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
100 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
101 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
102 static const WCHAR szContent_Transfer_Encoding[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
103 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
104 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
105 static const WCHAR szDate[] = { 'D','a','t','e',0 };
106 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
107 static const WCHAR szETag[] = { 'E','T','a','g',0 };
108 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
109 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
110 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
111 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
112 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
113 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
114 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
115 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
116 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
117 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
118 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
119 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
120 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
121 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
122 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
123 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
124 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
125 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
126 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
127 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
128 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
129 static const WCHAR szUnless_Modified_Since[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
130 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
131 static const WCHAR szURI[] = { 'U','R','I',0 };
132 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
133 static const WCHAR szVary[] = { 'V','a','r','y',0 };
134 static const WCHAR szVia[] = { 'V','i','a',0 };
135 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
136 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
138 #define MAXHOSTNAME 100
139 #define MAX_FIELD_VALUE_LEN 256
140 #define MAX_FIELD_LEN 256
142 #define HTTP_REFERER szReferer
143 #define HTTP_ACCEPT szAccept
144 #define HTTP_USERAGENT szUser_Agent
146 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
147 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
148 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
149 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
150 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
151 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
152 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
154 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
156 struct HttpAuthInfo
158 LPWSTR scheme;
159 CredHandle cred;
160 CtxtHandle ctx;
161 TimeStamp exp;
162 ULONG attr;
163 ULONG max_token;
164 void *auth_data;
165 unsigned int auth_data_len;
166 BOOL finished; /* finished authenticating */
170 struct gzip_stream_t {
171 #ifdef HAVE_ZLIB
172 z_stream zstream;
173 #endif
174 BYTE buf[8192];
175 DWORD buf_size;
176 DWORD buf_pos;
177 BOOL end_of_data;
180 typedef struct _authorizationData
182 struct list entry;
184 LPWSTR lpszwHost;
185 LPWSTR lpszwRealm;
186 LPSTR lpszAuthorization;
187 UINT AuthorizationLen;
188 } authorizationData;
190 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
192 static CRITICAL_SECTION authcache_cs;
193 static CRITICAL_SECTION_DEBUG critsect_debug =
195 0, 0, &authcache_cs,
196 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
197 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
199 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
201 static DWORD HTTP_OpenConnection(http_request_t *req);
202 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
203 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
204 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
205 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
206 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
207 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
208 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
209 static BOOL HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
210 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
211 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
212 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
213 static void HTTP_DrainContent(http_request_t *req);
214 static BOOL HTTP_FinishedReading(http_request_t *req);
216 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
218 int HeaderIndex = 0;
219 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
220 if (HeaderIndex == -1)
221 return NULL;
222 else
223 return &req->pCustHeaders[HeaderIndex];
226 #ifdef HAVE_ZLIB
228 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
230 return HeapAlloc(GetProcessHeap(), 0, items*size);
233 static void wininet_zfree(voidpf opaque, voidpf address)
235 HeapFree(GetProcessHeap(), 0, address);
238 static void init_gzip_stream(http_request_t *req)
240 gzip_stream_t *gzip_stream;
241 int index, zres;
243 gzip_stream = HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t));
244 gzip_stream->zstream.zalloc = wininet_zalloc;
245 gzip_stream->zstream.zfree = wininet_zfree;
246 gzip_stream->zstream.opaque = NULL;
247 gzip_stream->zstream.next_in = NULL;
248 gzip_stream->zstream.avail_in = 0;
249 gzip_stream->zstream.next_out = NULL;
250 gzip_stream->zstream.avail_out = 0;
251 gzip_stream->buf_pos = 0;
252 gzip_stream->buf_size = 0;
253 gzip_stream->end_of_data = FALSE;
255 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
256 if(zres != Z_OK) {
257 ERR("inflateInit failed: %d\n", zres);
258 HeapFree(GetProcessHeap(), 0, gzip_stream);
259 return;
262 req->gzip_stream = gzip_stream;
264 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
265 if(index != -1)
266 HTTP_DeleteCustomHeader(req, index);
269 #else
271 static void init_gzip_stream(http_request_t *req)
273 ERR("gzip stream not supported, missing zlib.\n");
276 #endif
278 /* set the request content length based on the headers */
279 static DWORD set_content_length( http_request_t *lpwhr )
281 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
282 WCHAR encoding[20];
283 DWORD size;
285 size = sizeof(lpwhr->dwContentLength);
286 if (!HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
287 &lpwhr->dwContentLength, &size, NULL))
288 lpwhr->dwContentLength = ~0u;
290 size = sizeof(encoding);
291 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) &&
292 !strcmpiW(encoding, szChunked))
294 lpwhr->dwContentLength = ~0u;
295 lpwhr->read_chunked = TRUE;
298 if(lpwhr->decoding) {
299 int encoding_idx;
301 static const WCHAR gzipW[] = {'g','z','i','p',0};
303 encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
304 if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
305 init_gzip_stream(lpwhr);
308 return lpwhr->dwContentLength;
311 /***********************************************************************
312 * HTTP_Tokenize (internal)
314 * Tokenize a string, allocating memory for the tokens.
316 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
318 LPWSTR * token_array;
319 int tokens = 0;
320 int i;
321 LPCWSTR next_token;
323 if (string)
325 /* empty string has no tokens */
326 if (*string)
327 tokens++;
328 /* count tokens */
329 for (i = 0; string[i]; i++)
331 if (!strncmpW(string+i, token_string, strlenW(token_string)))
333 DWORD j;
334 tokens++;
335 /* we want to skip over separators, but not the null terminator */
336 for (j = 0; j < strlenW(token_string) - 1; j++)
337 if (!string[i+j])
338 break;
339 i += j;
344 /* add 1 for terminating NULL */
345 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
346 token_array[tokens] = NULL;
347 if (!tokens)
348 return token_array;
349 for (i = 0; i < tokens; i++)
351 int len;
352 next_token = strstrW(string, token_string);
353 if (!next_token) next_token = string+strlenW(string);
354 len = next_token - string;
355 token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
356 memcpy(token_array[i], string, len*sizeof(WCHAR));
357 token_array[i][len] = '\0';
358 string = next_token+strlenW(token_string);
360 return token_array;
363 /***********************************************************************
364 * HTTP_FreeTokens (internal)
366 * Frees memory returned from HTTP_Tokenize.
368 static void HTTP_FreeTokens(LPWSTR * token_array)
370 int i;
371 for (i = 0; token_array[i]; i++)
372 HeapFree(GetProcessHeap(), 0, token_array[i]);
373 HeapFree(GetProcessHeap(), 0, token_array);
376 static void HTTP_FixURL(http_request_t *lpwhr)
378 static const WCHAR szSlash[] = { '/',0 };
379 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
381 /* If we don't have a path we set it to root */
382 if (NULL == lpwhr->lpszPath)
383 lpwhr->lpszPath = heap_strdupW(szSlash);
384 else /* remove \r and \n*/
386 int nLen = strlenW(lpwhr->lpszPath);
387 while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
389 nLen--;
390 lpwhr->lpszPath[nLen]='\0';
392 /* Replace '\' with '/' */
393 while (nLen>0) {
394 nLen--;
395 if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
399 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
400 lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
401 && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
403 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
404 (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
405 *fixurl = '/';
406 strcpyW(fixurl + 1, lpwhr->lpszPath);
407 HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
408 lpwhr->lpszPath = fixurl;
412 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
414 LPWSTR requestString;
415 DWORD len, n;
416 LPCWSTR *req;
417 UINT i;
418 LPWSTR p;
420 static const WCHAR szSpace[] = { ' ',0 };
421 static const WCHAR szColon[] = { ':',' ',0 };
422 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
424 /* allocate space for an array of all the string pointers to be added */
425 len = (lpwhr->nCustHeaders)*4 + 10;
426 req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
428 /* add the verb, path and HTTP version string */
429 n = 0;
430 req[n++] = verb;
431 req[n++] = szSpace;
432 req[n++] = path;
433 req[n++] = szSpace;
434 req[n++] = version;
436 /* Append custom request headers */
437 for (i = 0; i < lpwhr->nCustHeaders; i++)
439 if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
441 req[n++] = szCrLf;
442 req[n++] = lpwhr->pCustHeaders[i].lpszField;
443 req[n++] = szColon;
444 req[n++] = lpwhr->pCustHeaders[i].lpszValue;
446 TRACE("Adding custom header %s (%s)\n",
447 debugstr_w(lpwhr->pCustHeaders[i].lpszField),
448 debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
452 if( n >= len )
453 ERR("oops. buffer overrun\n");
455 req[n] = NULL;
456 requestString = HTTP_build_req( req, 4 );
457 HeapFree( GetProcessHeap(), 0, req );
460 * Set (header) termination string for request
461 * Make sure there's exactly two new lines at the end of the request
463 p = &requestString[strlenW(requestString)-1];
464 while ( (*p == '\n') || (*p == '\r') )
465 p--;
466 strcpyW( p+1, sztwocrlf );
468 return requestString;
471 static void HTTP_ProcessCookies( http_request_t *lpwhr )
473 int HeaderIndex;
474 int numCookies = 0;
475 LPHTTPHEADERW setCookieHeader;
477 while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
479 setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
481 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
483 int len;
484 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
485 LPWSTR buf_url;
486 LPHTTPHEADERW Host;
488 Host = HTTP_GetHeader(lpwhr, hostW);
489 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
490 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
491 sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
492 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
494 HeapFree(GetProcessHeap(), 0, buf_url);
496 numCookies++;
500 static void strip_spaces(LPWSTR start)
502 LPWSTR str = start;
503 LPWSTR end;
505 while (*str == ' ' && *str != '\0')
506 str++;
508 if (str != start)
509 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
511 end = start + strlenW(start) - 1;
512 while (end >= start && *end == ' ')
514 *end = '\0';
515 end--;
519 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
521 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
522 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
523 BOOL is_basic;
524 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
525 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
526 if (is_basic && pszRealm)
528 LPCWSTR token;
529 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
530 LPCWSTR realm;
531 ptr++;
532 *pszRealm=NULL;
533 token = strchrW(ptr,'=');
534 if (!token)
535 return TRUE;
536 realm = ptr;
537 while (*realm == ' ' && *realm != '\0')
538 realm++;
539 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
540 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
542 token++;
543 while (*token == ' ' && *token != '\0')
544 token++;
545 if (*token == '\0')
546 return TRUE;
547 *pszRealm = heap_strdupW(token);
548 strip_spaces(*pszRealm);
552 return is_basic;
555 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
557 if (!authinfo) return;
559 if (SecIsValidHandle(&authinfo->ctx))
560 DeleteSecurityContext(&authinfo->ctx);
561 if (SecIsValidHandle(&authinfo->cred))
562 FreeCredentialsHandle(&authinfo->cred);
564 HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
565 HeapFree(GetProcessHeap(), 0, authinfo->scheme);
566 HeapFree(GetProcessHeap(), 0, authinfo);
569 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
571 authorizationData *ad;
572 UINT rc = 0;
574 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
576 EnterCriticalSection(&authcache_cs);
577 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, authorizationData, entry)
579 if (!strcmpiW(host,ad->lpszwHost) && !strcmpW(realm,ad->lpszwRealm))
581 TRACE("Authorization found in cache\n");
582 *auth_data = HeapAlloc(GetProcessHeap(),0,ad->AuthorizationLen);
583 memcpy(*auth_data,ad->lpszAuthorization,ad->AuthorizationLen);
584 rc = ad->AuthorizationLen;
585 break;
588 LeaveCriticalSection(&authcache_cs);
589 return rc;
592 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
594 struct list *cursor;
595 authorizationData* ad = NULL;
597 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
599 EnterCriticalSection(&authcache_cs);
600 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
602 authorizationData *check = LIST_ENTRY(cursor,authorizationData,entry);
603 if (!strcmpiW(host,check->lpszwHost) && !strcmpW(realm,check->lpszwRealm))
605 ad = check;
606 break;
610 if (ad)
612 TRACE("Found match in cache, replacing\n");
613 HeapFree(GetProcessHeap(),0,ad->lpszAuthorization);
614 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
615 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
616 ad->AuthorizationLen = auth_data_len;
618 else
620 ad = HeapAlloc(GetProcessHeap(),0,sizeof(authorizationData));
621 ad->lpszwHost = heap_strdupW(host);
622 ad->lpszwRealm = heap_strdupW(realm);
623 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
624 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
625 ad->AuthorizationLen = auth_data_len;
626 list_add_head(&basicAuthorizationCache,&ad->entry);
627 TRACE("authorization cached\n");
629 LeaveCriticalSection(&authcache_cs);
632 static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
633 struct HttpAuthInfo **ppAuthInfo,
634 LPWSTR domain_and_username, LPWSTR password,
635 LPWSTR host )
637 SECURITY_STATUS sec_status;
638 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
639 BOOL first = FALSE;
640 LPWSTR szRealm = NULL;
642 TRACE("%s\n", debugstr_w(pszAuthValue));
644 if (!pAuthInfo)
646 TimeStamp exp;
648 first = TRUE;
649 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
650 if (!pAuthInfo)
651 return FALSE;
653 SecInvalidateHandle(&pAuthInfo->cred);
654 SecInvalidateHandle(&pAuthInfo->ctx);
655 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
656 pAuthInfo->attr = 0;
657 pAuthInfo->auth_data = NULL;
658 pAuthInfo->auth_data_len = 0;
659 pAuthInfo->finished = FALSE;
661 if (is_basic_auth_value(pszAuthValue,NULL))
663 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
664 pAuthInfo->scheme = heap_strdupW(szBasic);
665 if (!pAuthInfo->scheme)
667 HeapFree(GetProcessHeap(), 0, pAuthInfo);
668 return FALSE;
671 else
673 PVOID pAuthData;
674 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
676 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
677 if (!pAuthInfo->scheme)
679 HeapFree(GetProcessHeap(), 0, pAuthInfo);
680 return FALSE;
683 if (domain_and_username)
685 WCHAR *user = strchrW(domain_and_username, '\\');
686 WCHAR *domain = domain_and_username;
688 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
690 pAuthData = &nt_auth_identity;
692 if (user) user++;
693 else
695 user = domain_and_username;
696 domain = NULL;
699 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
700 nt_auth_identity.User = user;
701 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
702 nt_auth_identity.Domain = domain;
703 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
704 nt_auth_identity.Password = password;
705 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
707 else
708 /* use default credentials */
709 pAuthData = NULL;
711 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
712 SECPKG_CRED_OUTBOUND, NULL,
713 pAuthData, NULL,
714 NULL, &pAuthInfo->cred,
715 &exp);
716 if (sec_status == SEC_E_OK)
718 PSecPkgInfoW sec_pkg_info;
719 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
720 if (sec_status == SEC_E_OK)
722 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
723 FreeContextBuffer(sec_pkg_info);
726 if (sec_status != SEC_E_OK)
728 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
729 debugstr_w(pAuthInfo->scheme), sec_status);
730 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
731 HeapFree(GetProcessHeap(), 0, pAuthInfo);
732 return FALSE;
735 *ppAuthInfo = pAuthInfo;
737 else if (pAuthInfo->finished)
738 return FALSE;
740 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
741 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
743 ERR("authentication scheme changed from %s to %s\n",
744 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
745 return FALSE;
748 if (is_basic_auth_value(pszAuthValue,&szRealm))
750 int userlen;
751 int passlen;
752 char *auth_data = NULL;
753 UINT auth_data_len = 0;
755 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
757 if (!domain_and_username)
759 if (host && szRealm)
760 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
761 if (auth_data_len == 0)
763 HeapFree(GetProcessHeap(),0,szRealm);
764 return FALSE;
767 else
769 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
770 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
772 /* length includes a nul terminator, which will be re-used for the ':' */
773 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
774 if (!auth_data)
776 HeapFree(GetProcessHeap(),0,szRealm);
777 return FALSE;
780 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
781 auth_data[userlen] = ':';
782 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
783 auth_data_len = userlen + 1 + passlen;
784 if (host && szRealm)
785 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
788 pAuthInfo->auth_data = auth_data;
789 pAuthInfo->auth_data_len = auth_data_len;
790 pAuthInfo->finished = TRUE;
791 HeapFree(GetProcessHeap(),0,szRealm);
793 return TRUE;
795 else
797 LPCWSTR pszAuthData;
798 SecBufferDesc out_desc, in_desc;
799 SecBuffer out, in;
800 unsigned char *buffer;
801 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
802 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
804 in.BufferType = SECBUFFER_TOKEN;
805 in.cbBuffer = 0;
806 in.pvBuffer = NULL;
808 in_desc.ulVersion = 0;
809 in_desc.cBuffers = 1;
810 in_desc.pBuffers = &in;
812 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
813 if (*pszAuthData == ' ')
815 pszAuthData++;
816 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
817 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
818 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
821 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
823 out.BufferType = SECBUFFER_TOKEN;
824 out.cbBuffer = pAuthInfo->max_token;
825 out.pvBuffer = buffer;
827 out_desc.ulVersion = 0;
828 out_desc.cBuffers = 1;
829 out_desc.pBuffers = &out;
831 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
832 first ? NULL : &pAuthInfo->ctx,
833 first ? lpwhr->lpHttpSession->lpszServerName : NULL,
834 context_req, 0, SECURITY_NETWORK_DREP,
835 in.pvBuffer ? &in_desc : NULL,
836 0, &pAuthInfo->ctx, &out_desc,
837 &pAuthInfo->attr, &pAuthInfo->exp);
838 if (sec_status == SEC_E_OK)
840 pAuthInfo->finished = TRUE;
841 pAuthInfo->auth_data = out.pvBuffer;
842 pAuthInfo->auth_data_len = out.cbBuffer;
843 TRACE("sending last auth packet\n");
845 else if (sec_status == SEC_I_CONTINUE_NEEDED)
847 pAuthInfo->auth_data = out.pvBuffer;
848 pAuthInfo->auth_data_len = out.cbBuffer;
849 TRACE("sending next auth packet\n");
851 else
853 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
854 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
855 destroy_authinfo(pAuthInfo);
856 *ppAuthInfo = NULL;
857 return FALSE;
861 return TRUE;
864 /***********************************************************************
865 * HTTP_HttpAddRequestHeadersW (internal)
867 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
868 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
870 LPWSTR lpszStart;
871 LPWSTR lpszEnd;
872 LPWSTR buffer;
873 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
875 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
877 if( dwHeaderLength == ~0U )
878 len = strlenW(lpszHeader);
879 else
880 len = dwHeaderLength;
881 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
882 lstrcpynW( buffer, lpszHeader, len + 1);
884 lpszStart = buffer;
888 LPWSTR * pFieldAndValue;
890 lpszEnd = lpszStart;
892 while (*lpszEnd != '\0')
894 if (*lpszEnd == '\r' || *lpszEnd == '\n')
895 break;
896 lpszEnd++;
899 if (*lpszStart == '\0')
900 break;
902 if (*lpszEnd == '\r' || *lpszEnd == '\n')
904 *lpszEnd = '\0';
905 lpszEnd++; /* Jump over newline */
907 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
908 if (*lpszStart == '\0')
910 /* Skip 0-length headers */
911 lpszStart = lpszEnd;
912 res = ERROR_SUCCESS;
913 continue;
915 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
916 if (pFieldAndValue)
918 res = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
919 if (res == ERROR_SUCCESS)
920 res = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
921 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
922 HTTP_FreeTokens(pFieldAndValue);
925 lpszStart = lpszEnd;
926 } while (res == ERROR_SUCCESS);
928 HeapFree(GetProcessHeap(), 0, buffer);
930 return res;
933 /***********************************************************************
934 * HttpAddRequestHeadersW (WININET.@)
936 * Adds one or more HTTP header to the request handler
938 * NOTE
939 * On Windows if dwHeaderLength includes the trailing '\0', then
940 * HttpAddRequestHeadersW() adds it too. However this results in an
941 * invalid Http header which is rejected by some servers so we probably
942 * don't need to match Windows on that point.
944 * RETURNS
945 * TRUE on success
946 * FALSE on failure
949 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
950 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
952 http_request_t *lpwhr;
953 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
955 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
957 if (!lpszHeader)
958 return TRUE;
960 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
961 if (lpwhr && lpwhr->hdr.htype == WH_HHTTPREQ)
962 res = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
963 if( lpwhr )
964 WININET_Release( &lpwhr->hdr );
966 if(res != ERROR_SUCCESS)
967 SetLastError(res);
968 return res == ERROR_SUCCESS;
971 /***********************************************************************
972 * HttpAddRequestHeadersA (WININET.@)
974 * Adds one or more HTTP header to the request handler
976 * RETURNS
977 * TRUE on success
978 * FALSE on failure
981 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
982 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
984 DWORD len;
985 LPWSTR hdr;
986 BOOL r;
988 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
990 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
991 hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
992 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
993 if( dwHeaderLength != ~0U )
994 dwHeaderLength = len;
996 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
998 HeapFree( GetProcessHeap(), 0, hdr );
1000 return r;
1003 /***********************************************************************
1004 * HttpOpenRequestA (WININET.@)
1006 * Open a HTTP request handle
1008 * RETURNS
1009 * HINTERNET a HTTP request handle on success
1010 * NULL on failure
1013 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1014 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1015 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1016 DWORD dwFlags, DWORD_PTR dwContext)
1018 LPWSTR szVerb = NULL, szObjectName = NULL;
1019 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1020 INT acceptTypesCount;
1021 HINTERNET rc = FALSE;
1022 LPCSTR *types;
1024 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1025 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1026 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1027 dwFlags, dwContext);
1029 if (lpszVerb)
1031 szVerb = heap_strdupAtoW(lpszVerb);
1032 if ( !szVerb )
1033 goto end;
1036 if (lpszObjectName)
1038 szObjectName = heap_strdupAtoW(lpszObjectName);
1039 if ( !szObjectName )
1040 goto end;
1043 if (lpszVersion)
1045 szVersion = heap_strdupAtoW(lpszVersion);
1046 if ( !szVersion )
1047 goto end;
1050 if (lpszReferrer)
1052 szReferrer = heap_strdupAtoW(lpszReferrer);
1053 if ( !szReferrer )
1054 goto end;
1057 if (lpszAcceptTypes)
1059 acceptTypesCount = 0;
1060 types = lpszAcceptTypes;
1061 while (*types)
1063 __TRY
1065 /* find out how many there are */
1066 if (*types && **types)
1068 TRACE("accept type: %s\n", debugstr_a(*types));
1069 acceptTypesCount++;
1072 __EXCEPT_PAGE_FAULT
1074 WARN("invalid accept type pointer\n");
1076 __ENDTRY;
1077 types++;
1079 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1080 if (!szAcceptTypes) goto end;
1082 acceptTypesCount = 0;
1083 types = lpszAcceptTypes;
1084 while (*types)
1086 __TRY
1088 if (*types && **types)
1089 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1091 __EXCEPT_PAGE_FAULT
1093 /* ignore invalid pointer */
1095 __ENDTRY;
1096 types++;
1098 szAcceptTypes[acceptTypesCount] = NULL;
1101 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1102 szVersion, szReferrer,
1103 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1105 end:
1106 if (szAcceptTypes)
1108 acceptTypesCount = 0;
1109 while (szAcceptTypes[acceptTypesCount])
1111 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1112 acceptTypesCount++;
1114 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1116 HeapFree(GetProcessHeap(), 0, szReferrer);
1117 HeapFree(GetProcessHeap(), 0, szVersion);
1118 HeapFree(GetProcessHeap(), 0, szObjectName);
1119 HeapFree(GetProcessHeap(), 0, szVerb);
1121 return rc;
1124 /***********************************************************************
1125 * HTTP_EncodeBase64
1127 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1129 UINT n = 0, x;
1130 static const CHAR HTTP_Base64Enc[] =
1131 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1133 while( len > 0 )
1135 /* first 6 bits, all from bin[0] */
1136 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1137 x = (bin[0] & 3) << 4;
1139 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1140 if( len == 1 )
1142 base64[n++] = HTTP_Base64Enc[x];
1143 base64[n++] = '=';
1144 base64[n++] = '=';
1145 break;
1147 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1148 x = ( bin[1] & 0x0f ) << 2;
1150 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1151 if( len == 2 )
1153 base64[n++] = HTTP_Base64Enc[x];
1154 base64[n++] = '=';
1155 break;
1157 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1159 /* last 6 bits, all from bin [2] */
1160 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1161 bin += 3;
1162 len -= 3;
1164 base64[n] = 0;
1165 return n;
1168 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1169 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1170 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1171 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1172 static const signed char HTTP_Base64Dec[256] =
1174 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1175 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1176 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1177 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1178 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1179 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1180 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1181 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1182 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1183 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1184 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1185 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1186 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1187 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1188 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1189 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1190 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1191 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1192 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1193 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1194 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1195 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1196 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1197 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1198 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1199 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1201 #undef CH
1203 /***********************************************************************
1204 * HTTP_DecodeBase64
1206 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1208 unsigned int n = 0;
1210 while(*base64)
1212 signed char in[4];
1214 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1215 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1216 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1217 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1219 WARN("invalid base64: %s\n", debugstr_w(base64));
1220 return 0;
1222 if (bin)
1223 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1224 n++;
1226 if ((base64[2] == '=') && (base64[3] == '='))
1227 break;
1228 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1229 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1231 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1232 return 0;
1234 if (bin)
1235 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1236 n++;
1238 if (base64[3] == '=')
1239 break;
1240 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1241 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1243 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1244 return 0;
1246 if (bin)
1247 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1248 n++;
1250 base64 += 4;
1253 return n;
1256 /***********************************************************************
1257 * HTTP_InsertAuthorization
1259 * Insert or delete the authorization field in the request header.
1261 static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1263 if (pAuthInfo)
1265 static const WCHAR wszSpace[] = {' ',0};
1266 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1267 unsigned int len;
1268 WCHAR *authorization = NULL;
1270 if (pAuthInfo->auth_data_len)
1272 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1273 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1274 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1275 if (!authorization)
1276 return FALSE;
1278 strcpyW(authorization, pAuthInfo->scheme);
1279 strcatW(authorization, wszSpace);
1280 HTTP_EncodeBase64(pAuthInfo->auth_data,
1281 pAuthInfo->auth_data_len,
1282 authorization+strlenW(authorization));
1284 /* clear the data as it isn't valid now that it has been sent to the
1285 * server, unless it's Basic authentication which doesn't do
1286 * connection tracking */
1287 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1289 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1290 pAuthInfo->auth_data = NULL;
1291 pAuthInfo->auth_data_len = 0;
1295 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1297 HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1299 HeapFree(GetProcessHeap(), 0, authorization);
1301 return TRUE;
1304 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1306 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1307 DWORD size;
1309 size = sizeof(new_location);
1310 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL))
1312 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1313 strcpyW( url, new_location );
1315 else
1317 static const WCHAR slash[] = { '/',0 };
1318 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1319 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1320 http_session_t *session = req->lpHttpSession;
1322 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1323 size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
1325 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1327 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1328 sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
1329 else
1330 sprintfW( url, format, session->lpszHostName, session->nHostPort );
1331 if (req->lpszPath[0] != '/') strcatW( url, slash );
1332 strcatW( url, req->lpszPath );
1334 TRACE("url=%s\n", debugstr_w(url));
1335 return url;
1338 /***********************************************************************
1339 * HTTP_DealWithProxy
1341 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
1343 WCHAR buf[MAXHOSTNAME];
1344 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1345 static WCHAR szNul[] = { 0 };
1346 URL_COMPONENTSW UrlComponents;
1347 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1348 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1350 memset( &UrlComponents, 0, sizeof UrlComponents );
1351 UrlComponents.dwStructSize = sizeof UrlComponents;
1352 UrlComponents.lpszHostName = buf;
1353 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1355 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1356 hIC->lpszProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1357 sprintfW(proxy, szFormat, hIC->lpszProxy);
1358 else
1359 strcpyW(proxy, hIC->lpszProxy);
1360 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1361 return FALSE;
1362 if( UrlComponents.dwHostNameLength == 0 )
1363 return FALSE;
1365 if( !lpwhr->lpszPath )
1366 lpwhr->lpszPath = szNul;
1368 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1369 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1371 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1372 lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1373 lpwhs->nServerPort = UrlComponents.nPort;
1375 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1376 return TRUE;
1379 #ifndef INET6_ADDRSTRLEN
1380 #define INET6_ADDRSTRLEN 46
1381 #endif
1383 static DWORD HTTP_ResolveName(http_request_t *lpwhr)
1385 char szaddr[INET6_ADDRSTRLEN];
1386 http_session_t *lpwhs = lpwhr->lpHttpSession;
1387 const void *addr;
1389 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1390 INTERNET_STATUS_RESOLVING_NAME,
1391 lpwhs->lpszServerName,
1392 (strlenW(lpwhs->lpszServerName)+1) * sizeof(WCHAR));
1394 lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1395 if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1396 (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1397 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1399 switch (lpwhs->socketAddress.ss_family)
1401 case AF_INET:
1402 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1403 break;
1404 case AF_INET6:
1405 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1406 break;
1407 default:
1408 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1409 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1411 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1412 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1413 INTERNET_STATUS_NAME_RESOLVED,
1414 szaddr, strlen(szaddr)+1);
1416 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1417 return ERROR_SUCCESS;
1421 /***********************************************************************
1422 * HTTPREQ_Destroy (internal)
1424 * Deallocate request handle
1427 static void HTTPREQ_Destroy(object_header_t *hdr)
1429 http_request_t *lpwhr = (http_request_t*) hdr;
1430 DWORD i;
1432 TRACE("\n");
1434 if(lpwhr->hCacheFile)
1435 CloseHandle(lpwhr->hCacheFile);
1437 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1439 DeleteCriticalSection( &lpwhr->read_section );
1440 WININET_Release(&lpwhr->lpHttpSession->hdr);
1442 destroy_authinfo(lpwhr->pAuthInfo);
1443 destroy_authinfo(lpwhr->pProxyAuthInfo);
1445 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1446 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1447 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1448 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1449 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1451 for (i = 0; i < lpwhr->nCustHeaders; i++)
1453 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1454 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1457 #ifdef HAVE_ZLIB
1458 if(lpwhr->gzip_stream) {
1459 if(!lpwhr->gzip_stream->end_of_data)
1460 inflateEnd(&lpwhr->gzip_stream->zstream);
1461 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1463 #endif
1465 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1466 HeapFree(GetProcessHeap(), 0, lpwhr);
1469 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1471 http_request_t *lpwhr = (http_request_t*) hdr;
1473 TRACE("%p\n",lpwhr);
1475 if (!NETCON_connected(&lpwhr->netConnection))
1476 return;
1478 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1479 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1481 NETCON_close(&lpwhr->netConnection);
1483 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1484 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1487 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1489 LPHTTPHEADERW host_header;
1491 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1493 host_header = HTTP_GetHeader(req, hostW);
1494 if(!host_header)
1495 return FALSE;
1497 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1498 return TRUE;
1501 static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
1503 WCHAR szVersion[10];
1504 WCHAR szConnectionResponse[20];
1505 DWORD dwBufferSize = sizeof(szVersion);
1506 BOOL keepalive = FALSE;
1508 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1509 * the connection is keep-alive by default */
1510 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion,
1511 &dwBufferSize, NULL) &&
1512 !strcmpiW(szVersion, g_szHttp1_1))
1514 keepalive = TRUE;
1517 dwBufferSize = sizeof(szConnectionResponse);
1518 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) ||
1519 HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL))
1521 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1524 return keepalive;
1527 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1529 http_request_t *req = (http_request_t*)hdr;
1531 switch(option) {
1532 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1534 http_session_t *lpwhs = req->lpHttpSession;
1535 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1537 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1539 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1540 return ERROR_INSUFFICIENT_BUFFER;
1541 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1542 /* FIXME: can't get a SOCKET from our connection since we don't use
1543 * winsock
1545 info->Socket = 0;
1546 /* FIXME: get source port from req->netConnection */
1547 info->SourcePort = 0;
1548 info->DestPort = lpwhs->nHostPort;
1549 info->Flags = 0;
1550 if (HTTP_KeepAlive(req))
1551 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1552 if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
1553 info->Flags |= IDSI_FLAG_PROXY;
1554 if (req->netConnection.useSSL)
1555 info->Flags |= IDSI_FLAG_SECURE;
1557 return ERROR_SUCCESS;
1560 case INTERNET_OPTION_SECURITY_FLAGS:
1562 http_session_t *lpwhs;
1563 lpwhs = req->lpHttpSession;
1565 if (*size < sizeof(ULONG))
1566 return ERROR_INSUFFICIENT_BUFFER;
1568 *size = sizeof(DWORD);
1569 if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1570 *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1571 else
1572 *(DWORD*)buffer = 0;
1573 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1574 return ERROR_SUCCESS;
1577 case INTERNET_OPTION_HANDLE_TYPE:
1578 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1580 if (*size < sizeof(ULONG))
1581 return ERROR_INSUFFICIENT_BUFFER;
1583 *size = sizeof(DWORD);
1584 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1585 return ERROR_SUCCESS;
1587 case INTERNET_OPTION_URL: {
1588 WCHAR url[INTERNET_MAX_URL_LENGTH];
1589 HTTPHEADERW *host;
1590 DWORD len;
1591 WCHAR *pch;
1593 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1595 TRACE("INTERNET_OPTION_URL\n");
1597 host = HTTP_GetHeader(req, hostW);
1598 strcpyW(url, httpW);
1599 strcatW(url, host->lpszValue);
1600 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1601 *pch = 0;
1602 strcatW(url, req->lpszPath);
1604 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1606 if(unicode) {
1607 len = (strlenW(url)+1) * sizeof(WCHAR);
1608 if(*size < len)
1609 return ERROR_INSUFFICIENT_BUFFER;
1611 *size = len;
1612 strcpyW(buffer, url);
1613 return ERROR_SUCCESS;
1614 }else {
1615 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1616 if(len > *size)
1617 return ERROR_INSUFFICIENT_BUFFER;
1619 *size = len;
1620 return ERROR_SUCCESS;
1624 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1625 INTERNET_CACHE_ENTRY_INFOW *info;
1626 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1627 WCHAR url[INTERNET_MAX_URL_LENGTH];
1628 DWORD nbytes, error;
1629 BOOL ret;
1631 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1633 if (*size < sizeof(*ts))
1635 *size = sizeof(*ts);
1636 return ERROR_INSUFFICIENT_BUFFER;
1638 nbytes = 0;
1639 HTTP_GetRequestURL(req, url);
1640 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1641 error = GetLastError();
1642 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1644 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1645 return ERROR_OUTOFMEMORY;
1647 GetUrlCacheEntryInfoW(url, info, &nbytes);
1649 ts->ftExpires = info->ExpireTime;
1650 ts->ftLastModified = info->LastModifiedTime;
1652 HeapFree(GetProcessHeap(), 0, info);
1653 *size = sizeof(*ts);
1654 return ERROR_SUCCESS;
1656 return error;
1659 case INTERNET_OPTION_DATAFILE_NAME: {
1660 DWORD req_size;
1662 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1664 if(!req->lpszCacheFile) {
1665 *size = 0;
1666 return ERROR_INTERNET_ITEM_NOT_FOUND;
1669 if(unicode) {
1670 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1671 if(*size < req_size)
1672 return ERROR_INSUFFICIENT_BUFFER;
1674 *size = req_size;
1675 memcpy(buffer, req->lpszCacheFile, *size);
1676 return ERROR_SUCCESS;
1677 }else {
1678 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1679 if (req_size > *size)
1680 return ERROR_INSUFFICIENT_BUFFER;
1682 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1683 -1, buffer, *size, NULL, NULL);
1684 return ERROR_SUCCESS;
1688 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1689 PCCERT_CONTEXT context;
1691 if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1692 *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1693 return ERROR_INSUFFICIENT_BUFFER;
1696 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1697 if(context) {
1698 INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1699 DWORD len;
1701 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1702 info->ftExpiry = context->pCertInfo->NotAfter;
1703 info->ftStart = context->pCertInfo->NotBefore;
1704 if(unicode) {
1705 len = CertNameToStrW(context->dwCertEncodingType,
1706 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1707 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1708 if(info->lpszSubjectInfo)
1709 CertNameToStrW(context->dwCertEncodingType,
1710 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1711 info->lpszSubjectInfo, len);
1712 len = CertNameToStrW(context->dwCertEncodingType,
1713 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1714 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1715 if (info->lpszIssuerInfo)
1716 CertNameToStrW(context->dwCertEncodingType,
1717 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1718 info->lpszIssuerInfo, len);
1719 }else {
1720 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1722 len = CertNameToStrA(context->dwCertEncodingType,
1723 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1724 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1725 if(infoA->lpszSubjectInfo)
1726 CertNameToStrA(context->dwCertEncodingType,
1727 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1728 infoA->lpszSubjectInfo, len);
1729 len = CertNameToStrA(context->dwCertEncodingType,
1730 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1731 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1732 if(infoA->lpszIssuerInfo)
1733 CertNameToStrA(context->dwCertEncodingType,
1734 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1735 infoA->lpszIssuerInfo, len);
1739 * Contrary to MSDN, these do not appear to be set.
1740 * lpszProtocolName
1741 * lpszSignatureAlgName
1742 * lpszEncryptionAlgName
1743 * dwKeySize
1745 CertFreeCertificateContext(context);
1746 return ERROR_SUCCESS;
1751 return INET_QueryOption(option, buffer, size, unicode);
1754 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1756 http_request_t *req = (http_request_t*)hdr;
1758 switch(option) {
1759 case INTERNET_OPTION_SEND_TIMEOUT:
1760 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1761 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1763 if (size != sizeof(DWORD))
1764 return ERROR_INVALID_PARAMETER;
1766 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1767 *(DWORD*)buffer);
1769 case INTERNET_OPTION_USERNAME:
1770 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1771 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1772 return ERROR_SUCCESS;
1774 case INTERNET_OPTION_PASSWORD:
1775 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1776 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1777 return ERROR_SUCCESS;
1778 case INTERNET_OPTION_HTTP_DECODING:
1779 if(size != sizeof(BOOL))
1780 return ERROR_INVALID_PARAMETER;
1781 req->decoding = *(BOOL*)buffer;
1782 return ERROR_SUCCESS;
1785 return ERROR_INTERNET_INVALID_OPTION;
1788 /* read some more data into the read buffer (the read section must be held) */
1789 static DWORD read_more_data( http_request_t *req, int maxlen )
1791 DWORD res;
1792 int len;
1794 if (req->read_pos)
1796 /* move existing data to the start of the buffer */
1797 if(req->read_size)
1798 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1799 req->read_pos = 0;
1802 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1804 res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1805 maxlen - req->read_size, 0, &len );
1806 if(res == ERROR_SUCCESS)
1807 req->read_size += len;
1809 return res;
1812 /* remove some amount of data from the read buffer (the read section must be held) */
1813 static void remove_data( http_request_t *req, int count )
1815 if (!(req->read_size -= count)) req->read_pos = 0;
1816 else req->read_pos += count;
1819 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1821 int count, bytes_read, pos = 0;
1822 DWORD res;
1824 EnterCriticalSection( &req->read_section );
1825 for (;;)
1827 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1829 if (eol)
1831 count = eol - (req->read_buf + req->read_pos);
1832 bytes_read = count + 1;
1834 else count = bytes_read = req->read_size;
1836 count = min( count, *len - pos );
1837 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1838 pos += count;
1839 remove_data( req, bytes_read );
1840 if (eol) break;
1842 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
1844 *len = 0;
1845 TRACE( "returning empty string\n" );
1846 LeaveCriticalSection( &req->read_section );
1847 INTERNET_SetLastError(res);
1848 return FALSE;
1851 LeaveCriticalSection( &req->read_section );
1853 if (pos < *len)
1855 if (pos && buffer[pos - 1] == '\r') pos--;
1856 *len = pos + 1;
1858 buffer[*len - 1] = 0;
1859 TRACE( "returning %s\n", debugstr_a(buffer));
1860 return TRUE;
1863 /* discard data contents until we reach end of line (the read section must be held) */
1864 static DWORD discard_eol( http_request_t *req )
1866 DWORD res;
1870 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1871 if (eol)
1873 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
1874 break;
1876 req->read_pos = req->read_size = 0; /* discard everything */
1877 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1878 } while (req->read_size);
1879 return ERROR_SUCCESS;
1882 /* read the size of the next chunk (the read section must be held) */
1883 static DWORD start_next_chunk( http_request_t *req )
1885 DWORD chunk_size = 0, res;
1887 if (!req->dwContentLength) return ERROR_SUCCESS;
1888 if (req->dwContentLength == req->dwContentRead)
1890 /* read terminator for the previous chunk */
1891 if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
1892 req->dwContentLength = ~0u;
1893 req->dwContentRead = 0;
1895 for (;;)
1897 while (req->read_size)
1899 char ch = req->read_buf[req->read_pos];
1900 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
1901 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
1902 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
1903 else if (ch == ';' || ch == '\r' || ch == '\n')
1905 TRACE( "reading %u byte chunk\n", chunk_size );
1906 req->dwContentLength = chunk_size;
1907 req->dwContentRead = 0;
1908 return discard_eol( req );
1910 remove_data( req, 1 );
1912 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1913 if (!req->read_size)
1915 req->dwContentLength = req->dwContentRead = 0;
1916 return ERROR_SUCCESS;
1921 /* check if we have reached the end of the data to read (the read section must be held) */
1922 static BOOL end_of_read_data( http_request_t *req )
1924 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
1925 if (req->read_chunked) return (req->dwContentLength == 0);
1926 if (req->dwContentLength == ~0u) return FALSE;
1927 return (req->dwContentLength == req->dwContentRead);
1930 /* fetch some more data into the read buffer (the read section must be held) */
1931 static DWORD refill_buffer( http_request_t *req )
1933 int len = sizeof(req->read_buf);
1934 DWORD res;
1936 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
1938 if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
1941 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
1942 if (len <= req->read_size) return ERROR_SUCCESS;
1944 if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
1945 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
1946 return ERROR_SUCCESS;
1949 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
1951 DWORD ret = ERROR_SUCCESS;
1952 int read = 0;
1954 #ifdef HAVE_ZLIB
1955 z_stream *zstream = &req->gzip_stream->zstream;
1956 int zres;
1958 while(read < size && !req->gzip_stream->end_of_data) {
1959 if(!req->read_size) {
1960 if(!sync || refill_buffer(req) != ERROR_SUCCESS)
1961 break;
1964 zstream->next_in = req->read_buf+req->read_pos;
1965 zstream->avail_in = req->read_size;
1966 zstream->next_out = buf+read;
1967 zstream->avail_out = size-read;
1968 zres = inflate(zstream, Z_FULL_FLUSH);
1969 read = size - zstream->avail_out;
1970 remove_data(req, req->read_size-zstream->avail_in);
1971 if(zres == Z_STREAM_END) {
1972 TRACE("end of data\n");
1973 req->gzip_stream->end_of_data = TRUE;
1974 inflateEnd(&req->gzip_stream->zstream);
1975 }else if(zres != Z_OK) {
1976 WARN("inflate failed %d\n", zres);
1977 if(!read)
1978 ret = ERROR_INTERNET_DECODING_FAILED;
1979 break;
1982 #endif
1984 *read_ret = read;
1985 return ret;
1988 static void refill_gzip_buffer(http_request_t *req)
1990 DWORD res;
1991 int len;
1993 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
1994 return;
1996 if(req->gzip_stream->buf_pos) {
1997 if(req->gzip_stream->buf_size)
1998 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
1999 req->gzip_stream->buf_pos = 0;
2002 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2003 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2004 if(res == ERROR_SUCCESS)
2005 req->gzip_stream->buf_size += len;
2008 /* return the size of data available to be read immediately (the read section must be held) */
2009 static DWORD get_avail_data( http_request_t *req )
2011 if (req->gzip_stream) {
2012 refill_gzip_buffer(req);
2013 return req->gzip_stream->buf_size;
2015 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2016 return 0;
2017 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2020 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2022 INTERNET_ASYNC_RESULT iar;
2023 DWORD res;
2025 TRACE("%p\n", req);
2027 EnterCriticalSection( &req->read_section );
2028 if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
2029 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2030 iar.dwError = first_notif ? 0 : get_avail_data(req);
2031 }else {
2032 iar.dwResult = 0;
2033 iar.dwError = res;
2035 LeaveCriticalSection( &req->read_section );
2037 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2038 sizeof(INTERNET_ASYNC_RESULT));
2041 /* read data from the http connection (the read section must be held) */
2042 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2044 BOOL finished_reading = FALSE;
2045 int len, bytes_read = 0;
2046 DWORD ret = ERROR_SUCCESS;
2048 EnterCriticalSection( &req->read_section );
2050 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2052 if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
2055 if(req->gzip_stream) {
2056 if(req->gzip_stream->buf_size) {
2057 bytes_read = min(req->gzip_stream->buf_size, size);
2058 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2059 req->gzip_stream->buf_pos += bytes_read;
2060 req->gzip_stream->buf_size -= bytes_read;
2061 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2062 refill_buffer(req);
2065 if(size > bytes_read) {
2066 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2067 if(ret == ERROR_SUCCESS)
2068 bytes_read += len;
2071 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2072 }else {
2073 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2075 if (req->read_size) {
2076 bytes_read = min( req->read_size, size );
2077 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2078 remove_data( req, bytes_read );
2081 if (size > bytes_read && (!bytes_read || sync)) {
2082 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2083 sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2084 bytes_read += len;
2085 /* always return success, even if the network layer returns an error */
2088 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2090 done:
2091 req->dwContentRead += bytes_read;
2092 *read = bytes_read;
2094 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2095 LeaveCriticalSection( &req->read_section );
2097 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2098 BOOL res;
2099 DWORD dwBytesWritten;
2101 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2102 if(!res)
2103 WARN("WriteFile failed: %u\n", GetLastError());
2106 if(finished_reading)
2107 HTTP_FinishedReading(req);
2109 return ret;
2113 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2115 http_request_t *req = (http_request_t*)hdr;
2116 return HTTPREQ_Read(req, buffer, size, read, TRUE);
2119 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2121 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2122 http_request_t *req = (http_request_t*)workRequest->hdr;
2123 INTERNET_ASYNC_RESULT iar;
2124 DWORD res;
2126 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2128 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2129 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2131 iar.dwResult = res == ERROR_SUCCESS;
2132 iar.dwError = res;
2134 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2135 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2136 sizeof(INTERNET_ASYNC_RESULT));
2139 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2140 DWORD flags, DWORD_PTR context)
2142 http_request_t *req = (http_request_t*)hdr;
2143 DWORD res;
2145 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2146 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2148 if (buffers->dwStructSize != sizeof(*buffers))
2149 return ERROR_INVALID_PARAMETER;
2151 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2153 if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
2155 WORKREQUEST workRequest;
2157 if (TryEnterCriticalSection( &req->read_section ))
2159 if (get_avail_data(req))
2161 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2162 &buffers->dwBufferLength, FALSE);
2163 LeaveCriticalSection( &req->read_section );
2164 goto done;
2166 LeaveCriticalSection( &req->read_section );
2169 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2170 workRequest.hdr = WININET_AddRef(&req->hdr);
2171 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2173 INTERNET_AsyncCall(&workRequest);
2175 return ERROR_IO_PENDING;
2178 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2179 !(flags & IRF_NO_WAIT));
2181 done:
2182 if (res == ERROR_SUCCESS) {
2183 DWORD size = buffers->dwBufferLength;
2184 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2185 &size, sizeof(size));
2188 return res;
2191 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2193 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2194 http_request_t *req = (http_request_t*)workRequest->hdr;
2195 INTERNET_ASYNC_RESULT iar;
2196 DWORD res;
2198 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2200 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2201 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2203 iar.dwResult = res == ERROR_SUCCESS;
2204 iar.dwError = res;
2206 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2207 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2208 sizeof(INTERNET_ASYNC_RESULT));
2211 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2212 DWORD flags, DWORD_PTR context)
2215 http_request_t *req = (http_request_t*)hdr;
2216 DWORD res;
2218 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2219 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2221 if (buffers->dwStructSize != sizeof(*buffers))
2222 return ERROR_INVALID_PARAMETER;
2224 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2226 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2228 WORKREQUEST workRequest;
2230 if (TryEnterCriticalSection( &req->read_section ))
2232 if (get_avail_data(req))
2234 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2235 &buffers->dwBufferLength, FALSE);
2236 LeaveCriticalSection( &req->read_section );
2237 goto done;
2239 LeaveCriticalSection( &req->read_section );
2242 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2243 workRequest.hdr = WININET_AddRef(&req->hdr);
2244 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2246 INTERNET_AsyncCall(&workRequest);
2248 return ERROR_IO_PENDING;
2251 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2252 !(flags & IRF_NO_WAIT));
2254 done:
2255 if (res == ERROR_SUCCESS) {
2256 DWORD size = buffers->dwBufferLength;
2257 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2258 &size, sizeof(size));
2261 return res;
2264 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2266 DWORD res;
2267 http_request_t *lpwhr = (http_request_t*)hdr;
2269 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2271 *written = 0;
2272 res = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written);
2273 if (res == ERROR_SUCCESS)
2274 lpwhr->dwBytesWritten += *written;
2276 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2277 return res;
2280 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2282 http_request_t *req = (http_request_t*)workRequest->hdr;
2284 HTTP_ReceiveRequestData(req, FALSE);
2287 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2289 http_request_t *req = (http_request_t*)hdr;
2291 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2293 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2295 WORKREQUEST workRequest;
2297 /* never wait, if we can't enter the section we queue an async request right away */
2298 if (TryEnterCriticalSection( &req->read_section ))
2300 if ((*available = get_avail_data( req ))) goto done;
2301 if (end_of_read_data( req )) goto done;
2302 LeaveCriticalSection( &req->read_section );
2305 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2306 workRequest.hdr = WININET_AddRef( &req->hdr );
2308 INTERNET_AsyncCall(&workRequest);
2310 return ERROR_IO_PENDING;
2313 EnterCriticalSection( &req->read_section );
2315 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2317 refill_buffer( req );
2318 *available = get_avail_data( req );
2321 done:
2322 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2324 DWORD extra;
2325 if (NETCON_query_data_available(&req->netConnection, &extra))
2326 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2328 LeaveCriticalSection( &req->read_section );
2330 TRACE( "returning %u\n", *available );
2331 return ERROR_SUCCESS;
2334 static const object_vtbl_t HTTPREQVtbl = {
2335 HTTPREQ_Destroy,
2336 HTTPREQ_CloseConnection,
2337 HTTPREQ_QueryOption,
2338 HTTPREQ_SetOption,
2339 HTTPREQ_ReadFile,
2340 HTTPREQ_ReadFileExA,
2341 HTTPREQ_ReadFileExW,
2342 HTTPREQ_WriteFile,
2343 HTTPREQ_QueryDataAvailable,
2344 NULL
2347 /***********************************************************************
2348 * HTTP_HttpOpenRequestW (internal)
2350 * Open a HTTP request handle
2352 * RETURNS
2353 * HINTERNET a HTTP request handle on success
2354 * NULL on failure
2357 static DWORD HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2358 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2359 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2360 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2362 appinfo_t *hIC = NULL;
2363 http_request_t *lpwhr;
2364 LPWSTR lpszHostName = NULL;
2365 HINTERNET handle = NULL;
2366 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2367 DWORD len, res;
2369 TRACE("-->\n");
2371 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2372 hIC = lpwhs->lpAppInfo;
2374 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2375 if (NULL == lpwhr)
2377 res = ERROR_OUTOFMEMORY;
2378 goto lend;
2380 lpwhr->hdr.htype = WH_HHTTPREQ;
2381 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2382 lpwhr->hdr.dwFlags = dwFlags;
2383 lpwhr->hdr.dwContext = dwContext;
2384 lpwhr->hdr.refs = 1;
2385 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2386 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2387 lpwhr->dwContentLength = ~0u;
2388 InitializeCriticalSection( &lpwhr->read_section );
2390 WININET_AddRef( &lpwhs->hdr );
2391 lpwhr->lpHttpSession = lpwhs;
2392 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2394 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2395 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2396 if (NULL == lpszHostName)
2398 res = ERROR_OUTOFMEMORY;
2399 goto lend;
2402 handle = WININET_AllocHandle( &lpwhr->hdr );
2403 if (NULL == handle)
2405 res = ERROR_OUTOFMEMORY;
2406 goto lend;
2409 if ((res = NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2411 InternetCloseHandle( handle );
2412 handle = NULL;
2413 goto lend;
2416 if (lpszObjectName && *lpszObjectName) {
2417 HRESULT rc;
2419 len = 0;
2420 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2421 if (rc != E_POINTER)
2422 len = strlenW(lpszObjectName)+1;
2423 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2424 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2425 URL_ESCAPE_SPACES_ONLY);
2426 if (rc != S_OK)
2428 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2429 strcpyW(lpwhr->lpszPath,lpszObjectName);
2431 }else {
2432 static const WCHAR slashW[] = {'/',0};
2434 lpwhr->lpszPath = heap_strdupW(slashW);
2437 if (lpszReferrer && *lpszReferrer)
2438 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2440 if (lpszAcceptTypes)
2442 int i;
2443 for (i = 0; lpszAcceptTypes[i]; i++)
2445 if (!*lpszAcceptTypes[i]) continue;
2446 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2447 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2448 HTTP_ADDHDR_FLAG_REQ |
2449 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2453 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2454 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2456 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2457 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2458 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2460 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2461 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2462 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2464 else
2465 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2466 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2468 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2469 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2470 INTERNET_DEFAULT_HTTPS_PORT :
2471 INTERNET_DEFAULT_HTTP_PORT);
2473 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2474 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2475 INTERNET_DEFAULT_HTTPS_PORT :
2476 INTERNET_DEFAULT_HTTP_PORT);
2478 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2479 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2481 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2482 INTERNET_STATUS_HANDLE_CREATED, &handle,
2483 sizeof(handle));
2485 lend:
2486 HeapFree(GetProcessHeap(), 0, lpszHostName);
2487 if( lpwhr )
2488 WININET_Release( &lpwhr->hdr );
2490 TRACE("<-- %p (%p)\n", handle, lpwhr);
2491 *ret = handle;
2492 return res;
2495 /***********************************************************************
2496 * HttpOpenRequestW (WININET.@)
2498 * Open a HTTP request handle
2500 * RETURNS
2501 * HINTERNET a HTTP request handle on success
2502 * NULL on failure
2505 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2506 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2507 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2508 DWORD dwFlags, DWORD_PTR dwContext)
2510 http_session_t *lpwhs;
2511 HINTERNET handle = NULL;
2512 DWORD res;
2514 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2515 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2516 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2517 dwFlags, dwContext);
2518 if(lpszAcceptTypes!=NULL)
2520 int i;
2521 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2522 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2525 lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
2526 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
2528 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2529 goto lend;
2533 * My tests seem to show that the windows version does not
2534 * become asynchronous until after this point. And anyhow
2535 * if this call was asynchronous then how would you get the
2536 * necessary HINTERNET pointer returned by this function.
2539 res = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
2540 lpszVersion, lpszReferrer, lpszAcceptTypes,
2541 dwFlags, dwContext, &handle);
2542 lend:
2543 if( lpwhs )
2544 WININET_Release( &lpwhs->hdr );
2545 TRACE("returning %p\n", handle);
2546 if(res != ERROR_SUCCESS)
2547 SetLastError(res);
2548 return handle;
2551 /* read any content returned by the server so that the connection can be
2552 * reused */
2553 static void HTTP_DrainContent(http_request_t *req)
2555 DWORD bytes_read;
2557 if (!NETCON_connected(&req->netConnection)) return;
2559 if (req->dwContentLength == -1)
2561 NETCON_close(&req->netConnection);
2562 return;
2564 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2568 char buffer[2048];
2569 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2570 return;
2571 } while (bytes_read);
2574 static const LPCWSTR header_lookup[] = {
2575 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2576 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2577 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2578 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2579 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2580 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2581 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2582 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2583 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2584 szDate, /* HTTP_QUERY_DATE = 9 */
2585 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2586 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2587 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2588 szURI, /* HTTP_QUERY_URI = 13 */
2589 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2590 NULL, /* HTTP_QUERY_COST = 15 */
2591 NULL, /* HTTP_QUERY_LINK = 16 */
2592 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2593 NULL, /* HTTP_QUERY_VERSION = 18 */
2594 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2595 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2596 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2597 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2598 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2599 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2600 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2601 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2602 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2603 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2604 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2605 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2606 NULL, /* HTTP_QUERY_FROM = 31 */
2607 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2608 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2609 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2610 szReferer, /* HTTP_QUERY_REFERER = 35 */
2611 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2612 szServer, /* HTTP_QUERY_SERVER = 37 */
2613 NULL, /* HTTP_TITLE = 38 */
2614 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2615 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2616 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2617 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2618 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2619 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2620 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2621 NULL, /* HTTP_QUERY_REFRESH = 46 */
2622 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2623 szAge, /* HTTP_QUERY_AGE = 48 */
2624 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2625 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2626 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2627 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2628 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2629 szETag, /* HTTP_QUERY_ETAG = 54 */
2630 hostW, /* HTTP_QUERY_HOST = 55 */
2631 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2632 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2633 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2634 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2635 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2636 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2637 szRange, /* HTTP_QUERY_RANGE = 62 */
2638 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2639 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2640 szVary, /* HTTP_QUERY_VARY = 65 */
2641 szVia, /* HTTP_QUERY_VIA = 66 */
2642 szWarning, /* HTTP_QUERY_WARNING = 67 */
2643 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2644 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2645 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2648 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2650 /***********************************************************************
2651 * HTTP_HttpQueryInfoW (internal)
2653 static BOOL HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2654 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2656 LPHTTPHEADERW lphttpHdr = NULL;
2657 BOOL bSuccess = FALSE;
2658 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2659 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2660 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2661 INT index = -1;
2663 /* Find requested header structure */
2664 switch (level)
2666 case HTTP_QUERY_CUSTOM:
2667 if (!lpBuffer) return FALSE;
2668 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2669 break;
2670 case HTTP_QUERY_RAW_HEADERS_CRLF:
2672 LPWSTR headers;
2673 DWORD len = 0;
2674 BOOL ret = FALSE;
2676 if (request_only)
2677 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2678 else
2679 headers = lpwhr->lpszRawHeaders;
2681 if (headers)
2682 len = strlenW(headers) * sizeof(WCHAR);
2684 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2686 len += sizeof(WCHAR);
2687 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2688 ret = FALSE;
2690 else if (lpBuffer)
2692 if (headers)
2693 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2694 else
2696 len = strlenW(szCrLf) * sizeof(WCHAR);
2697 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2699 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2700 ret = TRUE;
2702 *lpdwBufferLength = len;
2704 if (request_only)
2705 HeapFree(GetProcessHeap(), 0, headers);
2706 return ret;
2708 case HTTP_QUERY_RAW_HEADERS:
2710 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2711 DWORD i, size = 0;
2712 LPWSTR pszString = lpBuffer;
2714 for (i = 0; ppszRawHeaderLines[i]; i++)
2715 size += strlenW(ppszRawHeaderLines[i]) + 1;
2717 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2719 HTTP_FreeTokens(ppszRawHeaderLines);
2720 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2721 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2722 return FALSE;
2724 if (pszString)
2726 for (i = 0; ppszRawHeaderLines[i]; i++)
2728 DWORD len = strlenW(ppszRawHeaderLines[i]);
2729 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2730 pszString += len+1;
2732 *pszString = '\0';
2733 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2735 *lpdwBufferLength = size * sizeof(WCHAR);
2736 HTTP_FreeTokens(ppszRawHeaderLines);
2738 return TRUE;
2740 case HTTP_QUERY_STATUS_TEXT:
2741 if (lpwhr->lpszStatusText)
2743 DWORD len = strlenW(lpwhr->lpszStatusText);
2744 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2746 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2747 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2748 return FALSE;
2750 if (lpBuffer)
2752 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2753 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2755 *lpdwBufferLength = len * sizeof(WCHAR);
2756 return TRUE;
2758 break;
2759 case HTTP_QUERY_VERSION:
2760 if (lpwhr->lpszVersion)
2762 DWORD len = strlenW(lpwhr->lpszVersion);
2763 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2765 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2766 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2767 return FALSE;
2769 if (lpBuffer)
2771 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2772 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2774 *lpdwBufferLength = len * sizeof(WCHAR);
2775 return TRUE;
2777 break;
2778 case HTTP_QUERY_CONTENT_ENCODING:
2779 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2780 requested_index,request_only);
2781 break;
2782 default:
2783 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2785 if (level < LAST_TABLE_HEADER && header_lookup[level])
2786 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2787 requested_index,request_only);
2790 if (index >= 0)
2791 lphttpHdr = &lpwhr->pCustHeaders[index];
2793 /* Ensure header satisfies requested attributes */
2794 if (!lphttpHdr ||
2795 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2796 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2798 INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
2799 return bSuccess;
2802 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2804 /* coalesce value to requested type */
2805 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2807 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2808 TRACE(" returning number: %d\n", *(int *)lpBuffer);
2809 bSuccess = TRUE;
2811 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2813 time_t tmpTime;
2814 struct tm tmpTM;
2815 SYSTEMTIME *STHook;
2817 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2819 tmpTM = *gmtime(&tmpTime);
2820 STHook = (SYSTEMTIME *)lpBuffer;
2821 STHook->wDay = tmpTM.tm_mday;
2822 STHook->wHour = tmpTM.tm_hour;
2823 STHook->wMilliseconds = 0;
2824 STHook->wMinute = tmpTM.tm_min;
2825 STHook->wDayOfWeek = tmpTM.tm_wday;
2826 STHook->wMonth = tmpTM.tm_mon + 1;
2827 STHook->wSecond = tmpTM.tm_sec;
2828 STHook->wYear = tmpTM.tm_year;
2829 bSuccess = TRUE;
2831 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
2832 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
2833 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
2835 else if (lphttpHdr->lpszValue)
2837 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
2839 if (len > *lpdwBufferLength)
2841 *lpdwBufferLength = len;
2842 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2843 return bSuccess;
2845 if (lpBuffer)
2847 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
2848 TRACE(" returning string: %s\n", debugstr_w(lpBuffer));
2850 *lpdwBufferLength = len - sizeof(WCHAR);
2851 bSuccess = TRUE;
2853 return bSuccess;
2856 /***********************************************************************
2857 * HttpQueryInfoW (WININET.@)
2859 * Queries for information about an HTTP request
2861 * RETURNS
2862 * TRUE on success
2863 * FALSE on failure
2866 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
2867 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2869 BOOL bSuccess = FALSE;
2870 http_request_t *lpwhr;
2872 if (TRACE_ON(wininet)) {
2873 #define FE(x) { x, #x }
2874 static const wininet_flag_info query_flags[] = {
2875 FE(HTTP_QUERY_MIME_VERSION),
2876 FE(HTTP_QUERY_CONTENT_TYPE),
2877 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
2878 FE(HTTP_QUERY_CONTENT_ID),
2879 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
2880 FE(HTTP_QUERY_CONTENT_LENGTH),
2881 FE(HTTP_QUERY_CONTENT_LANGUAGE),
2882 FE(HTTP_QUERY_ALLOW),
2883 FE(HTTP_QUERY_PUBLIC),
2884 FE(HTTP_QUERY_DATE),
2885 FE(HTTP_QUERY_EXPIRES),
2886 FE(HTTP_QUERY_LAST_MODIFIED),
2887 FE(HTTP_QUERY_MESSAGE_ID),
2888 FE(HTTP_QUERY_URI),
2889 FE(HTTP_QUERY_DERIVED_FROM),
2890 FE(HTTP_QUERY_COST),
2891 FE(HTTP_QUERY_LINK),
2892 FE(HTTP_QUERY_PRAGMA),
2893 FE(HTTP_QUERY_VERSION),
2894 FE(HTTP_QUERY_STATUS_CODE),
2895 FE(HTTP_QUERY_STATUS_TEXT),
2896 FE(HTTP_QUERY_RAW_HEADERS),
2897 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
2898 FE(HTTP_QUERY_CONNECTION),
2899 FE(HTTP_QUERY_ACCEPT),
2900 FE(HTTP_QUERY_ACCEPT_CHARSET),
2901 FE(HTTP_QUERY_ACCEPT_ENCODING),
2902 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
2903 FE(HTTP_QUERY_AUTHORIZATION),
2904 FE(HTTP_QUERY_CONTENT_ENCODING),
2905 FE(HTTP_QUERY_FORWARDED),
2906 FE(HTTP_QUERY_FROM),
2907 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
2908 FE(HTTP_QUERY_LOCATION),
2909 FE(HTTP_QUERY_ORIG_URI),
2910 FE(HTTP_QUERY_REFERER),
2911 FE(HTTP_QUERY_RETRY_AFTER),
2912 FE(HTTP_QUERY_SERVER),
2913 FE(HTTP_QUERY_TITLE),
2914 FE(HTTP_QUERY_USER_AGENT),
2915 FE(HTTP_QUERY_WWW_AUTHENTICATE),
2916 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
2917 FE(HTTP_QUERY_ACCEPT_RANGES),
2918 FE(HTTP_QUERY_SET_COOKIE),
2919 FE(HTTP_QUERY_COOKIE),
2920 FE(HTTP_QUERY_REQUEST_METHOD),
2921 FE(HTTP_QUERY_REFRESH),
2922 FE(HTTP_QUERY_CONTENT_DISPOSITION),
2923 FE(HTTP_QUERY_AGE),
2924 FE(HTTP_QUERY_CACHE_CONTROL),
2925 FE(HTTP_QUERY_CONTENT_BASE),
2926 FE(HTTP_QUERY_CONTENT_LOCATION),
2927 FE(HTTP_QUERY_CONTENT_MD5),
2928 FE(HTTP_QUERY_CONTENT_RANGE),
2929 FE(HTTP_QUERY_ETAG),
2930 FE(HTTP_QUERY_HOST),
2931 FE(HTTP_QUERY_IF_MATCH),
2932 FE(HTTP_QUERY_IF_NONE_MATCH),
2933 FE(HTTP_QUERY_IF_RANGE),
2934 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
2935 FE(HTTP_QUERY_MAX_FORWARDS),
2936 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
2937 FE(HTTP_QUERY_RANGE),
2938 FE(HTTP_QUERY_TRANSFER_ENCODING),
2939 FE(HTTP_QUERY_UPGRADE),
2940 FE(HTTP_QUERY_VARY),
2941 FE(HTTP_QUERY_VIA),
2942 FE(HTTP_QUERY_WARNING),
2943 FE(HTTP_QUERY_CUSTOM)
2945 static const wininet_flag_info modifier_flags[] = {
2946 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
2947 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
2948 FE(HTTP_QUERY_FLAG_NUMBER),
2949 FE(HTTP_QUERY_FLAG_COALESCE)
2951 #undef FE
2952 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
2953 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
2954 DWORD i;
2956 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, dwInfoLevel);
2957 TRACE(" Attribute:");
2958 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
2959 if (query_flags[i].val == info) {
2960 TRACE(" %s", query_flags[i].name);
2961 break;
2964 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
2965 TRACE(" Unknown (%08x)", info);
2968 TRACE(" Modifier:");
2969 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
2970 if (modifier_flags[i].val & info_mod) {
2971 TRACE(" %s", modifier_flags[i].name);
2972 info_mod &= ~ modifier_flags[i].val;
2976 if (info_mod) {
2977 TRACE(" Unknown (%08x)", info_mod);
2979 TRACE("\n");
2982 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
2983 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
2985 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2986 goto lend;
2989 if (lpBuffer == NULL)
2990 *lpdwBufferLength = 0;
2991 bSuccess = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
2992 lpBuffer, lpdwBufferLength, lpdwIndex);
2994 lend:
2995 if( lpwhr )
2996 WININET_Release( &lpwhr->hdr );
2998 TRACE("%d <--\n", bSuccess);
2999 return bSuccess;
3002 /***********************************************************************
3003 * HttpQueryInfoA (WININET.@)
3005 * Queries for information about an HTTP request
3007 * RETURNS
3008 * TRUE on success
3009 * FALSE on failure
3012 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3013 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3015 BOOL result;
3016 DWORD len;
3017 WCHAR* bufferW;
3019 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3020 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3022 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3023 lpdwBufferLength, lpdwIndex );
3026 if (lpBuffer)
3028 DWORD alloclen;
3029 len = (*lpdwBufferLength)*sizeof(WCHAR);
3030 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3032 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3033 if (alloclen < len)
3034 alloclen = len;
3036 else
3037 alloclen = len;
3038 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3039 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3040 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3041 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3042 } else
3044 bufferW = NULL;
3045 len = 0;
3048 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3049 &len, lpdwIndex );
3050 if( result )
3052 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3053 lpBuffer, *lpdwBufferLength, NULL, NULL );
3054 *lpdwBufferLength = len - 1;
3056 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3058 else
3059 /* since the strings being returned from HttpQueryInfoW should be
3060 * only ASCII characters, it is reasonable to assume that all of
3061 * the Unicode characters can be reduced to a single byte */
3062 *lpdwBufferLength = len / sizeof(WCHAR);
3064 HeapFree(GetProcessHeap(), 0, bufferW );
3066 return result;
3069 /***********************************************************************
3070 * HTTP_GetRedirectURL (internal)
3072 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3074 static WCHAR szHttp[] = {'h','t','t','p',0};
3075 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3076 http_session_t *lpwhs = lpwhr->lpHttpSession;
3077 URL_COMPONENTSW urlComponents;
3078 DWORD url_length = 0;
3079 LPWSTR orig_url;
3080 LPWSTR combined_url;
3082 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3083 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3084 urlComponents.dwSchemeLength = 0;
3085 urlComponents.lpszHostName = lpwhs->lpszHostName;
3086 urlComponents.dwHostNameLength = 0;
3087 urlComponents.nPort = lpwhs->nHostPort;
3088 urlComponents.lpszUserName = lpwhs->lpszUserName;
3089 urlComponents.dwUserNameLength = 0;
3090 urlComponents.lpszPassword = NULL;
3091 urlComponents.dwPasswordLength = 0;
3092 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3093 urlComponents.dwUrlPathLength = 0;
3094 urlComponents.lpszExtraInfo = NULL;
3095 urlComponents.dwExtraInfoLength = 0;
3097 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3098 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3099 return NULL;
3101 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3103 /* convert from bytes to characters */
3104 url_length = url_length / sizeof(WCHAR) - 1;
3105 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3107 HeapFree(GetProcessHeap(), 0, orig_url);
3108 return NULL;
3111 url_length = 0;
3112 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3113 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3115 HeapFree(GetProcessHeap(), 0, orig_url);
3116 return NULL;
3118 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3120 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3122 HeapFree(GetProcessHeap(), 0, orig_url);
3123 HeapFree(GetProcessHeap(), 0, combined_url);
3124 return NULL;
3126 HeapFree(GetProcessHeap(), 0, orig_url);
3127 return combined_url;
3131 /***********************************************************************
3132 * HTTP_HandleRedirect (internal)
3134 static DWORD HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3136 http_session_t *lpwhs = lpwhr->lpHttpSession;
3137 appinfo_t *hIC = lpwhs->lpAppInfo;
3138 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3139 WCHAR path[INTERNET_MAX_URL_LENGTH];
3140 int index;
3142 if(lpszUrl[0]=='/')
3144 /* if it's an absolute path, keep the same session info */
3145 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3147 else
3149 URL_COMPONENTSW urlComponents;
3150 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3151 static WCHAR szHttp[] = {'h','t','t','p',0};
3152 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3154 userName[0] = 0;
3155 hostName[0] = 0;
3156 protocol[0] = 0;
3158 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3159 urlComponents.lpszScheme = protocol;
3160 urlComponents.dwSchemeLength = 32;
3161 urlComponents.lpszHostName = hostName;
3162 urlComponents.dwHostNameLength = MAXHOSTNAME;
3163 urlComponents.lpszUserName = userName;
3164 urlComponents.dwUserNameLength = 1024;
3165 urlComponents.lpszPassword = NULL;
3166 urlComponents.dwPasswordLength = 0;
3167 urlComponents.lpszUrlPath = path;
3168 urlComponents.dwUrlPathLength = 2048;
3169 urlComponents.lpszExtraInfo = NULL;
3170 urlComponents.dwExtraInfoLength = 0;
3171 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3172 return INTERNET_GetLastError();
3174 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3175 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3177 TRACE("redirect from secure page to non-secure page\n");
3178 /* FIXME: warn about from secure redirect to non-secure page */
3179 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3181 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3182 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3184 TRACE("redirect from non-secure page to secure page\n");
3185 /* FIXME: notify about redirect to secure page */
3186 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3189 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3191 if (lstrlenW(protocol)>4) /*https*/
3192 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3193 else /*http*/
3194 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3197 #if 0
3199 * This upsets redirects to binary files on sourceforge.net
3200 * and gives an html page instead of the target file
3201 * Examination of the HTTP request sent by native wininet.dll
3202 * reveals that it doesn't send a referrer in that case.
3203 * Maybe there's a flag that enables this, or maybe a referrer
3204 * shouldn't be added in case of a redirect.
3207 /* consider the current host as the referrer */
3208 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3209 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3210 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3211 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3212 #endif
3214 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3215 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3216 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3218 int len;
3219 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3220 len = lstrlenW(hostName);
3221 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3222 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3223 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3225 else
3226 lpwhs->lpszHostName = heap_strdupW(hostName);
3228 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3230 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3231 lpwhs->lpszUserName = NULL;
3232 if (userName[0])
3233 lpwhs->lpszUserName = heap_strdupW(userName);
3235 if (!using_proxy)
3237 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3239 DWORD res;
3241 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3242 lpwhs->lpszServerName = heap_strdupW(hostName);
3243 lpwhs->nServerPort = urlComponents.nPort;
3245 NETCON_close(&lpwhr->netConnection);
3246 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS)
3247 return res;
3249 res = NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE);
3250 if (res != ERROR_SUCCESS)
3251 return res;
3253 lpwhr->read_pos = lpwhr->read_size = 0;
3254 lpwhr->read_chunked = FALSE;
3257 else
3258 TRACE("Redirect through proxy\n");
3261 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3262 lpwhr->lpszPath=NULL;
3263 if (*path)
3265 DWORD needed = 0;
3266 HRESULT rc;
3268 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3269 if (rc != E_POINTER)
3270 needed = strlenW(path)+1;
3271 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3272 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3273 URL_ESCAPE_SPACES_ONLY);
3274 if (rc != S_OK)
3276 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3277 strcpyW(lpwhr->lpszPath,path);
3281 /* Remove custom content-type/length headers on redirects. */
3282 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3283 if (0 <= index)
3284 HTTP_DeleteCustomHeader(lpwhr, index);
3285 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3286 if (0 <= index)
3287 HTTP_DeleteCustomHeader(lpwhr, index);
3289 return ERROR_SUCCESS;
3292 /***********************************************************************
3293 * HTTP_build_req (internal)
3295 * concatenate all the strings in the request together
3297 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3299 LPCWSTR *t;
3300 LPWSTR str;
3302 for( t = list; *t ; t++ )
3303 len += strlenW( *t );
3304 len++;
3306 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3307 *str = 0;
3309 for( t = list; *t ; t++ )
3310 strcatW( str, *t );
3312 return str;
3315 static DWORD HTTP_SecureProxyConnect(http_request_t *lpwhr)
3317 LPWSTR lpszPath;
3318 LPWSTR requestString;
3319 INT len;
3320 INT cnt;
3321 INT responseLen;
3322 char *ascii_req;
3323 DWORD res;
3324 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3325 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3326 http_session_t *lpwhs = lpwhr->lpHttpSession;
3328 TRACE("\n");
3330 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3331 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3332 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3333 HeapFree( GetProcessHeap(), 0, lpszPath );
3335 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3336 NULL, 0, NULL, NULL );
3337 len--; /* the nul terminator isn't needed */
3338 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3339 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3340 ascii_req, len, NULL, NULL );
3341 HeapFree( GetProcessHeap(), 0, requestString );
3343 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3345 res = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3346 HeapFree( GetProcessHeap(), 0, ascii_req );
3347 if (res != ERROR_SUCCESS)
3348 return res;
3350 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3351 if (!responseLen)
3352 return ERROR_HTTP_INVALID_HEADER;
3354 return ERROR_SUCCESS;
3357 static void HTTP_InsertCookies(http_request_t *lpwhr)
3359 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3360 LPWSTR lpszCookies, lpszUrl = NULL;
3361 DWORD nCookieSize, size;
3362 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3364 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3365 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3366 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3368 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3370 int cnt = 0;
3371 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3373 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3374 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3376 cnt += sprintfW(lpszCookies, szCookie);
3377 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3378 strcatW(lpszCookies, szCrLf);
3380 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3381 HeapFree(GetProcessHeap(), 0, lpszCookies);
3384 HeapFree(GetProcessHeap(), 0, lpszUrl);
3387 /***********************************************************************
3388 * HTTP_HttpSendRequestW (internal)
3390 * Sends the specified request to the HTTP server
3392 * RETURNS
3393 * TRUE on success
3394 * FALSE on failure
3397 static DWORD HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3398 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3399 DWORD dwContentLength, BOOL bEndRequest)
3401 INT cnt;
3402 BOOL redirected = FALSE;
3403 LPWSTR requestString = NULL;
3404 INT responseLen;
3405 BOOL loop_next;
3406 INTERNET_ASYNC_RESULT iar;
3407 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3408 static const WCHAR szContentLength[] =
3409 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3410 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3411 DWORD res;
3413 TRACE("--> %p\n", lpwhr);
3415 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3417 /* if the verb is NULL default to GET */
3418 if (!lpwhr->lpszVerb)
3419 lpwhr->lpszVerb = heap_strdupW(szGET);
3421 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3423 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3424 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3425 lpwhr->dwBytesToWrite = dwContentLength;
3427 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3429 WCHAR *agent_header;
3430 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3431 int len;
3433 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3434 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3435 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3437 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3438 HeapFree(GetProcessHeap(), 0, agent_header);
3440 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3442 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3443 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3445 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3447 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3448 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3449 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3454 DWORD len;
3455 char *ascii_req;
3457 loop_next = FALSE;
3459 /* like native, just in case the caller forgot to call InternetReadFile
3460 * for all the data */
3461 HTTP_DrainContent(lpwhr);
3462 lpwhr->dwContentRead = 0;
3464 if (TRACE_ON(wininet))
3466 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3467 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3470 HTTP_FixURL(lpwhr);
3471 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3473 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3475 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3476 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3478 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3479 HTTP_InsertCookies(lpwhr);
3481 /* add the headers the caller supplied */
3482 if( lpszHeaders && dwHeaderLength )
3484 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3485 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3488 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3490 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3491 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3492 HeapFree(GetProcessHeap(), 0, url);
3494 else
3495 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3498 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3500 /* Send the request and store the results */
3501 if ((res = HTTP_OpenConnection(lpwhr)) != ERROR_SUCCESS)
3502 goto lend;
3504 /* send the request as ASCII, tack on the optional data */
3505 if (!lpOptional || redirected)
3506 dwOptionalLength = 0;
3507 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3508 NULL, 0, NULL, NULL );
3509 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3510 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3511 ascii_req, len, NULL, NULL );
3512 if( lpOptional )
3513 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3514 len = (len + dwOptionalLength - 1);
3515 ascii_req[len] = 0;
3516 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3518 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3519 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3521 res = NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3522 HeapFree( GetProcessHeap(), 0, ascii_req );
3524 lpwhr->dwBytesWritten = dwOptionalLength;
3526 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3527 INTERNET_STATUS_REQUEST_SENT,
3528 &len, sizeof(DWORD));
3530 if (bEndRequest)
3532 DWORD dwBufferSize;
3533 DWORD dwStatusCode;
3535 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3536 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3538 if (res != ERROR_SUCCESS)
3539 goto lend;
3541 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3543 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3544 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3545 sizeof(DWORD));
3547 HTTP_ProcessCookies(lpwhr);
3549 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3551 dwBufferSize = sizeof(dwStatusCode);
3552 if (!HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3553 &dwStatusCode,&dwBufferSize,NULL))
3554 dwStatusCode = 0;
3556 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
3558 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3559 dwBufferSize=sizeof(szNewLocation);
3560 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
3561 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL))
3563 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3565 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3566 lpwhr->lpszVerb = heap_strdupW(szGET);
3568 HTTP_DrainContent(lpwhr);
3569 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3571 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3572 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3573 res = HTTP_HandleRedirect(lpwhr, new_url);
3574 if (res == ERROR_SUCCESS)
3576 HeapFree(GetProcessHeap(), 0, requestString);
3577 loop_next = TRUE;
3579 HeapFree( GetProcessHeap(), 0, new_url );
3581 redirected = TRUE;
3584 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
3586 WCHAR szAuthValue[2048];
3587 dwBufferSize=2048;
3588 if (dwStatusCode == HTTP_STATUS_DENIED)
3590 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3591 DWORD dwIndex = 0;
3592 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
3594 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3595 &lpwhr->pAuthInfo,
3596 lpwhr->lpHttpSession->lpszUserName,
3597 lpwhr->lpHttpSession->lpszPassword,
3598 Host->lpszValue))
3600 loop_next = TRUE;
3601 break;
3605 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3607 DWORD dwIndex = 0;
3608 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
3610 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3611 &lpwhr->pProxyAuthInfo,
3612 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3613 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
3614 NULL))
3616 loop_next = TRUE;
3617 break;
3623 else
3624 res = ERROR_SUCCESS;
3626 while (loop_next);
3628 if(res == ERROR_SUCCESS) {
3629 WCHAR url[INTERNET_MAX_URL_LENGTH];
3630 WCHAR cacheFileName[MAX_PATH+1];
3631 BOOL b;
3633 b = HTTP_GetRequestURL(lpwhr, url);
3634 if(!b) {
3635 WARN("Could not get URL\n");
3636 goto lend;
3639 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3640 if(b) {
3641 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3642 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3643 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3644 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3645 WARN("Could not create file: %u\n", GetLastError());
3646 lpwhr->hCacheFile = NULL;
3648 }else {
3649 WARN("Could not create cache entry: %08x\n", GetLastError());
3653 lend:
3655 HeapFree(GetProcessHeap(), 0, requestString);
3657 /* TODO: send notification for P3P header */
3659 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3661 if (res == ERROR_SUCCESS && lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite)
3662 HTTP_ReceiveRequestData(lpwhr, TRUE);
3663 else
3665 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3666 iar.dwError = res;
3668 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3669 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3670 sizeof(INTERNET_ASYNC_RESULT));
3674 TRACE("<--\n");
3675 return res;
3678 /***********************************************************************
3680 * Helper functions for the HttpSendRequest(Ex) functions
3683 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
3685 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
3686 http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
3688 TRACE("%p\n", lpwhr);
3690 HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
3691 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
3692 req->dwContentLength, req->bEndRequest);
3694 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
3698 static DWORD HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
3700 INT responseLen;
3701 DWORD dwBufferSize;
3702 INTERNET_ASYNC_RESULT iar;
3703 DWORD res = ERROR_SUCCESS;
3705 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3706 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3708 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3709 if (!responseLen)
3710 res = ERROR_HTTP_HEADER_NOT_FOUND;
3712 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3713 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
3715 /* process cookies here. Is this right? */
3716 HTTP_ProcessCookies(lpwhr);
3718 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3720 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
3722 DWORD dwCode,dwCodeLength = sizeof(DWORD);
3723 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) &&
3724 (dwCode == 302 || dwCode == 301 || dwCode == 303))
3726 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3727 dwBufferSize=sizeof(szNewLocation);
3728 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL))
3730 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3732 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3733 lpwhr->lpszVerb = heap_strdupW(szGET);
3735 HTTP_DrainContent(lpwhr);
3736 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3738 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3739 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3740 res = HTTP_HandleRedirect(lpwhr, new_url);
3741 if (res == ERROR_SUCCESS)
3742 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
3743 HeapFree( GetProcessHeap(), 0, new_url );
3749 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3750 iar.dwError = res;
3752 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3753 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3754 sizeof(INTERNET_ASYNC_RESULT));
3755 return res;
3758 /***********************************************************************
3759 * HttpEndRequestA (WININET.@)
3761 * Ends an HTTP request that was started by HttpSendRequestEx
3763 * RETURNS
3764 * TRUE if successful
3765 * FALSE on failure
3768 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
3769 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3771 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
3773 if (lpBuffersOut)
3775 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3776 return FALSE;
3779 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
3782 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
3784 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
3785 http_request_t *lpwhr = (http_request_t*)work->hdr;
3787 TRACE("%p\n", lpwhr);
3789 HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
3792 /***********************************************************************
3793 * HttpEndRequestW (WININET.@)
3795 * Ends an HTTP request that was started by HttpSendRequestEx
3797 * RETURNS
3798 * TRUE if successful
3799 * FALSE on failure
3802 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
3803 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3805 http_request_t *lpwhr;
3806 DWORD res;
3808 TRACE("-->\n");
3810 if (lpBuffersOut)
3812 SetLastError(ERROR_INVALID_PARAMETER);
3813 return FALSE;
3816 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3818 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3820 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3821 if (lpwhr)
3822 WININET_Release( &lpwhr->hdr );
3823 return FALSE;
3825 lpwhr->hdr.dwFlags |= dwFlags;
3827 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3829 WORKREQUEST work;
3830 struct WORKREQ_HTTPENDREQUESTW *request;
3832 work.asyncproc = AsyncHttpEndRequestProc;
3833 work.hdr = WININET_AddRef( &lpwhr->hdr );
3835 request = &work.u.HttpEndRequestW;
3836 request->dwFlags = dwFlags;
3837 request->dwContext = dwContext;
3839 INTERNET_AsyncCall(&work);
3840 res = ERROR_IO_PENDING;
3842 else
3843 res = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
3845 WININET_Release( &lpwhr->hdr );
3846 TRACE("%u <--\n", res);
3847 if(res != ERROR_SUCCESS)
3848 SetLastError(res);
3849 return res == ERROR_SUCCESS;
3852 /***********************************************************************
3853 * HttpSendRequestExA (WININET.@)
3855 * Sends the specified request to the HTTP server and allows chunked
3856 * transfers.
3858 * RETURNS
3859 * Success: TRUE
3860 * Failure: FALSE, call GetLastError() for more information.
3862 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3863 LPINTERNET_BUFFERSA lpBuffersIn,
3864 LPINTERNET_BUFFERSA lpBuffersOut,
3865 DWORD dwFlags, DWORD_PTR dwContext)
3867 INTERNET_BUFFERSW BuffersInW;
3868 BOOL rc = FALSE;
3869 DWORD headerlen;
3870 LPWSTR header = NULL;
3872 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3873 lpBuffersOut, dwFlags, dwContext);
3875 if (lpBuffersIn)
3877 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3878 if (lpBuffersIn->lpcszHeader)
3880 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3881 lpBuffersIn->dwHeadersLength,0,0);
3882 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3883 if (!(BuffersInW.lpcszHeader = header))
3885 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3886 return FALSE;
3888 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3889 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3890 header, headerlen);
3892 else
3893 BuffersInW.lpcszHeader = NULL;
3894 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3895 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3896 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3897 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3898 BuffersInW.Next = NULL;
3901 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3903 HeapFree(GetProcessHeap(),0,header);
3905 return rc;
3908 /***********************************************************************
3909 * HttpSendRequestExW (WININET.@)
3911 * Sends the specified request to the HTTP server and allows chunked
3912 * transfers
3914 * RETURNS
3915 * Success: TRUE
3916 * Failure: FALSE, call GetLastError() for more information.
3918 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3919 LPINTERNET_BUFFERSW lpBuffersIn,
3920 LPINTERNET_BUFFERSW lpBuffersOut,
3921 DWORD dwFlags, DWORD_PTR dwContext)
3923 http_request_t *lpwhr;
3924 http_session_t *lpwhs;
3925 appinfo_t *hIC;
3926 DWORD res;
3928 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3929 lpBuffersOut, dwFlags, dwContext);
3931 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3933 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3935 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3936 goto lend;
3939 lpwhs = lpwhr->lpHttpSession;
3940 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3941 hIC = lpwhs->lpAppInfo;
3942 assert(hIC->hdr.htype == WH_HINIT);
3944 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3946 WORKREQUEST workRequest;
3947 struct WORKREQ_HTTPSENDREQUESTW *req;
3949 workRequest.asyncproc = AsyncHttpSendRequestProc;
3950 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3951 req = &workRequest.u.HttpSendRequestW;
3952 if (lpBuffersIn)
3954 DWORD size = 0;
3956 if (lpBuffersIn->lpcszHeader)
3958 if (lpBuffersIn->dwHeadersLength == ~0u)
3959 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
3960 else
3961 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
3963 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
3964 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
3966 else req->lpszHeader = NULL;
3968 req->dwHeaderLength = size / sizeof(WCHAR);
3969 req->lpOptional = lpBuffersIn->lpvBuffer;
3970 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3971 req->dwContentLength = lpBuffersIn->dwBufferTotal;
3973 else
3975 req->lpszHeader = NULL;
3976 req->dwHeaderLength = 0;
3977 req->lpOptional = NULL;
3978 req->dwOptionalLength = 0;
3979 req->dwContentLength = 0;
3982 req->bEndRequest = FALSE;
3984 INTERNET_AsyncCall(&workRequest);
3986 * This is from windows.
3988 res = ERROR_IO_PENDING;
3990 else
3992 if (lpBuffersIn)
3993 res = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3994 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
3995 lpBuffersIn->dwBufferTotal, FALSE);
3996 else
3997 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
4000 lend:
4001 if ( lpwhr )
4002 WININET_Release( &lpwhr->hdr );
4004 TRACE("<---\n");
4005 if(res != ERROR_SUCCESS)
4006 SetLastError(res);
4007 return res == ERROR_SUCCESS;
4010 /***********************************************************************
4011 * HttpSendRequestW (WININET.@)
4013 * Sends the specified request to the HTTP server
4015 * RETURNS
4016 * TRUE on success
4017 * FALSE on failure
4020 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4021 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4023 http_request_t *lpwhr;
4024 http_session_t *lpwhs = NULL;
4025 appinfo_t *hIC = NULL;
4026 DWORD res = ERROR_SUCCESS;
4028 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4029 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4031 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
4032 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4034 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4035 goto lend;
4038 lpwhs = lpwhr->lpHttpSession;
4039 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
4041 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4042 goto lend;
4045 hIC = lpwhs->lpAppInfo;
4046 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
4048 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4049 goto lend;
4052 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4054 WORKREQUEST workRequest;
4055 struct WORKREQ_HTTPSENDREQUESTW *req;
4057 workRequest.asyncproc = AsyncHttpSendRequestProc;
4058 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4059 req = &workRequest.u.HttpSendRequestW;
4060 if (lpszHeaders)
4062 DWORD size;
4064 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4065 else size = dwHeaderLength * sizeof(WCHAR);
4067 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4068 memcpy(req->lpszHeader, lpszHeaders, size);
4070 else
4071 req->lpszHeader = 0;
4072 req->dwHeaderLength = dwHeaderLength;
4073 req->lpOptional = lpOptional;
4074 req->dwOptionalLength = dwOptionalLength;
4075 req->dwContentLength = dwOptionalLength;
4076 req->bEndRequest = TRUE;
4078 INTERNET_AsyncCall(&workRequest);
4080 * This is from windows.
4082 res = ERROR_IO_PENDING;
4084 else
4086 res = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
4087 dwHeaderLength, lpOptional, dwOptionalLength,
4088 dwOptionalLength, TRUE);
4090 lend:
4091 if( lpwhr )
4092 WININET_Release( &lpwhr->hdr );
4094 if(res != ERROR_SUCCESS)
4095 SetLastError(res);
4096 return res == ERROR_SUCCESS;
4099 /***********************************************************************
4100 * HttpSendRequestA (WININET.@)
4102 * Sends the specified request to the HTTP server
4104 * RETURNS
4105 * TRUE on success
4106 * FALSE on failure
4109 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4110 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4112 BOOL result;
4113 LPWSTR szHeaders=NULL;
4114 DWORD nLen=dwHeaderLength;
4115 if(lpszHeaders!=NULL)
4117 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4118 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4119 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4121 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4122 HeapFree(GetProcessHeap(),0,szHeaders);
4123 return result;
4126 /***********************************************************************
4127 * HTTPSESSION_Destroy (internal)
4129 * Deallocate session handle
4132 static void HTTPSESSION_Destroy(object_header_t *hdr)
4134 http_session_t *lpwhs = (http_session_t*) hdr;
4136 TRACE("%p\n", lpwhs);
4138 WININET_Release(&lpwhs->lpAppInfo->hdr);
4140 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4141 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4142 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4143 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4144 HeapFree(GetProcessHeap(), 0, lpwhs);
4147 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4149 switch(option) {
4150 case INTERNET_OPTION_HANDLE_TYPE:
4151 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4153 if (*size < sizeof(ULONG))
4154 return ERROR_INSUFFICIENT_BUFFER;
4156 *size = sizeof(DWORD);
4157 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4158 return ERROR_SUCCESS;
4161 return INET_QueryOption(option, buffer, size, unicode);
4164 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4166 http_session_t *ses = (http_session_t*)hdr;
4168 switch(option) {
4169 case INTERNET_OPTION_USERNAME:
4171 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4172 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4173 return ERROR_SUCCESS;
4175 case INTERNET_OPTION_PASSWORD:
4177 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4178 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4179 return ERROR_SUCCESS;
4181 default: break;
4184 return ERROR_INTERNET_INVALID_OPTION;
4187 static const object_vtbl_t HTTPSESSIONVtbl = {
4188 HTTPSESSION_Destroy,
4189 NULL,
4190 HTTPSESSION_QueryOption,
4191 HTTPSESSION_SetOption,
4192 NULL,
4193 NULL,
4194 NULL,
4195 NULL,
4196 NULL
4200 /***********************************************************************
4201 * HTTP_Connect (internal)
4203 * Create http session handle
4205 * RETURNS
4206 * HINTERNET a session handle on success
4207 * NULL on failure
4210 HINTERNET HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4211 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4212 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4213 DWORD dwInternalFlags)
4215 http_session_t *lpwhs = NULL;
4216 HINTERNET handle = NULL;
4218 TRACE("-->\n");
4220 if (!lpszServerName || !lpszServerName[0])
4222 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4223 goto lerror;
4226 assert( hIC->hdr.htype == WH_HINIT );
4228 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4229 if (NULL == lpwhs)
4231 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4232 goto lerror;
4236 * According to my tests. The name is not resolved until a request is sent
4239 lpwhs->hdr.htype = WH_HHTTPSESSION;
4240 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4241 lpwhs->hdr.dwFlags = dwFlags;
4242 lpwhs->hdr.dwContext = dwContext;
4243 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4244 lpwhs->hdr.refs = 1;
4245 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4247 WININET_AddRef( &hIC->hdr );
4248 lpwhs->lpAppInfo = hIC;
4249 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4251 handle = WININET_AllocHandle( &lpwhs->hdr );
4252 if (NULL == handle)
4254 ERR("Failed to alloc handle\n");
4255 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4256 goto lerror;
4259 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4260 if(strchrW(hIC->lpszProxy, ' '))
4261 FIXME("Several proxies not implemented.\n");
4262 if(hIC->lpszProxyBypass)
4263 FIXME("Proxy bypass is ignored.\n");
4265 if (lpszServerName && lpszServerName[0])
4267 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4268 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4270 if (lpszUserName && lpszUserName[0])
4271 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4272 if (lpszPassword && lpszPassword[0])
4273 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4274 lpwhs->nServerPort = nServerPort;
4275 lpwhs->nHostPort = nServerPort;
4277 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4278 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4280 INTERNET_SendCallback(&hIC->hdr, dwContext,
4281 INTERNET_STATUS_HANDLE_CREATED, &handle,
4282 sizeof(handle));
4285 lerror:
4286 if( lpwhs )
4287 WININET_Release( &lpwhs->hdr );
4290 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4291 * windows
4294 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4295 return handle;
4299 /***********************************************************************
4300 * HTTP_OpenConnection (internal)
4302 * Connect to a web server
4304 * RETURNS
4306 * TRUE on success
4307 * FALSE on failure
4309 static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
4311 http_session_t *lpwhs;
4312 appinfo_t *hIC = NULL;
4313 char szaddr[INET6_ADDRSTRLEN];
4314 const void *addr;
4315 DWORD res = ERROR_SUCCESS;
4317 TRACE("-->\n");
4320 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4322 res = ERROR_INVALID_PARAMETER;
4323 goto lend;
4326 if (NETCON_connected(&lpwhr->netConnection))
4327 goto lend;
4328 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
4330 lpwhs = lpwhr->lpHttpSession;
4332 hIC = lpwhs->lpAppInfo;
4333 switch (lpwhs->socketAddress.ss_family)
4335 case AF_INET:
4336 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4337 break;
4338 case AF_INET6:
4339 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4340 break;
4341 default:
4342 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4343 return ERROR_INTERNET_NAME_NOT_RESOLVED;
4345 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4346 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4347 INTERNET_STATUS_CONNECTING_TO_SERVER,
4348 szaddr,
4349 strlen(szaddr)+1);
4351 res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
4352 if (res != ERROR_SUCCESS)
4354 WARN("Socket creation failed: %u\n", res);
4355 goto lend;
4358 res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4359 lpwhs->sa_len);
4360 if(res != ERROR_SUCCESS)
4361 goto lend;
4363 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4365 /* Note: we differ from Microsoft's WinINet here. they seem to have
4366 * a bug that causes no status callbacks to be sent when starting
4367 * a tunnel to a proxy server using the CONNECT verb. i believe our
4368 * behaviour to be more correct and to not cause any incompatibilities
4369 * because using a secure connection through a proxy server is a rare
4370 * case that would be hard for anyone to depend on */
4371 if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS)
4372 goto lend;
4374 res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
4375 if(res != ERROR_SUCCESS)
4377 WARN("Couldn't connect securely to host\n");
4378 goto lend;
4382 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4383 INTERNET_STATUS_CONNECTED_TO_SERVER,
4384 szaddr, strlen(szaddr)+1);
4386 lend:
4387 lpwhr->read_pos = lpwhr->read_size = 0;
4388 lpwhr->read_chunked = FALSE;
4390 TRACE("%d <--\n", res);
4391 return res;
4395 /***********************************************************************
4396 * HTTP_clear_response_headers (internal)
4398 * clear out any old response headers
4400 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4402 DWORD i;
4404 for( i=0; i<lpwhr->nCustHeaders; i++)
4406 if( !lpwhr->pCustHeaders[i].lpszField )
4407 continue;
4408 if( !lpwhr->pCustHeaders[i].lpszValue )
4409 continue;
4410 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4411 continue;
4412 HTTP_DeleteCustomHeader( lpwhr, i );
4413 i--;
4417 /***********************************************************************
4418 * HTTP_GetResponseHeaders (internal)
4420 * Read server response
4422 * RETURNS
4424 * TRUE on success
4425 * FALSE on error
4427 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4429 INT cbreaks = 0;
4430 WCHAR buffer[MAX_REPLY_LEN];
4431 DWORD buflen = MAX_REPLY_LEN;
4432 BOOL bSuccess = FALSE;
4433 INT rc = 0;
4434 char bufferA[MAX_REPLY_LEN];
4435 LPWSTR status_code = NULL, status_text = NULL;
4436 DWORD cchMaxRawHeaders = 1024;
4437 LPWSTR lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4438 LPWSTR temp;
4439 DWORD cchRawHeaders = 0;
4440 BOOL codeHundred = FALSE;
4442 TRACE("-->\n");
4444 /* clear old response headers (eg. from a redirect response) */
4445 if (clear) HTTP_clear_response_headers( lpwhr );
4447 if (!NETCON_connected(&lpwhr->netConnection))
4448 goto lend;
4450 do {
4451 static const WCHAR szHundred[] = {'1','0','0',0};
4453 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4455 buflen = MAX_REPLY_LEN;
4456 if (!read_line(lpwhr, bufferA, &buflen))
4457 goto lend;
4458 rc += buflen;
4459 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4460 /* check is this a status code line? */
4461 if (!strncmpW(buffer, g_szHttp1_0, 4))
4463 /* split the version from the status code */
4464 status_code = strchrW( buffer, ' ' );
4465 if( !status_code )
4466 goto lend;
4467 *status_code++=0;
4469 /* split the status code from the status text */
4470 status_text = strchrW( status_code, ' ' );
4471 if( !status_text )
4472 goto lend;
4473 *status_text++=0;
4475 TRACE("version [%s] status code [%s] status text [%s]\n",
4476 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4478 codeHundred = (!strcmpW(status_code, szHundred));
4480 else if (!codeHundred)
4482 FIXME("Non status line at head of response (%s)\n",debugstr_w(buffer));
4483 goto lend;
4485 } while (codeHundred);
4487 /* Add status code */
4488 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4489 HTTP_ADDHDR_FLAG_REPLACE);
4491 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4492 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4494 lpwhr->lpszVersion = heap_strdupW(buffer);
4495 lpwhr->lpszStatusText = heap_strdupW(status_text);
4497 /* Restore the spaces */
4498 *(status_code-1) = ' ';
4499 *(status_text-1) = ' ';
4501 /* regenerate raw headers */
4502 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4503 cchMaxRawHeaders *= 2;
4504 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4505 if (temp == NULL) goto lend;
4506 lpszRawHeaders = temp;
4507 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4508 cchRawHeaders += (buflen-1);
4509 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4510 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4511 lpszRawHeaders[cchRawHeaders] = '\0';
4513 /* Parse each response line */
4516 buflen = MAX_REPLY_LEN;
4517 if (read_line(lpwhr, bufferA, &buflen))
4519 LPWSTR * pFieldAndValue;
4521 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4523 if (!bufferA[0]) break;
4524 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4526 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4527 if (pFieldAndValue)
4529 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4530 cchMaxRawHeaders *= 2;
4531 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4532 if (temp == NULL) goto lend;
4533 lpszRawHeaders = temp;
4534 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4535 cchRawHeaders += (buflen-1);
4536 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4537 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4538 lpszRawHeaders[cchRawHeaders] = '\0';
4540 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4541 HTTP_ADDREQ_FLAG_ADD );
4543 HTTP_FreeTokens(pFieldAndValue);
4546 else
4548 cbreaks++;
4549 if (cbreaks >= 2)
4550 break;
4552 }while(1);
4554 /* make sure the response header is terminated with an empty line. Some apps really
4555 truly care about that empty line being there for some reason. Just add it to the
4556 header. */
4557 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4559 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4560 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4561 if (temp == NULL) goto lend;
4562 lpszRawHeaders = temp;
4565 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4567 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4568 lpwhr->lpszRawHeaders = lpszRawHeaders;
4569 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4570 bSuccess = TRUE;
4572 lend:
4574 TRACE("<--\n");
4575 if (bSuccess)
4576 return rc;
4577 else
4579 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4580 return 0;
4584 /***********************************************************************
4585 * HTTP_InterpretHttpHeader (internal)
4587 * Parse server response
4589 * RETURNS
4591 * Pointer to array of field, value, NULL on success.
4592 * NULL on error.
4594 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4596 LPWSTR * pTokenPair;
4597 LPWSTR pszColon;
4598 INT len;
4600 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4602 pszColon = strchrW(buffer, ':');
4603 /* must have two tokens */
4604 if (!pszColon)
4606 HTTP_FreeTokens(pTokenPair);
4607 if (buffer[0])
4608 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4609 return NULL;
4612 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4613 if (!pTokenPair[0])
4615 HTTP_FreeTokens(pTokenPair);
4616 return NULL;
4618 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4619 pTokenPair[0][pszColon - buffer] = '\0';
4621 /* skip colon */
4622 pszColon++;
4623 len = strlenW(pszColon);
4624 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4625 if (!pTokenPair[1])
4627 HTTP_FreeTokens(pTokenPair);
4628 return NULL;
4630 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4632 strip_spaces(pTokenPair[0]);
4633 strip_spaces(pTokenPair[1]);
4635 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4636 return pTokenPair;
4639 /***********************************************************************
4640 * HTTP_ProcessHeader (internal)
4642 * Stuff header into header tables according to <dwModifier>
4646 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4648 static DWORD HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4650 LPHTTPHEADERW lphttpHdr = NULL;
4651 INT index = -1;
4652 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4653 DWORD res = ERROR_HTTP_INVALID_HEADER;
4655 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4657 /* REPLACE wins out over ADD */
4658 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4659 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4661 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4662 index = -1;
4663 else
4664 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4666 if (index >= 0)
4668 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4669 return ERROR_HTTP_INVALID_HEADER;
4670 lphttpHdr = &lpwhr->pCustHeaders[index];
4672 else if (value)
4674 HTTPHEADERW hdr;
4676 hdr.lpszField = (LPWSTR)field;
4677 hdr.lpszValue = (LPWSTR)value;
4678 hdr.wFlags = hdr.wCount = 0;
4680 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4681 hdr.wFlags |= HDR_ISREQUEST;
4683 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4685 /* no value to delete */
4686 else return ERROR_SUCCESS;
4688 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4689 lphttpHdr->wFlags |= HDR_ISREQUEST;
4690 else
4691 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4693 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4695 HTTP_DeleteCustomHeader( lpwhr, index );
4697 if (value)
4699 HTTPHEADERW hdr;
4701 hdr.lpszField = (LPWSTR)field;
4702 hdr.lpszValue = (LPWSTR)value;
4703 hdr.wFlags = hdr.wCount = 0;
4705 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4706 hdr.wFlags |= HDR_ISREQUEST;
4708 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4711 return ERROR_SUCCESS;
4713 else if (dwModifier & COALESCEFLAGS)
4715 LPWSTR lpsztmp;
4716 WCHAR ch = 0;
4717 INT len = 0;
4718 INT origlen = strlenW(lphttpHdr->lpszValue);
4719 INT valuelen = strlenW(value);
4721 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4723 ch = ',';
4724 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4726 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4728 ch = ';';
4729 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4732 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4734 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4735 if (lpsztmp)
4737 lphttpHdr->lpszValue = lpsztmp;
4738 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4739 if (ch > 0)
4741 lphttpHdr->lpszValue[origlen] = ch;
4742 origlen++;
4743 lphttpHdr->lpszValue[origlen] = ' ';
4744 origlen++;
4747 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4748 lphttpHdr->lpszValue[len] = '\0';
4749 res = ERROR_SUCCESS;
4751 else
4753 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4754 res = ERROR_OUTOFMEMORY;
4757 TRACE("<-- %d\n", res);
4758 return res;
4762 /***********************************************************************
4763 * HTTP_FinishedReading (internal)
4765 * Called when all content from server has been read by client.
4768 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4770 BOOL keepalive = HTTP_KeepAlive(lpwhr);
4772 TRACE("\n");
4775 if (!keepalive)
4777 HTTPREQ_CloseConnection(&lpwhr->hdr);
4780 /* FIXME: store data in the URL cache here */
4782 return TRUE;
4786 /***********************************************************************
4787 * HTTP_GetCustomHeaderIndex (internal)
4789 * Return index of custom header from header array
4792 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
4793 int requested_index, BOOL request_only)
4795 DWORD index;
4797 TRACE("%s\n", debugstr_w(lpszField));
4799 for (index = 0; index < lpwhr->nCustHeaders; index++)
4801 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
4802 continue;
4804 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4805 continue;
4807 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4808 continue;
4810 if (requested_index == 0)
4811 break;
4812 requested_index --;
4815 if (index >= lpwhr->nCustHeaders)
4816 index = -1;
4818 TRACE("Return: %d\n", index);
4819 return index;
4823 /***********************************************************************
4824 * HTTP_InsertCustomHeader (internal)
4826 * Insert header into array
4829 static DWORD HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
4831 INT count;
4832 LPHTTPHEADERW lph = NULL;
4834 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
4835 count = lpwhr->nCustHeaders + 1;
4836 if (count > 1)
4837 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
4838 else
4839 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
4841 if (!lph)
4842 return ERROR_OUTOFMEMORY;
4844 lpwhr->pCustHeaders = lph;
4845 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
4846 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
4847 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
4848 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
4849 lpwhr->nCustHeaders++;
4851 return ERROR_SUCCESS;
4855 /***********************************************************************
4856 * HTTP_DeleteCustomHeader (internal)
4858 * Delete header from array
4859 * If this function is called, the indexs may change.
4861 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
4863 if( lpwhr->nCustHeaders <= 0 )
4864 return FALSE;
4865 if( index >= lpwhr->nCustHeaders )
4866 return FALSE;
4867 lpwhr->nCustHeaders--;
4869 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
4870 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
4872 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
4873 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
4874 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
4876 return TRUE;
4880 /***********************************************************************
4881 * HTTP_VerifyValidHeader (internal)
4883 * Verify the given header is not invalid for the given http request
4886 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
4888 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4889 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
4890 return ERROR_HTTP_INVALID_HEADER;
4892 return ERROR_SUCCESS;
4895 /***********************************************************************
4896 * IsHostInProxyBypassList (@)
4898 * Undocumented
4901 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
4903 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
4904 return FALSE;