[WININET]
[reactos.git] / reactos / dll / win32 / wininet / http.c
1 /*
2 * Wininet - Http Implementation
3 *
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
10 *
11 * Ulrich Czekalla
12 * David Hammerton
13 *
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.
18 *
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.
23 *
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
27 */
28
29 #include "config.h"
30 #include "wine/port.h"
31
32 #if defined(__MINGW32__) || defined (_MSC_VER)
33 #include <ws2tcpip.h>
34 #endif
35
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
54
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"
66
67 #include "internet.h"
68 #include "wine/debug.h"
69 #include "wine/exception.h"
70 #include "wine/unicode.h"
71
72 #include "inet_ntop.c"
73
74 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
75
76 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
77 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
78 static const WCHAR szOK[] = {'O','K',0};
79 static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
80 static const WCHAR hostW[] = { 'H','o','s','t',0 };
81 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
82 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
83 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
84 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
85 static const WCHAR szGET[] = { 'G','E','T', 0 };
86 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
87 static const WCHAR szCrLf[] = {'\r','\n', 0};
88
89 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
90 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
91 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
92 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
93 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
94 static const WCHAR szAge[] = { 'A','g','e',0 };
95 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
96 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
97 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
98 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
99 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
100 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
101 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
102 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
103 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
104 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
105 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
106 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 };
107 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
108 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
109 static const WCHAR szDate[] = { 'D','a','t','e',0 };
110 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
111 static const WCHAR szETag[] = { 'E','T','a','g',0 };
112 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
113 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
114 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
115 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
116 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
117 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
118 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
119 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
120 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
121 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
122 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
123 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
124 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
125 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
126 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
127 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
128 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
129 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
130 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
131 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
132 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
133 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 };
134 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
135 static const WCHAR szURI[] = { 'U','R','I',0 };
136 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
137 static const WCHAR szVary[] = { 'V','a','r','y',0 };
138 static const WCHAR szVia[] = { 'V','i','a',0 };
139 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
140 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
141
142 #define MAXHOSTNAME 100
143 #define MAX_FIELD_VALUE_LEN 256
144 #define MAX_FIELD_LEN 256
145
146 #define HTTP_REFERER szReferer
147 #define HTTP_ACCEPT szAccept
148 #define HTTP_USERAGENT szUser_Agent
149
150 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
151 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
152 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
153 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
154 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
155 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
156 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
157
158 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
159
160 struct HttpAuthInfo
161 {
162 LPWSTR scheme;
163 CredHandle cred;
164 CtxtHandle ctx;
165 TimeStamp exp;
166 ULONG attr;
167 ULONG max_token;
168 void *auth_data;
169 unsigned int auth_data_len;
170 BOOL finished; /* finished authenticating */
171 };
172
173
174 struct gzip_stream_t {
175 #ifdef HAVE_ZLIB
176 z_stream zstream;
177 #endif
178 BYTE buf[8192];
179 DWORD buf_size;
180 DWORD buf_pos;
181 BOOL end_of_data;
182 };
183
184 typedef struct _authorizationData
185 {
186 struct list entry;
187
188 LPWSTR lpszwHost;
189 LPWSTR lpszwRealm;
190 LPSTR lpszAuthorization;
191 UINT AuthorizationLen;
192 } authorizationData;
193
194 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
195
196 static CRITICAL_SECTION authcache_cs;
197 static CRITICAL_SECTION_DEBUG critsect_debug =
198 {
199 0, 0, &authcache_cs,
200 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
201 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
202 };
203 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
204
205 static DWORD HTTP_OpenConnection(http_request_t *req);
206 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
207 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
208 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
209 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
210 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
211 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
212 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
213 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
214 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
215 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
216 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
217 static void HTTP_DrainContent(http_request_t *req);
218 static BOOL HTTP_FinishedReading(http_request_t *req);
219
220 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
221 {
222 int HeaderIndex = 0;
223 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
224 if (HeaderIndex == -1)
225 return NULL;
226 else
227 return &req->pCustHeaders[HeaderIndex];
228 }
229
230 #ifdef HAVE_ZLIB
231
232 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
233 {
234 return HeapAlloc(GetProcessHeap(), 0, items*size);
235 }
236
237 static void wininet_zfree(voidpf opaque, voidpf address)
238 {
239 HeapFree(GetProcessHeap(), 0, address);
240 }
241
242 static void init_gzip_stream(http_request_t *req)
243 {
244 gzip_stream_t *gzip_stream;
245 int index, zres;
246
247 gzip_stream = HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t));
248 gzip_stream->zstream.zalloc = wininet_zalloc;
249 gzip_stream->zstream.zfree = wininet_zfree;
250 gzip_stream->zstream.opaque = NULL;
251 gzip_stream->zstream.next_in = NULL;
252 gzip_stream->zstream.avail_in = 0;
253 gzip_stream->zstream.next_out = NULL;
254 gzip_stream->zstream.avail_out = 0;
255 gzip_stream->buf_pos = 0;
256 gzip_stream->buf_size = 0;
257 gzip_stream->end_of_data = FALSE;
258
259 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
260 if(zres != Z_OK) {
261 ERR("inflateInit failed: %d\n", zres);
262 HeapFree(GetProcessHeap(), 0, gzip_stream);
263 return;
264 }
265
266 req->gzip_stream = gzip_stream;
267
268 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
269 if(index != -1)
270 HTTP_DeleteCustomHeader(req, index);
271 }
272
273 #else
274
275 static void init_gzip_stream(http_request_t *req)
276 {
277 ERR("gzip stream not supported, missing zlib.\n");
278 }
279
280 #endif
281
282 /* set the request content length based on the headers */
283 static DWORD set_content_length( http_request_t *lpwhr )
284 {
285 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
286 WCHAR encoding[20];
287 DWORD size;
288
289 size = sizeof(lpwhr->dwContentLength);
290 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
291 &lpwhr->dwContentLength, &size, NULL) != ERROR_SUCCESS)
292 lpwhr->dwContentLength = ~0u;
293
294 size = sizeof(encoding);
295 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
296 !strcmpiW(encoding, szChunked))
297 {
298 lpwhr->dwContentLength = ~0u;
299 lpwhr->read_chunked = TRUE;
300 }
301
302 if(lpwhr->decoding) {
303 int encoding_idx;
304
305 static const WCHAR gzipW[] = {'g','z','i','p',0};
306
307 encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
308 if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
309 init_gzip_stream(lpwhr);
310 }
311
312 return lpwhr->dwContentLength;
313 }
314
315 /***********************************************************************
316 * HTTP_Tokenize (internal)
317 *
318 * Tokenize a string, allocating memory for the tokens.
319 */
320 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
321 {
322 LPWSTR * token_array;
323 int tokens = 0;
324 int i;
325 LPCWSTR next_token;
326
327 if (string)
328 {
329 /* empty string has no tokens */
330 if (*string)
331 tokens++;
332 /* count tokens */
333 for (i = 0; string[i]; i++)
334 {
335 if (!strncmpW(string+i, token_string, strlenW(token_string)))
336 {
337 DWORD j;
338 tokens++;
339 /* we want to skip over separators, but not the null terminator */
340 for (j = 0; j < strlenW(token_string) - 1; j++)
341 if (!string[i+j])
342 break;
343 i += j;
344 }
345 }
346 }
347
348 /* add 1 for terminating NULL */
349 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
350 token_array[tokens] = NULL;
351 if (!tokens)
352 return token_array;
353 for (i = 0; i < tokens; i++)
354 {
355 int len;
356 next_token = strstrW(string, token_string);
357 if (!next_token) next_token = string+strlenW(string);
358 len = next_token - string;
359 token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
360 memcpy(token_array[i], string, len*sizeof(WCHAR));
361 token_array[i][len] = '\0';
362 string = next_token+strlenW(token_string);
363 }
364 return token_array;
365 }
366
367 /***********************************************************************
368 * HTTP_FreeTokens (internal)
369 *
370 * Frees memory returned from HTTP_Tokenize.
371 */
372 static void HTTP_FreeTokens(LPWSTR * token_array)
373 {
374 int i;
375 for (i = 0; token_array[i]; i++)
376 HeapFree(GetProcessHeap(), 0, token_array[i]);
377 HeapFree(GetProcessHeap(), 0, token_array);
378 }
379
380 static void HTTP_FixURL(http_request_t *lpwhr)
381 {
382 static const WCHAR szSlash[] = { '/',0 };
383 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
384
385 /* If we don't have a path we set it to root */
386 if (NULL == lpwhr->lpszPath)
387 lpwhr->lpszPath = heap_strdupW(szSlash);
388 else /* remove \r and \n*/
389 {
390 int nLen = strlenW(lpwhr->lpszPath);
391 while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
392 {
393 nLen--;
394 lpwhr->lpszPath[nLen]='\0';
395 }
396 /* Replace '\' with '/' */
397 while (nLen>0) {
398 nLen--;
399 if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
400 }
401 }
402
403 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
404 lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
405 && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
406 {
407 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
408 (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
409 *fixurl = '/';
410 strcpyW(fixurl + 1, lpwhr->lpszPath);
411 HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
412 lpwhr->lpszPath = fixurl;
413 }
414 }
415
416 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
417 {
418 LPWSTR requestString;
419 DWORD len, n;
420 LPCWSTR *req;
421 UINT i;
422 LPWSTR p;
423
424 static const WCHAR szSpace[] = { ' ',0 };
425 static const WCHAR szColon[] = { ':',' ',0 };
426 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
427
428 /* allocate space for an array of all the string pointers to be added */
429 len = (lpwhr->nCustHeaders)*4 + 10;
430 req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
431
432 /* add the verb, path and HTTP version string */
433 n = 0;
434 req[n++] = verb;
435 req[n++] = szSpace;
436 req[n++] = path;
437 req[n++] = szSpace;
438 req[n++] = version;
439
440 /* Append custom request headers */
441 for (i = 0; i < lpwhr->nCustHeaders; i++)
442 {
443 if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
444 {
445 req[n++] = szCrLf;
446 req[n++] = lpwhr->pCustHeaders[i].lpszField;
447 req[n++] = szColon;
448 req[n++] = lpwhr->pCustHeaders[i].lpszValue;
449
450 TRACE("Adding custom header %s (%s)\n",
451 debugstr_w(lpwhr->pCustHeaders[i].lpszField),
452 debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
453 }
454 }
455
456 if( n >= len )
457 ERR("oops. buffer overrun\n");
458
459 req[n] = NULL;
460 requestString = HTTP_build_req( req, 4 );
461 HeapFree( GetProcessHeap(), 0, req );
462
463 /*
464 * Set (header) termination string for request
465 * Make sure there's exactly two new lines at the end of the request
466 */
467 p = &requestString[strlenW(requestString)-1];
468 while ( (*p == '\n') || (*p == '\r') )
469 p--;
470 strcpyW( p+1, sztwocrlf );
471
472 return requestString;
473 }
474
475 static void HTTP_ProcessCookies( http_request_t *lpwhr )
476 {
477 int HeaderIndex;
478 int numCookies = 0;
479 LPHTTPHEADERW setCookieHeader;
480
481 while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
482 {
483 setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
484
485 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
486 {
487 int len;
488 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
489 LPWSTR buf_url;
490 LPHTTPHEADERW Host;
491
492 Host = HTTP_GetHeader(lpwhr, hostW);
493 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
494 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
495 sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
496 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
497
498 HeapFree(GetProcessHeap(), 0, buf_url);
499 }
500 numCookies++;
501 }
502 }
503
504 static void strip_spaces(LPWSTR start)
505 {
506 LPWSTR str = start;
507 LPWSTR end;
508
509 while (*str == ' ' && *str != '\0')
510 str++;
511
512 if (str != start)
513 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
514
515 end = start + strlenW(start) - 1;
516 while (end >= start && *end == ' ')
517 {
518 *end = '\0';
519 end--;
520 }
521 }
522
523 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
524 {
525 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
526 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
527 BOOL is_basic;
528 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
529 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
530 if (is_basic && pszRealm)
531 {
532 LPCWSTR token;
533 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
534 LPCWSTR realm;
535 ptr++;
536 *pszRealm=NULL;
537 token = strchrW(ptr,'=');
538 if (!token)
539 return TRUE;
540 realm = ptr;
541 while (*realm == ' ' && *realm != '\0')
542 realm++;
543 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
544 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
545 {
546 token++;
547 while (*token == ' ' && *token != '\0')
548 token++;
549 if (*token == '\0')
550 return TRUE;
551 *pszRealm = heap_strdupW(token);
552 strip_spaces(*pszRealm);
553 }
554 }
555
556 return is_basic;
557 }
558
559 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
560 {
561 if (!authinfo) return;
562
563 if (SecIsValidHandle(&authinfo->ctx))
564 DeleteSecurityContext(&authinfo->ctx);
565 if (SecIsValidHandle(&authinfo->cred))
566 FreeCredentialsHandle(&authinfo->cred);
567
568 HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
569 HeapFree(GetProcessHeap(), 0, authinfo->scheme);
570 HeapFree(GetProcessHeap(), 0, authinfo);
571 }
572
573 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
574 {
575 authorizationData *ad;
576 UINT rc = 0;
577
578 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
579
580 EnterCriticalSection(&authcache_cs);
581 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, authorizationData, entry)
582 {
583 if (!strcmpiW(host,ad->lpszwHost) && !strcmpW(realm,ad->lpszwRealm))
584 {
585 TRACE("Authorization found in cache\n");
586 *auth_data = HeapAlloc(GetProcessHeap(),0,ad->AuthorizationLen);
587 memcpy(*auth_data,ad->lpszAuthorization,ad->AuthorizationLen);
588 rc = ad->AuthorizationLen;
589 break;
590 }
591 }
592 LeaveCriticalSection(&authcache_cs);
593 return rc;
594 }
595
596 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
597 {
598 struct list *cursor;
599 authorizationData* ad = NULL;
600
601 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
602
603 EnterCriticalSection(&authcache_cs);
604 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
605 {
606 authorizationData *check = LIST_ENTRY(cursor,authorizationData,entry);
607 if (!strcmpiW(host,check->lpszwHost) && !strcmpW(realm,check->lpszwRealm))
608 {
609 ad = check;
610 break;
611 }
612 }
613
614 if (ad)
615 {
616 TRACE("Found match in cache, replacing\n");
617 HeapFree(GetProcessHeap(),0,ad->lpszAuthorization);
618 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
619 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
620 ad->AuthorizationLen = auth_data_len;
621 }
622 else
623 {
624 ad = HeapAlloc(GetProcessHeap(),0,sizeof(authorizationData));
625 ad->lpszwHost = heap_strdupW(host);
626 ad->lpszwRealm = heap_strdupW(realm);
627 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
628 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
629 ad->AuthorizationLen = auth_data_len;
630 list_add_head(&basicAuthorizationCache,&ad->entry);
631 TRACE("authorization cached\n");
632 }
633 LeaveCriticalSection(&authcache_cs);
634 }
635
636 static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
637 struct HttpAuthInfo **ppAuthInfo,
638 LPWSTR domain_and_username, LPWSTR password,
639 LPWSTR host )
640 {
641 SECURITY_STATUS sec_status;
642 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
643 BOOL first = FALSE;
644 LPWSTR szRealm = NULL;
645
646 TRACE("%s\n", debugstr_w(pszAuthValue));
647
648 if (!pAuthInfo)
649 {
650 TimeStamp exp;
651
652 first = TRUE;
653 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
654 if (!pAuthInfo)
655 return FALSE;
656
657 SecInvalidateHandle(&pAuthInfo->cred);
658 SecInvalidateHandle(&pAuthInfo->ctx);
659 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
660 pAuthInfo->attr = 0;
661 pAuthInfo->auth_data = NULL;
662 pAuthInfo->auth_data_len = 0;
663 pAuthInfo->finished = FALSE;
664
665 if (is_basic_auth_value(pszAuthValue,NULL))
666 {
667 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
668 pAuthInfo->scheme = heap_strdupW(szBasic);
669 if (!pAuthInfo->scheme)
670 {
671 HeapFree(GetProcessHeap(), 0, pAuthInfo);
672 return FALSE;
673 }
674 }
675 else
676 {
677 PVOID pAuthData;
678 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
679
680 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
681 if (!pAuthInfo->scheme)
682 {
683 HeapFree(GetProcessHeap(), 0, pAuthInfo);
684 return FALSE;
685 }
686
687 if (domain_and_username)
688 {
689 WCHAR *user = strchrW(domain_and_username, '\\');
690 WCHAR *domain = domain_and_username;
691
692 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
693
694 pAuthData = &nt_auth_identity;
695
696 if (user) user++;
697 else
698 {
699 user = domain_and_username;
700 domain = NULL;
701 }
702
703 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
704 nt_auth_identity.User = user;
705 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
706 nt_auth_identity.Domain = domain;
707 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
708 nt_auth_identity.Password = password;
709 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
710 }
711 else
712 /* use default credentials */
713 pAuthData = NULL;
714
715 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
716 SECPKG_CRED_OUTBOUND, NULL,
717 pAuthData, NULL,
718 NULL, &pAuthInfo->cred,
719 &exp);
720 if (sec_status == SEC_E_OK)
721 {
722 PSecPkgInfoW sec_pkg_info;
723 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
724 if (sec_status == SEC_E_OK)
725 {
726 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
727 FreeContextBuffer(sec_pkg_info);
728 }
729 }
730 if (sec_status != SEC_E_OK)
731 {
732 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
733 debugstr_w(pAuthInfo->scheme), sec_status);
734 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
735 HeapFree(GetProcessHeap(), 0, pAuthInfo);
736 return FALSE;
737 }
738 }
739 *ppAuthInfo = pAuthInfo;
740 }
741 else if (pAuthInfo->finished)
742 return FALSE;
743
744 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
745 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
746 {
747 ERR("authentication scheme changed from %s to %s\n",
748 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
749 return FALSE;
750 }
751
752 if (is_basic_auth_value(pszAuthValue,&szRealm))
753 {
754 int userlen;
755 int passlen;
756 char *auth_data = NULL;
757 UINT auth_data_len = 0;
758
759 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
760
761 if (!domain_and_username)
762 {
763 if (host && szRealm)
764 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
765 if (auth_data_len == 0)
766 {
767 HeapFree(GetProcessHeap(),0,szRealm);
768 return FALSE;
769 }
770 }
771 else
772 {
773 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
774 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
775
776 /* length includes a nul terminator, which will be re-used for the ':' */
777 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
778 if (!auth_data)
779 {
780 HeapFree(GetProcessHeap(),0,szRealm);
781 return FALSE;
782 }
783
784 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
785 auth_data[userlen] = ':';
786 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
787 auth_data_len = userlen + 1 + passlen;
788 if (host && szRealm)
789 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
790 }
791
792 pAuthInfo->auth_data = auth_data;
793 pAuthInfo->auth_data_len = auth_data_len;
794 pAuthInfo->finished = TRUE;
795 HeapFree(GetProcessHeap(),0,szRealm);
796
797 return TRUE;
798 }
799 else
800 {
801 LPCWSTR pszAuthData;
802 SecBufferDesc out_desc, in_desc;
803 SecBuffer out, in;
804 unsigned char *buffer;
805 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
806 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
807
808 in.BufferType = SECBUFFER_TOKEN;
809 in.cbBuffer = 0;
810 in.pvBuffer = NULL;
811
812 in_desc.ulVersion = 0;
813 in_desc.cBuffers = 1;
814 in_desc.pBuffers = &in;
815
816 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
817 if (*pszAuthData == ' ')
818 {
819 pszAuthData++;
820 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
821 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
822 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
823 }
824
825 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
826
827 out.BufferType = SECBUFFER_TOKEN;
828 out.cbBuffer = pAuthInfo->max_token;
829 out.pvBuffer = buffer;
830
831 out_desc.ulVersion = 0;
832 out_desc.cBuffers = 1;
833 out_desc.pBuffers = &out;
834
835 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
836 first ? NULL : &pAuthInfo->ctx,
837 first ? lpwhr->lpHttpSession->lpszServerName : NULL,
838 context_req, 0, SECURITY_NETWORK_DREP,
839 in.pvBuffer ? &in_desc : NULL,
840 0, &pAuthInfo->ctx, &out_desc,
841 &pAuthInfo->attr, &pAuthInfo->exp);
842 if (sec_status == SEC_E_OK)
843 {
844 pAuthInfo->finished = TRUE;
845 pAuthInfo->auth_data = out.pvBuffer;
846 pAuthInfo->auth_data_len = out.cbBuffer;
847 TRACE("sending last auth packet\n");
848 }
849 else if (sec_status == SEC_I_CONTINUE_NEEDED)
850 {
851 pAuthInfo->auth_data = out.pvBuffer;
852 pAuthInfo->auth_data_len = out.cbBuffer;
853 TRACE("sending next auth packet\n");
854 }
855 else
856 {
857 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
858 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
859 destroy_authinfo(pAuthInfo);
860 *ppAuthInfo = NULL;
861 return FALSE;
862 }
863 }
864
865 return TRUE;
866 }
867
868 /***********************************************************************
869 * HTTP_HttpAddRequestHeadersW (internal)
870 */
871 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
872 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
873 {
874 LPWSTR lpszStart;
875 LPWSTR lpszEnd;
876 LPWSTR buffer;
877 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
878
879 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
880
881 if( dwHeaderLength == ~0U )
882 len = strlenW(lpszHeader);
883 else
884 len = dwHeaderLength;
885 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
886 lstrcpynW( buffer, lpszHeader, len + 1);
887
888 lpszStart = buffer;
889
890 do
891 {
892 LPWSTR * pFieldAndValue;
893
894 lpszEnd = lpszStart;
895
896 while (*lpszEnd != '\0')
897 {
898 if (*lpszEnd == '\r' || *lpszEnd == '\n')
899 break;
900 lpszEnd++;
901 }
902
903 if (*lpszStart == '\0')
904 break;
905
906 if (*lpszEnd == '\r' || *lpszEnd == '\n')
907 {
908 *lpszEnd = '\0';
909 lpszEnd++; /* Jump over newline */
910 }
911 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
912 if (*lpszStart == '\0')
913 {
914 /* Skip 0-length headers */
915 lpszStart = lpszEnd;
916 res = ERROR_SUCCESS;
917 continue;
918 }
919 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
920 if (pFieldAndValue)
921 {
922 res = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
923 if (res == ERROR_SUCCESS)
924 res = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
925 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
926 HTTP_FreeTokens(pFieldAndValue);
927 }
928
929 lpszStart = lpszEnd;
930 } while (res == ERROR_SUCCESS);
931
932 HeapFree(GetProcessHeap(), 0, buffer);
933
934 return res;
935 }
936
937 /***********************************************************************
938 * HttpAddRequestHeadersW (WININET.@)
939 *
940 * Adds one or more HTTP header to the request handler
941 *
942 * NOTE
943 * On Windows if dwHeaderLength includes the trailing '\0', then
944 * HttpAddRequestHeadersW() adds it too. However this results in an
945 * invalid Http header which is rejected by some servers so we probably
946 * don't need to match Windows on that point.
947 *
948 * RETURNS
949 * TRUE on success
950 * FALSE on failure
951 *
952 */
953 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
954 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
955 {
956 http_request_t *lpwhr;
957 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
958
959 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
960
961 if (!lpszHeader)
962 return TRUE;
963
964 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
965 if (lpwhr && lpwhr->hdr.htype == WH_HHTTPREQ)
966 res = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
967 if( lpwhr )
968 WININET_Release( &lpwhr->hdr );
969
970 if(res != ERROR_SUCCESS)
971 SetLastError(res);
972 return res == ERROR_SUCCESS;
973 }
974
975 /***********************************************************************
976 * HttpAddRequestHeadersA (WININET.@)
977 *
978 * Adds one or more HTTP header to the request handler
979 *
980 * RETURNS
981 * TRUE on success
982 * FALSE on failure
983 *
984 */
985 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
986 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
987 {
988 DWORD len;
989 LPWSTR hdr;
990 BOOL r;
991
992 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
993
994 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
995 hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
996 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
997 if( dwHeaderLength != ~0U )
998 dwHeaderLength = len;
999
1000 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1001
1002 HeapFree( GetProcessHeap(), 0, hdr );
1003
1004 return r;
1005 }
1006
1007 /***********************************************************************
1008 * HttpOpenRequestA (WININET.@)
1009 *
1010 * Open a HTTP request handle
1011 *
1012 * RETURNS
1013 * HINTERNET a HTTP request handle on success
1014 * NULL on failure
1015 *
1016 */
1017 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1018 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1019 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1020 DWORD dwFlags, DWORD_PTR dwContext)
1021 {
1022 LPWSTR szVerb = NULL, szObjectName = NULL;
1023 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1024 INT acceptTypesCount;
1025 HINTERNET rc = FALSE;
1026 LPCSTR *types;
1027
1028 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1029 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1030 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1031 dwFlags, dwContext);
1032
1033 if (lpszVerb)
1034 {
1035 szVerb = heap_strdupAtoW(lpszVerb);
1036 if ( !szVerb )
1037 goto end;
1038 }
1039
1040 if (lpszObjectName)
1041 {
1042 szObjectName = heap_strdupAtoW(lpszObjectName);
1043 if ( !szObjectName )
1044 goto end;
1045 }
1046
1047 if (lpszVersion)
1048 {
1049 szVersion = heap_strdupAtoW(lpszVersion);
1050 if ( !szVersion )
1051 goto end;
1052 }
1053
1054 if (lpszReferrer)
1055 {
1056 szReferrer = heap_strdupAtoW(lpszReferrer);
1057 if ( !szReferrer )
1058 goto end;
1059 }
1060
1061 if (lpszAcceptTypes)
1062 {
1063 acceptTypesCount = 0;
1064 types = lpszAcceptTypes;
1065 while (*types)
1066 {
1067 __TRY
1068 {
1069 /* find out how many there are */
1070 if (*types && **types)
1071 {
1072 TRACE("accept type: %s\n", debugstr_a(*types));
1073 acceptTypesCount++;
1074 }
1075 }
1076 __EXCEPT_PAGE_FAULT
1077 {
1078 WARN("invalid accept type pointer\n");
1079 }
1080 __ENDTRY;
1081 types++;
1082 }
1083 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1084 if (!szAcceptTypes) goto end;
1085
1086 acceptTypesCount = 0;
1087 types = lpszAcceptTypes;
1088 while (*types)
1089 {
1090 __TRY
1091 {
1092 if (*types && **types)
1093 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1094 }
1095 __EXCEPT_PAGE_FAULT
1096 {
1097 /* ignore invalid pointer */
1098 }
1099 __ENDTRY;
1100 types++;
1101 }
1102 szAcceptTypes[acceptTypesCount] = NULL;
1103 }
1104
1105 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1106 szVersion, szReferrer,
1107 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1108
1109 end:
1110 if (szAcceptTypes)
1111 {
1112 acceptTypesCount = 0;
1113 while (szAcceptTypes[acceptTypesCount])
1114 {
1115 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1116 acceptTypesCount++;
1117 }
1118 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1119 }
1120 HeapFree(GetProcessHeap(), 0, szReferrer);
1121 HeapFree(GetProcessHeap(), 0, szVersion);
1122 HeapFree(GetProcessHeap(), 0, szObjectName);
1123 HeapFree(GetProcessHeap(), 0, szVerb);
1124
1125 return rc;
1126 }
1127
1128 /***********************************************************************
1129 * HTTP_EncodeBase64
1130 */
1131 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1132 {
1133 UINT n = 0, x;
1134 static const CHAR HTTP_Base64Enc[] =
1135 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1136
1137 while( len > 0 )
1138 {
1139 /* first 6 bits, all from bin[0] */
1140 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1141 x = (bin[0] & 3) << 4;
1142
1143 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1144 if( len == 1 )
1145 {
1146 base64[n++] = HTTP_Base64Enc[x];
1147 base64[n++] = '=';
1148 base64[n++] = '=';
1149 break;
1150 }
1151 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1152 x = ( bin[1] & 0x0f ) << 2;
1153
1154 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1155 if( len == 2 )
1156 {
1157 base64[n++] = HTTP_Base64Enc[x];
1158 base64[n++] = '=';
1159 break;
1160 }
1161 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1162
1163 /* last 6 bits, all from bin [2] */
1164 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1165 bin += 3;
1166 len -= 3;
1167 }
1168 base64[n] = 0;
1169 return n;
1170 }
1171
1172 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1173 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1174 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1175 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1176 static const signed char HTTP_Base64Dec[256] =
1177 {
1178 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1179 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1180 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1181 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1182 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1183 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1184 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1185 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1186 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1187 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1188 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1189 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1190 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1191 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1192 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1193 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1194 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1195 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1196 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1197 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1198 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1199 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1200 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1201 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1202 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1203 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1204 };
1205 #undef CH
1206
1207 /***********************************************************************
1208 * HTTP_DecodeBase64
1209 */
1210 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1211 {
1212 unsigned int n = 0;
1213
1214 while(*base64)
1215 {
1216 signed char in[4];
1217
1218 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1219 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1220 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1221 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1222 {
1223 WARN("invalid base64: %s\n", debugstr_w(base64));
1224 return 0;
1225 }
1226 if (bin)
1227 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1228 n++;
1229
1230 if ((base64[2] == '=') && (base64[3] == '='))
1231 break;
1232 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1233 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1234 {
1235 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1236 return 0;
1237 }
1238 if (bin)
1239 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1240 n++;
1241
1242 if (base64[3] == '=')
1243 break;
1244 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1245 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1246 {
1247 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1248 return 0;
1249 }
1250 if (bin)
1251 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1252 n++;
1253
1254 base64 += 4;
1255 }
1256
1257 return n;
1258 }
1259
1260 /***********************************************************************
1261 * HTTP_InsertAuthorization
1262 *
1263 * Insert or delete the authorization field in the request header.
1264 */
1265 static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1266 {
1267 if (pAuthInfo)
1268 {
1269 static const WCHAR wszSpace[] = {' ',0};
1270 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1271 unsigned int len;
1272 WCHAR *authorization = NULL;
1273
1274 if (pAuthInfo->auth_data_len)
1275 {
1276 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1277 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1278 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1279 if (!authorization)
1280 return FALSE;
1281
1282 strcpyW(authorization, pAuthInfo->scheme);
1283 strcatW(authorization, wszSpace);
1284 HTTP_EncodeBase64(pAuthInfo->auth_data,
1285 pAuthInfo->auth_data_len,
1286 authorization+strlenW(authorization));
1287
1288 /* clear the data as it isn't valid now that it has been sent to the
1289 * server, unless it's Basic authentication which doesn't do
1290 * connection tracking */
1291 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1292 {
1293 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1294 pAuthInfo->auth_data = NULL;
1295 pAuthInfo->auth_data_len = 0;
1296 }
1297 }
1298
1299 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1300
1301 HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1302
1303 HeapFree(GetProcessHeap(), 0, authorization);
1304 }
1305 return TRUE;
1306 }
1307
1308 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1309 {
1310 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1311 DWORD size;
1312
1313 size = sizeof(new_location);
1314 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1315 {
1316 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1317 strcpyW( url, new_location );
1318 }
1319 else
1320 {
1321 static const WCHAR slash[] = { '/',0 };
1322 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1323 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1324 http_session_t *session = req->lpHttpSession;
1325
1326 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1327 size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
1328
1329 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1330
1331 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1332 sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
1333 else
1334 sprintfW( url, format, session->lpszHostName, session->nHostPort );
1335 if (req->lpszPath[0] != '/') strcatW( url, slash );
1336 strcatW( url, req->lpszPath );
1337 }
1338 TRACE("url=%s\n", debugstr_w(url));
1339 return url;
1340 }
1341
1342 /***********************************************************************
1343 * HTTP_DealWithProxy
1344 */
1345 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
1346 {
1347 WCHAR buf[MAXHOSTNAME];
1348 WCHAR protoProxy[MAXHOSTNAME + 15];
1349 DWORD protoProxyLen = sizeof(protoProxy) / sizeof(protoProxy[0]);
1350 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1351 static WCHAR szNul[] = { 0 };
1352 URL_COMPONENTSW UrlComponents;
1353 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1354 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1355 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1356
1357 memset( &UrlComponents, 0, sizeof UrlComponents );
1358 UrlComponents.dwStructSize = sizeof UrlComponents;
1359 UrlComponents.lpszHostName = buf;
1360 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1361
1362 if (!INTERNET_FindProxyForProtocol(hIC->lpszProxy, protoHttp, protoProxy, &protoProxyLen))
1363 return FALSE;
1364 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1365 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1366 sprintfW(proxy, szFormat, protoProxy);
1367 else
1368 strcpyW(proxy, protoProxy);
1369 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1370 return FALSE;
1371 if( UrlComponents.dwHostNameLength == 0 )
1372 return FALSE;
1373
1374 if( !lpwhr->lpszPath )
1375 lpwhr->lpszPath = szNul;
1376
1377 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1378 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1379
1380 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1381 lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1382 lpwhs->nServerPort = UrlComponents.nPort;
1383
1384 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1385 return TRUE;
1386 }
1387
1388 #ifndef INET6_ADDRSTRLEN
1389 #define INET6_ADDRSTRLEN 46
1390 #endif
1391
1392 static DWORD HTTP_ResolveName(http_request_t *lpwhr)
1393 {
1394 char szaddr[INET6_ADDRSTRLEN];
1395 http_session_t *lpwhs = lpwhr->lpHttpSession;
1396 const void *addr;
1397
1398 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1399 INTERNET_STATUS_RESOLVING_NAME,
1400 lpwhs->lpszServerName,
1401 (strlenW(lpwhs->lpszServerName)+1) * sizeof(WCHAR));
1402
1403 lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1404 if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1405 (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1406 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1407
1408 switch (lpwhs->socketAddress.ss_family)
1409 {
1410 case AF_INET:
1411 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1412 break;
1413 case AF_INET6:
1414 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1415 break;
1416 default:
1417 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1418 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1419 }
1420 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1421 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1422 INTERNET_STATUS_NAME_RESOLVED,
1423 szaddr, strlen(szaddr)+1);
1424
1425 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1426 return ERROR_SUCCESS;
1427 }
1428
1429
1430 /***********************************************************************
1431 * HTTPREQ_Destroy (internal)
1432 *
1433 * Deallocate request handle
1434 *
1435 */
1436 static void HTTPREQ_Destroy(object_header_t *hdr)
1437 {
1438 http_request_t *lpwhr = (http_request_t*) hdr;
1439 DWORD i;
1440
1441 TRACE("\n");
1442
1443 if(lpwhr->hCacheFile)
1444 CloseHandle(lpwhr->hCacheFile);
1445
1446 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1447
1448 DeleteCriticalSection( &lpwhr->read_section );
1449 WININET_Release(&lpwhr->lpHttpSession->hdr);
1450
1451 destroy_authinfo(lpwhr->pAuthInfo);
1452 destroy_authinfo(lpwhr->pProxyAuthInfo);
1453
1454 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1455 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1456 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1457 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1458 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1459
1460 for (i = 0; i < lpwhr->nCustHeaders; i++)
1461 {
1462 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1463 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1464 }
1465
1466 #ifdef HAVE_ZLIB
1467 if(lpwhr->gzip_stream) {
1468 if(!lpwhr->gzip_stream->end_of_data)
1469 inflateEnd(&lpwhr->gzip_stream->zstream);
1470 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1471 }
1472 #endif
1473
1474 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1475 HeapFree(GetProcessHeap(), 0, lpwhr);
1476 }
1477
1478 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1479 {
1480 http_request_t *lpwhr = (http_request_t*) hdr;
1481
1482 TRACE("%p\n",lpwhr);
1483
1484 if (!NETCON_connected(&lpwhr->netConnection))
1485 return;
1486
1487 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1488 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1489
1490 NETCON_close(&lpwhr->netConnection);
1491
1492 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1493 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1494 }
1495
1496 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1497 {
1498 LPHTTPHEADERW host_header;
1499
1500 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1501
1502 host_header = HTTP_GetHeader(req, hostW);
1503 if(!host_header)
1504 return FALSE;
1505
1506 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1507 return TRUE;
1508 }
1509
1510 static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
1511 {
1512 WCHAR szVersion[10];
1513 WCHAR szConnectionResponse[20];
1514 DWORD dwBufferSize = sizeof(szVersion);
1515 BOOL keepalive = FALSE;
1516
1517 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1518 * the connection is keep-alive by default */
1519 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1520 && !strcmpiW(szVersion, g_szHttp1_1))
1521 {
1522 keepalive = TRUE;
1523 }
1524
1525 dwBufferSize = sizeof(szConnectionResponse);
1526 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1527 || HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1528 {
1529 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1530 }
1531
1532 return keepalive;
1533 }
1534
1535 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1536 {
1537 http_request_t *req = (http_request_t*)hdr;
1538
1539 switch(option) {
1540 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1541 {
1542 http_session_t *lpwhs = req->lpHttpSession;
1543 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1544
1545 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1546
1547 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1548 return ERROR_INSUFFICIENT_BUFFER;
1549 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1550 /* FIXME: can't get a SOCKET from our connection since we don't use
1551 * winsock
1552 */
1553 info->Socket = 0;
1554 /* FIXME: get source port from req->netConnection */
1555 info->SourcePort = 0;
1556 info->DestPort = lpwhs->nHostPort;
1557 info->Flags = 0;
1558 if (HTTP_KeepAlive(req))
1559 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1560 if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
1561 info->Flags |= IDSI_FLAG_PROXY;
1562 if (req->netConnection.useSSL)
1563 info->Flags |= IDSI_FLAG_SECURE;
1564
1565 return ERROR_SUCCESS;
1566 }
1567
1568 case INTERNET_OPTION_SECURITY_FLAGS:
1569 {
1570 http_session_t *lpwhs;
1571 lpwhs = req->lpHttpSession;
1572
1573 if (*size < sizeof(ULONG))
1574 return ERROR_INSUFFICIENT_BUFFER;
1575
1576 *size = sizeof(DWORD);
1577 if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1578 *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1579 else
1580 *(DWORD*)buffer = 0;
1581 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1582 return ERROR_SUCCESS;
1583 }
1584
1585 case INTERNET_OPTION_HANDLE_TYPE:
1586 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1587
1588 if (*size < sizeof(ULONG))
1589 return ERROR_INSUFFICIENT_BUFFER;
1590
1591 *size = sizeof(DWORD);
1592 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1593 return ERROR_SUCCESS;
1594
1595 case INTERNET_OPTION_URL: {
1596 WCHAR url[INTERNET_MAX_URL_LENGTH];
1597 HTTPHEADERW *host;
1598 DWORD len;
1599 WCHAR *pch;
1600
1601 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1602
1603 TRACE("INTERNET_OPTION_URL\n");
1604
1605 host = HTTP_GetHeader(req, hostW);
1606 strcpyW(url, httpW);
1607 strcatW(url, host->lpszValue);
1608 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1609 *pch = 0;
1610 strcatW(url, req->lpszPath);
1611
1612 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1613
1614 if(unicode) {
1615 len = (strlenW(url)+1) * sizeof(WCHAR);
1616 if(*size < len)
1617 return ERROR_INSUFFICIENT_BUFFER;
1618
1619 *size = len;
1620 strcpyW(buffer, url);
1621 return ERROR_SUCCESS;
1622 }else {
1623 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1624 if(len > *size)
1625 return ERROR_INSUFFICIENT_BUFFER;
1626
1627 *size = len;
1628 return ERROR_SUCCESS;
1629 }
1630 }
1631
1632 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1633 INTERNET_CACHE_ENTRY_INFOW *info;
1634 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1635 WCHAR url[INTERNET_MAX_URL_LENGTH];
1636 DWORD nbytes, error;
1637 BOOL ret;
1638
1639 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1640
1641 if (*size < sizeof(*ts))
1642 {
1643 *size = sizeof(*ts);
1644 return ERROR_INSUFFICIENT_BUFFER;
1645 }
1646 nbytes = 0;
1647 HTTP_GetRequestURL(req, url);
1648 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1649 error = GetLastError();
1650 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1651 {
1652 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1653 return ERROR_OUTOFMEMORY;
1654
1655 GetUrlCacheEntryInfoW(url, info, &nbytes);
1656
1657 ts->ftExpires = info->ExpireTime;
1658 ts->ftLastModified = info->LastModifiedTime;
1659
1660 HeapFree(GetProcessHeap(), 0, info);
1661 *size = sizeof(*ts);
1662 return ERROR_SUCCESS;
1663 }
1664 return error;
1665 }
1666
1667 case INTERNET_OPTION_DATAFILE_NAME: {
1668 DWORD req_size;
1669
1670 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1671
1672 if(!req->lpszCacheFile) {
1673 *size = 0;
1674 return ERROR_INTERNET_ITEM_NOT_FOUND;
1675 }
1676
1677 if(unicode) {
1678 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1679 if(*size < req_size)
1680 return ERROR_INSUFFICIENT_BUFFER;
1681
1682 *size = req_size;
1683 memcpy(buffer, req->lpszCacheFile, *size);
1684 return ERROR_SUCCESS;
1685 }else {
1686 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1687 if (req_size > *size)
1688 return ERROR_INSUFFICIENT_BUFFER;
1689
1690 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1691 -1, buffer, *size, NULL, NULL);
1692 return ERROR_SUCCESS;
1693 }
1694 }
1695
1696 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1697 PCCERT_CONTEXT context;
1698
1699 if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1700 *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1701 return ERROR_INSUFFICIENT_BUFFER;
1702 }
1703
1704 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1705 if(context) {
1706 INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1707 DWORD len;
1708
1709 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1710 info->ftExpiry = context->pCertInfo->NotAfter;
1711 info->ftStart = context->pCertInfo->NotBefore;
1712 if(unicode) {
1713 len = CertNameToStrW(context->dwCertEncodingType,
1714 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1715 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1716 if(info->lpszSubjectInfo)
1717 CertNameToStrW(context->dwCertEncodingType,
1718 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1719 info->lpszSubjectInfo, len);
1720 len = CertNameToStrW(context->dwCertEncodingType,
1721 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1722 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1723 if (info->lpszIssuerInfo)
1724 CertNameToStrW(context->dwCertEncodingType,
1725 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1726 info->lpszIssuerInfo, len);
1727 }else {
1728 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1729
1730 len = CertNameToStrA(context->dwCertEncodingType,
1731 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1732 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1733 if(infoA->lpszSubjectInfo)
1734 CertNameToStrA(context->dwCertEncodingType,
1735 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1736 infoA->lpszSubjectInfo, len);
1737 len = CertNameToStrA(context->dwCertEncodingType,
1738 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1739 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1740 if(infoA->lpszIssuerInfo)
1741 CertNameToStrA(context->dwCertEncodingType,
1742 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1743 infoA->lpszIssuerInfo, len);
1744 }
1745
1746 /*
1747 * Contrary to MSDN, these do not appear to be set.
1748 * lpszProtocolName
1749 * lpszSignatureAlgName
1750 * lpszEncryptionAlgName
1751 * dwKeySize
1752 */
1753 CertFreeCertificateContext(context);
1754 return ERROR_SUCCESS;
1755 }
1756 }
1757 }
1758
1759 return INET_QueryOption(option, buffer, size, unicode);
1760 }
1761
1762 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1763 {
1764 http_request_t *req = (http_request_t*)hdr;
1765
1766 switch(option) {
1767 case INTERNET_OPTION_SEND_TIMEOUT:
1768 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1769 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1770
1771 if (size != sizeof(DWORD))
1772 return ERROR_INVALID_PARAMETER;
1773
1774 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1775 *(DWORD*)buffer);
1776
1777 case INTERNET_OPTION_USERNAME:
1778 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1779 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1780 return ERROR_SUCCESS;
1781
1782 case INTERNET_OPTION_PASSWORD:
1783 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1784 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1785 return ERROR_SUCCESS;
1786 case INTERNET_OPTION_HTTP_DECODING:
1787 if(size != sizeof(BOOL))
1788 return ERROR_INVALID_PARAMETER;
1789 req->decoding = *(BOOL*)buffer;
1790 return ERROR_SUCCESS;
1791 }
1792
1793 return ERROR_INTERNET_INVALID_OPTION;
1794 }
1795
1796 /* read some more data into the read buffer (the read section must be held) */
1797 static DWORD read_more_data( http_request_t *req, int maxlen )
1798 {
1799 DWORD res;
1800 int len;
1801
1802 if (req->read_pos)
1803 {
1804 /* move existing data to the start of the buffer */
1805 if(req->read_size)
1806 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1807 req->read_pos = 0;
1808 }
1809
1810 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1811
1812 res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1813 maxlen - req->read_size, 0, &len );
1814 if(res == ERROR_SUCCESS)
1815 req->read_size += len;
1816
1817 return res;
1818 }
1819
1820 /* remove some amount of data from the read buffer (the read section must be held) */
1821 static void remove_data( http_request_t *req, int count )
1822 {
1823 if (!(req->read_size -= count)) req->read_pos = 0;
1824 else req->read_pos += count;
1825 }
1826
1827 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1828 {
1829 int count, bytes_read, pos = 0;
1830 DWORD res;
1831
1832 EnterCriticalSection( &req->read_section );
1833 for (;;)
1834 {
1835 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1836
1837 if (eol)
1838 {
1839 count = eol - (req->read_buf + req->read_pos);
1840 bytes_read = count + 1;
1841 }
1842 else count = bytes_read = req->read_size;
1843
1844 count = min( count, *len - pos );
1845 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1846 pos += count;
1847 remove_data( req, bytes_read );
1848 if (eol) break;
1849
1850 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
1851 {
1852 *len = 0;
1853 TRACE( "returning empty string\n" );
1854 LeaveCriticalSection( &req->read_section );
1855 INTERNET_SetLastError(res);
1856 return FALSE;
1857 }
1858 }
1859 LeaveCriticalSection( &req->read_section );
1860
1861 if (pos < *len)
1862 {
1863 if (pos && buffer[pos - 1] == '\r') pos--;
1864 *len = pos + 1;
1865 }
1866 buffer[*len - 1] = 0;
1867 TRACE( "returning %s\n", debugstr_a(buffer));
1868 return TRUE;
1869 }
1870
1871 /* discard data contents until we reach end of line (the read section must be held) */
1872 static DWORD discard_eol( http_request_t *req )
1873 {
1874 DWORD res;
1875
1876 do
1877 {
1878 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1879 if (eol)
1880 {
1881 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
1882 break;
1883 }
1884 req->read_pos = req->read_size = 0; /* discard everything */
1885 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1886 } while (req->read_size);
1887 return ERROR_SUCCESS;
1888 }
1889
1890 /* read the size of the next chunk (the read section must be held) */
1891 static DWORD start_next_chunk( http_request_t *req )
1892 {
1893 DWORD chunk_size = 0, res;
1894
1895 if (!req->dwContentLength) return ERROR_SUCCESS;
1896 if (req->dwContentLength == req->dwContentRead)
1897 {
1898 /* read terminator for the previous chunk */
1899 if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
1900 req->dwContentLength = ~0u;
1901 req->dwContentRead = 0;
1902 }
1903 for (;;)
1904 {
1905 while (req->read_size)
1906 {
1907 char ch = req->read_buf[req->read_pos];
1908 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
1909 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
1910 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
1911 else if (ch == ';' || ch == '\r' || ch == '\n')
1912 {
1913 TRACE( "reading %u byte chunk\n", chunk_size );
1914 req->dwContentLength = chunk_size;
1915 req->dwContentRead = 0;
1916 return discard_eol( req );
1917 }
1918 remove_data( req, 1 );
1919 }
1920 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1921 if (!req->read_size)
1922 {
1923 req->dwContentLength = req->dwContentRead = 0;
1924 return ERROR_SUCCESS;
1925 }
1926 }
1927 }
1928
1929 /* check if we have reached the end of the data to read (the read section must be held) */
1930 static BOOL end_of_read_data( http_request_t *req )
1931 {
1932 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
1933 if (req->read_chunked) return (req->dwContentLength == 0);
1934 if (req->dwContentLength == ~0u) return FALSE;
1935 return (req->dwContentLength == req->dwContentRead);
1936 }
1937
1938 /* fetch some more data into the read buffer (the read section must be held) */
1939 static DWORD refill_buffer( http_request_t *req )
1940 {
1941 int len = sizeof(req->read_buf);
1942 DWORD res;
1943
1944 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
1945 {
1946 if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
1947 }
1948
1949 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
1950 if (len <= req->read_size) return ERROR_SUCCESS;
1951
1952 if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
1953 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
1954 return ERROR_SUCCESS;
1955 }
1956
1957 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
1958 {
1959 DWORD ret = ERROR_SUCCESS;
1960 int read = 0;
1961
1962 #ifdef HAVE_ZLIB
1963 z_stream *zstream = &req->gzip_stream->zstream;
1964 DWORD buf_avail;
1965 int zres;
1966
1967 while(read < size && !req->gzip_stream->end_of_data) {
1968 if(!req->read_size) {
1969 if(!sync || refill_buffer(req) != ERROR_SUCCESS)
1970 break;
1971 }
1972
1973 buf_avail = req->dwContentLength == ~0 ? req->read_size : min(req->read_size, req->dwContentLength-req->dwContentRead);
1974
1975 zstream->next_in = req->read_buf+req->read_pos;
1976 zstream->avail_in = buf_avail;
1977 zstream->next_out = buf+read;
1978 zstream->avail_out = size-read;
1979 zres = inflate(zstream, Z_FULL_FLUSH);
1980 read = size - zstream->avail_out;
1981 req->dwContentRead += buf_avail-zstream->avail_in;
1982 remove_data(req, buf_avail-zstream->avail_in);
1983 if(zres == Z_STREAM_END) {
1984 TRACE("end of data\n");
1985 req->gzip_stream->end_of_data = TRUE;
1986 inflateEnd(&req->gzip_stream->zstream);
1987 }else if(zres != Z_OK) {
1988 WARN("inflate failed %d\n", zres);
1989 if(!read)
1990 ret = ERROR_INTERNET_DECODING_FAILED;
1991 break;
1992 }
1993 }
1994 #endif
1995
1996 *read_ret = read;
1997 return ret;
1998 }
1999
2000 static void refill_gzip_buffer(http_request_t *req)
2001 {
2002 DWORD res;
2003 int len;
2004
2005 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2006 return;
2007
2008 if(req->gzip_stream->buf_pos) {
2009 if(req->gzip_stream->buf_size)
2010 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2011 req->gzip_stream->buf_pos = 0;
2012 }
2013
2014 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2015 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2016 if(res == ERROR_SUCCESS)
2017 req->gzip_stream->buf_size += len;
2018 }
2019
2020 /* return the size of data available to be read immediately (the read section must be held) */
2021 static DWORD get_avail_data( http_request_t *req )
2022 {
2023 if (req->gzip_stream) {
2024 refill_gzip_buffer(req);
2025 return req->gzip_stream->buf_size;
2026 }
2027 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2028 return 0;
2029 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2030 }
2031
2032 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2033 {
2034 INTERNET_ASYNC_RESULT iar;
2035 DWORD res;
2036
2037 TRACE("%p\n", req);
2038
2039 EnterCriticalSection( &req->read_section );
2040 if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
2041 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2042 iar.dwError = first_notif ? 0 : get_avail_data(req);
2043 }else {
2044 iar.dwResult = 0;
2045 iar.dwError = res;
2046 }
2047 LeaveCriticalSection( &req->read_section );
2048
2049 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2050 sizeof(INTERNET_ASYNC_RESULT));
2051 }
2052
2053 /* read data from the http connection (the read section must be held) */
2054 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2055 {
2056 BOOL finished_reading = FALSE;
2057 int len, bytes_read = 0;
2058 DWORD ret = ERROR_SUCCESS;
2059
2060 EnterCriticalSection( &req->read_section );
2061
2062 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2063 {
2064 if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
2065 }
2066
2067 if(req->gzip_stream) {
2068 if(req->gzip_stream->buf_size) {
2069 bytes_read = min(req->gzip_stream->buf_size, size);
2070 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2071 req->gzip_stream->buf_pos += bytes_read;
2072 req->gzip_stream->buf_size -= bytes_read;
2073 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2074 refill_buffer(req);
2075 }
2076
2077 if(size > bytes_read) {
2078 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2079 if(ret == ERROR_SUCCESS)
2080 bytes_read += len;
2081 }
2082
2083 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2084 }else {
2085 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2086
2087 if (req->read_size) {
2088 bytes_read = min( req->read_size, size );
2089 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2090 remove_data( req, bytes_read );
2091 }
2092
2093 if (size > bytes_read && (!bytes_read || sync)) {
2094 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2095 sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2096 bytes_read += len;
2097 /* always return success, even if the network layer returns an error */
2098 }
2099
2100 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2101 req->dwContentRead += bytes_read;
2102 }
2103 done:
2104 *read = bytes_read;
2105
2106 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2107 LeaveCriticalSection( &req->read_section );
2108
2109 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2110 BOOL res;
2111 DWORD dwBytesWritten;
2112
2113 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2114 if(!res)
2115 WARN("WriteFile failed: %u\n", GetLastError());
2116 }
2117
2118 if(finished_reading)
2119 HTTP_FinishedReading(req);
2120
2121 return ret;
2122 }
2123
2124
2125 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2126 {
2127 http_request_t *req = (http_request_t*)hdr;
2128 return HTTPREQ_Read(req, buffer, size, read, TRUE);
2129 }
2130
2131 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2132 {
2133 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2134 http_request_t *req = (http_request_t*)workRequest->hdr;
2135 INTERNET_ASYNC_RESULT iar;
2136 DWORD res;
2137
2138 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2139
2140 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2141 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2142
2143 iar.dwResult = res == ERROR_SUCCESS;
2144 iar.dwError = res;
2145
2146 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2147 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2148 sizeof(INTERNET_ASYNC_RESULT));
2149 }
2150
2151 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2152 DWORD flags, DWORD_PTR context)
2153 {
2154 http_request_t *req = (http_request_t*)hdr;
2155 DWORD res;
2156
2157 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2158 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2159
2160 if (buffers->dwStructSize != sizeof(*buffers))
2161 return ERROR_INVALID_PARAMETER;
2162
2163 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2164
2165 if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
2166 {
2167 WORKREQUEST workRequest;
2168
2169 if (TryEnterCriticalSection( &req->read_section ))
2170 {
2171 if (get_avail_data(req))
2172 {
2173 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2174 &buffers->dwBufferLength, FALSE);
2175 LeaveCriticalSection( &req->read_section );
2176 goto done;
2177 }
2178 LeaveCriticalSection( &req->read_section );
2179 }
2180
2181 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2182 workRequest.hdr = WININET_AddRef(&req->hdr);
2183 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2184
2185 INTERNET_AsyncCall(&workRequest);
2186
2187 return ERROR_IO_PENDING;
2188 }
2189
2190 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2191 !(flags & IRF_NO_WAIT));
2192
2193 done:
2194 if (res == ERROR_SUCCESS) {
2195 DWORD size = buffers->dwBufferLength;
2196 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2197 &size, sizeof(size));
2198 }
2199
2200 return res;
2201 }
2202
2203 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2204 {
2205 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2206 http_request_t *req = (http_request_t*)workRequest->hdr;
2207 INTERNET_ASYNC_RESULT iar;
2208 DWORD res;
2209
2210 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2211
2212 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2213 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2214
2215 iar.dwResult = res == ERROR_SUCCESS;
2216 iar.dwError = res;
2217
2218 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2219 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2220 sizeof(INTERNET_ASYNC_RESULT));
2221 }
2222
2223 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2224 DWORD flags, DWORD_PTR context)
2225 {
2226
2227 http_request_t *req = (http_request_t*)hdr;
2228 DWORD res;
2229
2230 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2231 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2232
2233 if (buffers->dwStructSize != sizeof(*buffers))
2234 return ERROR_INVALID_PARAMETER;
2235
2236 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2237
2238 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2239 {
2240 WORKREQUEST workRequest;
2241
2242 if (TryEnterCriticalSection( &req->read_section ))
2243 {
2244 if (get_avail_data(req))
2245 {
2246 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2247 &buffers->dwBufferLength, FALSE);
2248 LeaveCriticalSection( &req->read_section );
2249 goto done;
2250 }
2251 LeaveCriticalSection( &req->read_section );
2252 }
2253
2254 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2255 workRequest.hdr = WININET_AddRef(&req->hdr);
2256 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2257
2258 INTERNET_AsyncCall(&workRequest);
2259
2260 return ERROR_IO_PENDING;
2261 }
2262
2263 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2264 !(flags & IRF_NO_WAIT));
2265
2266 done:
2267 if (res == ERROR_SUCCESS) {
2268 DWORD size = buffers->dwBufferLength;
2269 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2270 &size, sizeof(size));
2271 }
2272
2273 return res;
2274 }
2275
2276 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2277 {
2278 DWORD res;
2279 http_request_t *lpwhr = (http_request_t*)hdr;
2280
2281 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2282
2283 *written = 0;
2284 res = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written);
2285 if (res == ERROR_SUCCESS)
2286 lpwhr->dwBytesWritten += *written;
2287
2288 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2289 return res;
2290 }
2291
2292 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2293 {
2294 http_request_t *req = (http_request_t*)workRequest->hdr;
2295
2296 HTTP_ReceiveRequestData(req, FALSE);
2297 }
2298
2299 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2300 {
2301 http_request_t *req = (http_request_t*)hdr;
2302
2303 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2304
2305 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2306 {
2307 WORKREQUEST workRequest;
2308
2309 /* never wait, if we can't enter the section we queue an async request right away */
2310 if (TryEnterCriticalSection( &req->read_section ))
2311 {
2312 if ((*available = get_avail_data( req ))) goto done;
2313 if (end_of_read_data( req )) goto done;
2314 LeaveCriticalSection( &req->read_section );
2315 }
2316
2317 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2318 workRequest.hdr = WININET_AddRef( &req->hdr );
2319
2320 INTERNET_AsyncCall(&workRequest);
2321
2322 return ERROR_IO_PENDING;
2323 }
2324
2325 EnterCriticalSection( &req->read_section );
2326
2327 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2328 {
2329 refill_buffer( req );
2330 *available = get_avail_data( req );
2331 }
2332
2333 done:
2334 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2335 {
2336 DWORD extra;
2337 if (NETCON_query_data_available(&req->netConnection, &extra))
2338 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2339 }
2340 LeaveCriticalSection( &req->read_section );
2341
2342 TRACE( "returning %u\n", *available );
2343 return ERROR_SUCCESS;
2344 }
2345
2346 static const object_vtbl_t HTTPREQVtbl = {
2347 HTTPREQ_Destroy,
2348 HTTPREQ_CloseConnection,
2349 HTTPREQ_QueryOption,
2350 HTTPREQ_SetOption,
2351 HTTPREQ_ReadFile,
2352 HTTPREQ_ReadFileExA,
2353 HTTPREQ_ReadFileExW,
2354 HTTPREQ_WriteFile,
2355 HTTPREQ_QueryDataAvailable,
2356 NULL
2357 };
2358
2359 /***********************************************************************
2360 * HTTP_HttpOpenRequestW (internal)
2361 *
2362 * Open a HTTP request handle
2363 *
2364 * RETURNS
2365 * HINTERNET a HTTP request handle on success
2366 * NULL on failure
2367 *
2368 */
2369 static DWORD HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2370 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2371 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2372 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2373 {
2374 appinfo_t *hIC = NULL;
2375 http_request_t *lpwhr;
2376 LPWSTR lpszHostName = NULL;
2377 HINTERNET handle = NULL;
2378 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2379 DWORD len, res;
2380
2381 TRACE("-->\n");
2382
2383 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2384 hIC = lpwhs->lpAppInfo;
2385
2386 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2387 if (NULL == lpwhr)
2388 {
2389 res = ERROR_OUTOFMEMORY;
2390 goto lend;
2391 }
2392 lpwhr->hdr.htype = WH_HHTTPREQ;
2393 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2394 lpwhr->hdr.dwFlags = dwFlags;
2395 lpwhr->hdr.dwContext = dwContext;
2396 lpwhr->hdr.refs = 1;
2397 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2398 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2399 lpwhr->dwContentLength = ~0u;
2400 InitializeCriticalSection( &lpwhr->read_section );
2401
2402 WININET_AddRef( &lpwhs->hdr );
2403 lpwhr->lpHttpSession = lpwhs;
2404 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2405
2406 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2407 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2408 if (NULL == lpszHostName)
2409 {
2410 res = ERROR_OUTOFMEMORY;
2411 goto lend;
2412 }
2413
2414 handle = WININET_AllocHandle( &lpwhr->hdr );
2415 if (NULL == handle)
2416 {
2417 res = ERROR_OUTOFMEMORY;
2418 goto lend;
2419 }
2420
2421 if ((res = NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2422 {
2423 InternetCloseHandle( handle );
2424 handle = NULL;
2425 goto lend;
2426 }
2427
2428 if (lpszObjectName && *lpszObjectName) {
2429 HRESULT rc;
2430
2431 len = 0;
2432 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2433 if (rc != E_POINTER)
2434 len = strlenW(lpszObjectName)+1;
2435 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2436 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2437 URL_ESCAPE_SPACES_ONLY);
2438 if (rc != S_OK)
2439 {
2440 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2441 strcpyW(lpwhr->lpszPath,lpszObjectName);
2442 }
2443 }else {
2444 static const WCHAR slashW[] = {'/',0};
2445
2446 lpwhr->lpszPath = heap_strdupW(slashW);
2447 }
2448
2449 if (lpszReferrer && *lpszReferrer)
2450 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2451
2452 if (lpszAcceptTypes)
2453 {
2454 int i;
2455 for (i = 0; lpszAcceptTypes[i]; i++)
2456 {
2457 if (!*lpszAcceptTypes[i]) continue;
2458 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2459 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2460 HTTP_ADDHDR_FLAG_REQ |
2461 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2462 }
2463 }
2464
2465 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2466 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2467
2468 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2469 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2470 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2471 {
2472 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2473 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2474 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2475 }
2476 else
2477 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2478 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2479
2480 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2481 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2482 INTERNET_DEFAULT_HTTPS_PORT :
2483 INTERNET_DEFAULT_HTTP_PORT);
2484
2485 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2486 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2487 INTERNET_DEFAULT_HTTPS_PORT :
2488 INTERNET_DEFAULT_HTTP_PORT);
2489
2490 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2491 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2492
2493 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2494 INTERNET_STATUS_HANDLE_CREATED, &handle,
2495 sizeof(handle));
2496
2497 lend:
2498 HeapFree(GetProcessHeap(), 0, lpszHostName);
2499 if( lpwhr )
2500 WININET_Release( &lpwhr->hdr );
2501
2502 TRACE("<-- %p (%p)\n", handle, lpwhr);
2503 *ret = handle;
2504 return res;
2505 }
2506
2507 /***********************************************************************
2508 * HttpOpenRequestW (WININET.@)
2509 *
2510 * Open a HTTP request handle
2511 *
2512 * RETURNS
2513 * HINTERNET a HTTP request handle on success
2514 * NULL on failure
2515 *
2516 */
2517 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2518 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2519 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2520 DWORD dwFlags, DWORD_PTR dwContext)
2521 {
2522 http_session_t *lpwhs;
2523 HINTERNET handle = NULL;
2524 DWORD res;
2525
2526 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2527 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2528 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2529 dwFlags, dwContext);
2530 if(lpszAcceptTypes!=NULL)
2531 {
2532 int i;
2533 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2534 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2535 }
2536
2537 lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
2538 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
2539 {
2540 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2541 goto lend;
2542 }
2543
2544 /*
2545 * My tests seem to show that the windows version does not
2546 * become asynchronous until after this point. And anyhow
2547 * if this call was asynchronous then how would you get the
2548 * necessary HINTERNET pointer returned by this function.
2549 *
2550 */
2551 res = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
2552 lpszVersion, lpszReferrer, lpszAcceptTypes,
2553 dwFlags, dwContext, &handle);
2554 lend:
2555 if( lpwhs )
2556 WININET_Release( &lpwhs->hdr );
2557 TRACE("returning %p\n", handle);
2558 if(res != ERROR_SUCCESS)
2559 SetLastError(res);
2560 return handle;
2561 }
2562
2563 /* read any content returned by the server so that the connection can be
2564 * reused */
2565 static void HTTP_DrainContent(http_request_t *req)
2566 {
2567 DWORD bytes_read;
2568
2569 if (!NETCON_connected(&req->netConnection)) return;
2570
2571 if (req->dwContentLength == -1)
2572 {
2573 NETCON_close(&req->netConnection);
2574 return;
2575 }
2576 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2577
2578 do
2579 {
2580 char buffer[2048];
2581 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2582 return;
2583 } while (bytes_read);
2584 }
2585
2586 static const LPCWSTR header_lookup[] = {
2587 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2588 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2589 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2590 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2591 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2592 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2593 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2594 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2595 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2596 szDate, /* HTTP_QUERY_DATE = 9 */
2597 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2598 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2599 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2600 szURI, /* HTTP_QUERY_URI = 13 */
2601 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2602 NULL, /* HTTP_QUERY_COST = 15 */
2603 NULL, /* HTTP_QUERY_LINK = 16 */
2604 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2605 NULL, /* HTTP_QUERY_VERSION = 18 */
2606 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2607 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2608 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2609 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2610 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2611 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2612 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2613 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2614 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2615 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2616 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2617 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2618 NULL, /* HTTP_QUERY_FROM = 31 */
2619 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2620 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2621 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2622 szReferer, /* HTTP_QUERY_REFERER = 35 */
2623 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2624 szServer, /* HTTP_QUERY_SERVER = 37 */
2625 NULL, /* HTTP_TITLE = 38 */
2626 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2627 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2628 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2629 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2630 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2631 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2632 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2633 NULL, /* HTTP_QUERY_REFRESH = 46 */
2634 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2635 szAge, /* HTTP_QUERY_AGE = 48 */
2636 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2637 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2638 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2639 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2640 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2641 szETag, /* HTTP_QUERY_ETAG = 54 */
2642 hostW, /* HTTP_QUERY_HOST = 55 */
2643 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2644 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2645 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2646 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2647 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2648 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2649 szRange, /* HTTP_QUERY_RANGE = 62 */
2650 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2651 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2652 szVary, /* HTTP_QUERY_VARY = 65 */
2653 szVia, /* HTTP_QUERY_VIA = 66 */
2654 szWarning, /* HTTP_QUERY_WARNING = 67 */
2655 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2656 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2657 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2658 };
2659
2660 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2661
2662 /***********************************************************************
2663 * HTTP_HttpQueryInfoW (internal)
2664 */
2665 static DWORD HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2666 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2667 {
2668 LPHTTPHEADERW lphttpHdr = NULL;
2669 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2670 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2671 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2672 INT index = -1;
2673
2674 /* Find requested header structure */
2675 switch (level)
2676 {
2677 case HTTP_QUERY_CUSTOM:
2678 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
2679 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2680 break;
2681 case HTTP_QUERY_RAW_HEADERS_CRLF:
2682 {
2683 LPWSTR headers;
2684 DWORD len = 0;
2685 DWORD res = ERROR_INVALID_PARAMETER;
2686
2687 if (request_only)
2688 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2689 else
2690 headers = lpwhr->lpszRawHeaders;
2691
2692 if (headers)
2693 len = strlenW(headers) * sizeof(WCHAR);
2694
2695 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2696 {
2697 len += sizeof(WCHAR);
2698 res = ERROR_INSUFFICIENT_BUFFER;
2699 }
2700 else if (lpBuffer)
2701 {
2702 if (headers)
2703 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2704 else
2705 {
2706 len = strlenW(szCrLf) * sizeof(WCHAR);
2707 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2708 }
2709 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2710 res = ERROR_SUCCESS;
2711 }
2712 *lpdwBufferLength = len;
2713
2714 if (request_only)
2715 HeapFree(GetProcessHeap(), 0, headers);
2716 return res;
2717 }
2718 case HTTP_QUERY_RAW_HEADERS:
2719 {
2720 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2721 DWORD i, size = 0;
2722 LPWSTR pszString = lpBuffer;
2723
2724 for (i = 0; ppszRawHeaderLines[i]; i++)
2725 size += strlenW(ppszRawHeaderLines[i]) + 1;
2726
2727 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2728 {
2729 HTTP_FreeTokens(ppszRawHeaderLines);
2730 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2731 return ERROR_INSUFFICIENT_BUFFER;
2732 }
2733 if (pszString)
2734 {
2735 for (i = 0; ppszRawHeaderLines[i]; i++)
2736 {
2737 DWORD len = strlenW(ppszRawHeaderLines[i]);
2738 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2739 pszString += len+1;
2740 }
2741 *pszString = '\0';
2742 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2743 }
2744 *lpdwBufferLength = size * sizeof(WCHAR);
2745 HTTP_FreeTokens(ppszRawHeaderLines);
2746
2747 return ERROR_SUCCESS;
2748 }
2749 case HTTP_QUERY_STATUS_TEXT:
2750 if (lpwhr->lpszStatusText)
2751 {
2752 DWORD len = strlenW(lpwhr->lpszStatusText);
2753 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2754 {
2755 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2756 return ERROR_INSUFFICIENT_BUFFER;
2757 }
2758 if (lpBuffer)
2759 {
2760 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2761 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2762 }
2763 *lpdwBufferLength = len * sizeof(WCHAR);
2764 return ERROR_SUCCESS;
2765 }
2766 break;
2767 case HTTP_QUERY_VERSION:
2768 if (lpwhr->lpszVersion)
2769 {
2770 DWORD len = strlenW(lpwhr->lpszVersion);
2771 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2772 {
2773 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2774 return ERROR_INSUFFICIENT_BUFFER;
2775 }
2776 if (lpBuffer)
2777 {
2778 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2779 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2780 }
2781 *lpdwBufferLength = len * sizeof(WCHAR);
2782 return ERROR_SUCCESS;
2783 }
2784 break;
2785 case HTTP_QUERY_CONTENT_ENCODING:
2786 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2787 requested_index,request_only);
2788 break;
2789 default:
2790 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2791
2792 if (level < LAST_TABLE_HEADER && header_lookup[level])
2793 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2794 requested_index,request_only);
2795 }
2796
2797 if (index >= 0)
2798 lphttpHdr = &lpwhr->pCustHeaders[index];
2799
2800 /* Ensure header satisfies requested attributes */
2801 if (!lphttpHdr ||
2802 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2803 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2804 {
2805 return ERROR_HTTP_HEADER_NOT_FOUND;
2806 }
2807
2808 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2809
2810 /* coalesce value to requested type */
2811 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2812 {
2813 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2814 TRACE(" returning number: %d\n", *(int *)lpBuffer);
2815 }
2816 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2817 {
2818 time_t tmpTime;
2819 struct tm tmpTM;
2820 SYSTEMTIME *STHook;
2821
2822 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2823
2824 tmpTM = *gmtime(&tmpTime);
2825 STHook = (SYSTEMTIME *)lpBuffer;
2826 STHook->wDay = tmpTM.tm_mday;
2827 STHook->wHour = tmpTM.tm_hour;
2828 STHook->wMilliseconds = 0;
2829 STHook->wMinute = tmpTM.tm_min;
2830 STHook->wDayOfWeek = tmpTM.tm_wday;
2831 STHook->wMonth = tmpTM.tm_mon + 1;
2832 STHook->wSecond = tmpTM.tm_sec;
2833 STHook->wYear = tmpTM.tm_year;
2834
2835 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
2836 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
2837 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
2838 }
2839 else if (lphttpHdr->lpszValue)
2840 {
2841 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
2842
2843 if (len > *lpdwBufferLength)
2844 {
2845 *lpdwBufferLength = len;
2846 return ERROR_INSUFFICIENT_BUFFER;
2847 }
2848 if (lpBuffer)
2849 {
2850 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
2851 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
2852 }
2853 *lpdwBufferLength = len - sizeof(WCHAR);
2854 }
2855 return ERROR_SUCCESS;
2856 }
2857
2858 /***********************************************************************
2859 * HttpQueryInfoW (WININET.@)
2860 *
2861 * Queries for information about an HTTP request
2862 *
2863 * RETURNS
2864 * TRUE on success
2865 * FALSE on failure
2866 *
2867 */
2868 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
2869 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2870 {
2871 http_request_t *lpwhr;
2872 DWORD res;
2873
2874 if (TRACE_ON(wininet)) {
2875 #define FE(x) { x, #x }
2876 static const wininet_flag_info query_flags[] = {
2877 FE(HTTP_QUERY_MIME_VERSION),
2878 FE(HTTP_QUERY_CONTENT_TYPE),
2879 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
2880 FE(HTTP_QUERY_CONTENT_ID),
2881 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
2882 FE(HTTP_QUERY_CONTENT_LENGTH),
2883 FE(HTTP_QUERY_CONTENT_LANGUAGE),
2884 FE(HTTP_QUERY_ALLOW),
2885 FE(HTTP_QUERY_PUBLIC),
2886 FE(HTTP_QUERY_DATE),
2887 FE(HTTP_QUERY_EXPIRES),
2888 FE(HTTP_QUERY_LAST_MODIFIED),
2889 FE(HTTP_QUERY_MESSAGE_ID),
2890 FE(HTTP_QUERY_URI),
2891 FE(HTTP_QUERY_DERIVED_FROM),
2892 FE(HTTP_QUERY_COST),
2893 FE(HTTP_QUERY_LINK),
2894 FE(HTTP_QUERY_PRAGMA),
2895 FE(HTTP_QUERY_VERSION),
2896 FE(HTTP_QUERY_STATUS_CODE),
2897 FE(HTTP_QUERY_STATUS_TEXT),
2898 FE(HTTP_QUERY_RAW_HEADERS),
2899 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
2900 FE(HTTP_QUERY_CONNECTION),
2901 FE(HTTP_QUERY_ACCEPT),
2902 FE(HTTP_QUERY_ACCEPT_CHARSET),
2903 FE(HTTP_QUERY_ACCEPT_ENCODING),
2904 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
2905 FE(HTTP_QUERY_AUTHORIZATION),
2906 FE(HTTP_QUERY_CONTENT_ENCODING),
2907 FE(HTTP_QUERY_FORWARDED),
2908 FE(HTTP_QUERY_FROM),
2909 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
2910 FE(HTTP_QUERY_LOCATION),
2911 FE(HTTP_QUERY_ORIG_URI),
2912 FE(HTTP_QUERY_REFERER),
2913 FE(HTTP_QUERY_RETRY_AFTER),
2914 FE(HTTP_QUERY_SERVER),
2915 FE(HTTP_QUERY_TITLE),
2916 FE(HTTP_QUERY_USER_AGENT),
2917 FE(HTTP_QUERY_WWW_AUTHENTICATE),
2918 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
2919 FE(HTTP_QUERY_ACCEPT_RANGES),
2920 FE(HTTP_QUERY_SET_COOKIE),
2921 FE(HTTP_QUERY_COOKIE),
2922 FE(HTTP_QUERY_REQUEST_METHOD),
2923 FE(HTTP_QUERY_REFRESH),
2924 FE(HTTP_QUERY_CONTENT_DISPOSITION),
2925 FE(HTTP_QUERY_AGE),
2926 FE(HTTP_QUERY_CACHE_CONTROL),
2927 FE(HTTP_QUERY_CONTENT_BASE),
2928 FE(HTTP_QUERY_CONTENT_LOCATION),
2929 FE(HTTP_QUERY_CONTENT_MD5),
2930 FE(HTTP_QUERY_CONTENT_RANGE),
2931 FE(HTTP_QUERY_ETAG),
2932 FE(HTTP_QUERY_HOST),
2933 FE(HTTP_QUERY_IF_MATCH),
2934 FE(HTTP_QUERY_IF_NONE_MATCH),
2935 FE(HTTP_QUERY_IF_RANGE),
2936 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
2937 FE(HTTP_QUERY_MAX_FORWARDS),
2938 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
2939 FE(HTTP_QUERY_RANGE),
2940 FE(HTTP_QUERY_TRANSFER_ENCODING),
2941 FE(HTTP_QUERY_UPGRADE),
2942 FE(HTTP_QUERY_VARY),
2943 FE(HTTP_QUERY_VIA),
2944 FE(HTTP_QUERY_WARNING),
2945 FE(HTTP_QUERY_CUSTOM)
2946 };
2947 static const wininet_flag_info modifier_flags[] = {
2948 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
2949 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
2950 FE(HTTP_QUERY_FLAG_NUMBER),
2951 FE(HTTP_QUERY_FLAG_COALESCE)
2952 };
2953 #undef FE
2954 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
2955 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
2956 DWORD i;
2957
2958 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
2959 TRACE(" Attribute:");
2960 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
2961 if (query_flags[i].val == info) {
2962 TRACE(" %s", query_flags[i].name);
2963 break;
2964 }
2965 }
2966 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
2967 TRACE(" Unknown (%08x)", info);
2968 }
2969
2970 TRACE(" Modifier:");
2971 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
2972 if (modifier_flags[i].val & info_mod) {
2973 TRACE(" %s", modifier_flags[i].name);
2974 info_mod &= ~ modifier_flags[i].val;
2975 }
2976 }
2977
2978 if (info_mod) {
2979 TRACE(" Unknown (%08x)", info_mod);
2980 }
2981 TRACE("\n");
2982 }
2983
2984 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
2985 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
2986 {
2987 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2988 goto lend;
2989 }
2990
2991 if (lpBuffer == NULL)
2992 *lpdwBufferLength = 0;
2993 res = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
2994 lpBuffer, lpdwBufferLength, lpdwIndex);
2995
2996 lend:
2997 if( lpwhr )
2998 WININET_Release( &lpwhr->hdr );
2999
3000 TRACE("%u <--\n", res);
3001 if(res != ERROR_SUCCESS)
3002 SetLastError(res);
3003 return res == ERROR_SUCCESS;
3004 }
3005
3006 /***********************************************************************
3007 * HttpQueryInfoA (WININET.@)
3008 *
3009 * Queries for information about an HTTP request
3010 *
3011 * RETURNS
3012 * TRUE on success
3013 * FALSE on failure
3014 *
3015 */
3016 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3017 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3018 {
3019 BOOL result;
3020 DWORD len;
3021 WCHAR* bufferW;
3022
3023 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3024 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3025 {
3026 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3027 lpdwBufferLength, lpdwIndex );
3028 }
3029
3030 if (lpBuffer)
3031 {
3032 DWORD alloclen;
3033 len = (*lpdwBufferLength)*sizeof(WCHAR);
3034 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3035 {
3036 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3037 if (alloclen < len)
3038 alloclen = len;
3039 }
3040 else
3041 alloclen = len;
3042 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3043 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3044 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3045 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3046 } else
3047 {
3048 bufferW = NULL;
3049 len = 0;
3050 }
3051
3052 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3053 &len, lpdwIndex );
3054 if( result )
3055 {
3056 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3057 lpBuffer, *lpdwBufferLength, NULL, NULL );
3058 *lpdwBufferLength = len - 1;
3059
3060 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3061 }
3062 else
3063 /* since the strings being returned from HttpQueryInfoW should be
3064 * only ASCII characters, it is reasonable to assume that all of
3065 * the Unicode characters can be reduced to a single byte */
3066 *lpdwBufferLength = len / sizeof(WCHAR);
3067
3068 HeapFree(GetProcessHeap(), 0, bufferW );
3069
3070 return result;
3071 }
3072
3073 /***********************************************************************
3074 * HTTP_GetRedirectURL (internal)
3075 */
3076 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3077 {
3078 static WCHAR szHttp[] = {'h','t','t','p',0};
3079 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3080 http_session_t *lpwhs = lpwhr->lpHttpSession;
3081 URL_COMPONENTSW urlComponents;
3082 DWORD url_length = 0;
3083 LPWSTR orig_url;
3084 LPWSTR combined_url;
3085
3086 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3087 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3088 urlComponents.dwSchemeLength = 0;
3089 urlComponents.lpszHostName = lpwhs->lpszHostName;
3090 urlComponents.dwHostNameLength = 0;
3091 urlComponents.nPort = lpwhs->nHostPort;
3092 urlComponents.lpszUserName = lpwhs->lpszUserName;
3093 urlComponents.dwUserNameLength = 0;
3094 urlComponents.lpszPassword = NULL;
3095 urlComponents.dwPasswordLength = 0;
3096 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3097 urlComponents.dwUrlPathLength = 0;
3098 urlComponents.lpszExtraInfo = NULL;
3099 urlComponents.dwExtraInfoLength = 0;
3100
3101 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3102 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3103 return NULL;
3104
3105 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3106
3107 /* convert from bytes to characters */
3108 url_length = url_length / sizeof(WCHAR) - 1;
3109 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3110 {
3111 HeapFree(GetProcessHeap(), 0, orig_url);
3112 return NULL;
3113 }
3114
3115 url_length = 0;
3116 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3117 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3118 {
3119 HeapFree(GetProcessHeap(), 0, orig_url);
3120 return NULL;
3121 }
3122 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3123
3124 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3125 {
3126 HeapFree(GetProcessHeap(), 0, orig_url);
3127 HeapFree(GetProcessHeap(), 0, combined_url);
3128 return NULL;
3129 }
3130 HeapFree(GetProcessHeap(), 0, orig_url);
3131 return combined_url;
3132 }
3133
3134
3135 /***********************************************************************
3136 * HTTP_HandleRedirect (internal)
3137 */
3138 static DWORD HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3139 {
3140 http_session_t *lpwhs = lpwhr->lpHttpSession;
3141 appinfo_t *hIC = lpwhs->lpAppInfo;
3142 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3143 WCHAR path[INTERNET_MAX_URL_LENGTH];
3144 int index;
3145
3146 if(lpszUrl[0]=='/')
3147 {
3148 /* if it's an absolute path, keep the same session info */
3149 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3150 }
3151 else
3152 {
3153 URL_COMPONENTSW urlComponents;
3154 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3155 static WCHAR szHttp[] = {'h','t','t','p',0};
3156 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3157
3158 userName[0] = 0;
3159 hostName[0] = 0;
3160 protocol[0] = 0;
3161
3162 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3163 urlComponents.lpszScheme = protocol;
3164 urlComponents.dwSchemeLength = 32;
3165 urlComponents.lpszHostName = hostName;
3166 urlComponents.dwHostNameLength = MAXHOSTNAME;
3167 urlComponents.lpszUserName = userName;
3168 urlComponents.dwUserNameLength = 1024;
3169 urlComponents.lpszPassword = NULL;
3170 urlComponents.dwPasswordLength = 0;
3171 urlComponents.lpszUrlPath = path;
3172 urlComponents.dwUrlPathLength = 2048;
3173 urlComponents.lpszExtraInfo = NULL;
3174 urlComponents.dwExtraInfoLength = 0;
3175 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3176 return INTERNET_GetLastError();
3177
3178 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3179 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3180 {
3181 TRACE("redirect from secure page to non-secure page\n");
3182 /* FIXME: warn about from secure redirect to non-secure page */
3183 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3184 }
3185 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3186 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3187 {
3188 TRACE("redirect from non-secure page to secure page\n");
3189 /* FIXME: notify about redirect to secure page */
3190 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3191 }
3192
3193 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3194 {
3195 if (lstrlenW(protocol)>4) /*https*/
3196 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3197 else /*http*/
3198 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3199 }
3200
3201 #if 0
3202 /*
3203 * This upsets redirects to binary files on sourceforge.net
3204 * and gives an html page instead of the target file
3205 * Examination of the HTTP request sent by native wininet.dll
3206 * reveals that it doesn't send a referrer in that case.
3207 * Maybe there's a flag that enables this, or maybe a referrer
3208 * shouldn't be added in case of a redirect.
3209 */
3210
3211 /* consider the current host as the referrer */
3212 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3213 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3214 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3215 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3216 #endif
3217
3218 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3219 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3220 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3221 {
3222 int len;
3223 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3224 len = lstrlenW(hostName);
3225 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3226 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3227 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3228 }
3229 else
3230 lpwhs->lpszHostName = heap_strdupW(hostName);
3231
3232 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3233
3234 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3235 lpwhs->lpszUserName = NULL;
3236 if (userName[0])
3237 lpwhs->lpszUserName = heap_strdupW(userName);
3238
3239 if (!using_proxy)
3240 {
3241 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3242 {
3243 DWORD res;
3244
3245 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3246 lpwhs->lpszServerName = heap_strdupW(hostName);
3247 lpwhs->nServerPort = urlComponents.nPort;
3248
3249 NETCON_close(&lpwhr->netConnection);
3250 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS)
3251 return res;
3252
3253 res = NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE);
3254 if (res != ERROR_SUCCESS)
3255 return res;
3256
3257 lpwhr->read_pos = lpwhr->read_size = 0;
3258 lpwhr->read_chunked = FALSE;
3259 }
3260 }
3261 else
3262 TRACE("Redirect through proxy\n");
3263 }
3264
3265 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3266 lpwhr->lpszPath=NULL;
3267 if (*path)
3268 {
3269 DWORD needed = 0;
3270 HRESULT rc;
3271
3272 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3273 if (rc != E_POINTER)
3274 needed = strlenW(path)+1;
3275 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3276 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3277 URL_ESCAPE_SPACES_ONLY);
3278 if (rc != S_OK)
3279 {
3280 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3281 strcpyW(lpwhr->lpszPath,path);
3282 }
3283 }
3284
3285 /* Remove custom content-type/length headers on redirects. */
3286 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3287 if (0 <= index)
3288 HTTP_DeleteCustomHeader(lpwhr, index);
3289 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3290 if (0 <= index)
3291 HTTP_DeleteCustomHeader(lpwhr, index);
3292
3293 return ERROR_SUCCESS;
3294 }
3295
3296 /***********************************************************************
3297 * HTTP_build_req (internal)
3298 *
3299 * concatenate all the strings in the request together
3300 */
3301 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3302 {
3303 LPCWSTR *t;
3304 LPWSTR str;
3305
3306 for( t = list; *t ; t++ )
3307 len += strlenW( *t );
3308 len++;
3309
3310 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3311 *str = 0;
3312
3313 for( t = list; *t ; t++ )
3314 strcatW( str, *t );
3315
3316 return str;
3317 }
3318
3319 static DWORD HTTP_SecureProxyConnect(http_request_t *lpwhr)
3320 {
3321 LPWSTR lpszPath;
3322 LPWSTR requestString;
3323 INT len;
3324 INT cnt;
3325 INT responseLen;
3326 char *ascii_req;
3327 DWORD res;
3328 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3329 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3330 http_session_t *lpwhs = lpwhr->lpHttpSession;
3331
3332 TRACE("\n");
3333
3334 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3335 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3336 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3337 HeapFree( GetProcessHeap(), 0, lpszPath );
3338
3339 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3340 NULL, 0, NULL, NULL );
3341 len--; /* the nul terminator isn't needed */
3342 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3343 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3344 ascii_req, len, NULL, NULL );
3345 HeapFree( GetProcessHeap(), 0, requestString );
3346
3347 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3348
3349 res = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3350 HeapFree( GetProcessHeap(), 0, ascii_req );
3351 if (res != ERROR_SUCCESS)
3352 return res;
3353
3354 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3355 if (!responseLen)
3356 return ERROR_HTTP_INVALID_HEADER;
3357
3358 return ERROR_SUCCESS;
3359 }
3360
3361 static void HTTP_InsertCookies(http_request_t *lpwhr)
3362 {
3363 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3364 LPWSTR lpszCookies, lpszUrl = NULL;
3365 DWORD nCookieSize, size;
3366 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3367
3368 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3369 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3370 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3371
3372 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3373 {
3374 int cnt = 0;
3375 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3376
3377 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3378 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3379 {
3380 cnt += sprintfW(lpszCookies, szCookie);
3381 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3382 strcatW(lpszCookies, szCrLf);
3383
3384 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3385 HeapFree(GetProcessHeap(), 0, lpszCookies);
3386 }
3387 }
3388 HeapFree(GetProcessHeap(), 0, lpszUrl);
3389 }
3390
3391 /***********************************************************************
3392 * HTTP_HttpSendRequestW (internal)
3393 *
3394 * Sends the specified request to the HTTP server
3395 *
3396 * RETURNS
3397 * TRUE on success
3398 * FALSE on failure
3399 *
3400 */
3401 static DWORD HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3402 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3403 DWORD dwContentLength, BOOL bEndRequest)
3404 {
3405 INT cnt;
3406 BOOL redirected = FALSE;
3407 LPWSTR requestString = NULL;
3408 INT responseLen;
3409 BOOL loop_next;
3410 INTERNET_ASYNC_RESULT iar;
3411 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3412 static const WCHAR szContentLength[] =
3413 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3414 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3415 DWORD res;
3416
3417 TRACE("--> %p\n", lpwhr);
3418
3419 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3420
3421 /* if the verb is NULL default to GET */
3422 if (!lpwhr->lpszVerb)
3423 lpwhr->lpszVerb = heap_strdupW(szGET);
3424
3425 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3426 {
3427 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3428 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3429 lpwhr->dwBytesToWrite = dwContentLength;
3430 }
3431 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3432 {
3433 WCHAR *agent_header;
3434 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3435 int len;
3436
3437 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3438 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3439 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3440
3441 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3442 HeapFree(GetProcessHeap(), 0, agent_header);
3443 }
3444 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3445 {
3446 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3447 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3448 }
3449 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3450 {
3451 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3452 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3453 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3454 }
3455
3456 do
3457 {
3458 DWORD len;
3459 char *ascii_req;
3460
3461 loop_next = FALSE;
3462
3463 /* like native, just in case the caller forgot to call InternetReadFile
3464 * for all the data */
3465 HTTP_DrainContent(lpwhr);
3466 lpwhr->dwContentRead = 0;
3467
3468 if (TRACE_ON(wininet))
3469 {
3470 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3471 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3472 }
3473
3474 HTTP_FixURL(lpwhr);
3475 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3476 {
3477 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3478 }
3479 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3480 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3481
3482 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3483 HTTP_InsertCookies(lpwhr);
3484
3485 /* add the headers the caller supplied */
3486 if( lpszHeaders && dwHeaderLength )
3487 {
3488 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3489 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3490 }
3491
3492 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3493 {
3494 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3495 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3496 HeapFree(GetProcessHeap(), 0, url);
3497 }
3498 else
3499 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3500
3501
3502 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3503
3504 /* Send the request and store the results */
3505 if ((res = HTTP_OpenConnection(lpwhr)) != ERROR_SUCCESS)
3506 goto lend;
3507
3508 /* send the request as ASCII, tack on the optional data */
3509 if (!lpOptional || redirected)
3510 dwOptionalLength = 0;
3511 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3512 NULL, 0, NULL, NULL );
3513 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3514 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3515 ascii_req, len, NULL, NULL );
3516 if( lpOptional )
3517 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3518 len = (len + dwOptionalLength - 1);
3519 ascii_req[len] = 0;
3520 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3521
3522 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3523 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3524
3525 res = NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3526 HeapFree( GetProcessHeap(), 0, ascii_req );
3527
3528 lpwhr->dwBytesWritten = dwOptionalLength;
3529
3530 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3531 INTERNET_STATUS_REQUEST_SENT,
3532 &len, sizeof(DWORD));
3533
3534 if (bEndRequest)
3535 {
3536 DWORD dwBufferSize;
3537 DWORD dwStatusCode;
3538
3539 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3540 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3541
3542 if (res != ERROR_SUCCESS)
3543 goto lend;
3544
3545 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3546
3547 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3548 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3549 sizeof(DWORD));
3550
3551 HTTP_ProcessCookies(lpwhr);
3552
3553 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3554
3555 dwBufferSize = sizeof(dwStatusCode);
3556 if (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3557 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
3558 dwStatusCode = 0;
3559
3560 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
3561 {
3562 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3563 dwBufferSize=sizeof(szNewLocation);
3564 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
3565 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
3566 {
3567 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3568 {
3569 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3570 lpwhr->lpszVerb = heap_strdupW(szGET);
3571 }
3572 HTTP_DrainContent(lpwhr);
3573 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3574 {
3575 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3576 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3577 res = HTTP_HandleRedirect(lpwhr, new_url);
3578 if (res == ERROR_SUCCESS)
3579 {
3580 HeapFree(GetProcessHeap(), 0, requestString);
3581 loop_next = TRUE;
3582 }
3583 HeapFree( GetProcessHeap(), 0, new_url );
3584 }
3585 redirected = TRUE;
3586 }
3587 }
3588 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
3589 {
3590 WCHAR szAuthValue[2048];
3591 dwBufferSize=2048;
3592 if (dwStatusCode == HTTP_STATUS_DENIED)
3593 {
3594 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3595 DWORD dwIndex = 0;
3596 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3597 {
3598 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3599 &lpwhr->pAuthInfo,
3600 lpwhr->lpHttpSession->lpszUserName,
3601 lpwhr->lpHttpSession->lpszPassword,
3602 Host->lpszValue))
3603 {
3604 HeapFree(GetProcessHeap(), 0, requestString);
3605 loop_next = TRUE;
3606 break;
3607 }
3608 }
3609 }
3610 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3611 {
3612 DWORD dwIndex = 0;
3613 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3614 {
3615 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3616 &lpwhr->pProxyAuthInfo,
3617 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3618 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
3619 NULL))
3620 {
3621 loop_next = TRUE;
3622 break;
3623 }
3624 }
3625 }
3626 }
3627 }
3628 else
3629 res = ERROR_SUCCESS;
3630 }
3631 while (loop_next);
3632
3633 if(res == ERROR_SUCCESS) {
3634 WCHAR url[INTERNET_MAX_URL_LENGTH];
3635 WCHAR cacheFileName[MAX_PATH+1];
3636 BOOL b;
3637
3638 b = HTTP_GetRequestURL(lpwhr, url);
3639 if(!b) {
3640 WARN("Could not get URL\n");
3641 goto lend;
3642 }
3643
3644 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3645 if(b) {
3646 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
3647 CloseHandle(lpwhr->hCacheFile);
3648
3649 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3650 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3651 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3652 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3653 WARN("Could not create file: %u\n", GetLastError());
3654 lpwhr->hCacheFile = NULL;
3655 }
3656 }else {
3657 WARN("Could not create cache entry: %08x\n", GetLastError());
3658 }
3659 }
3660
3661 lend:
3662
3663 HeapFree(GetProcessHeap(), 0, requestString);
3664
3665 /* TODO: send notification for P3P header */
3666
3667 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3668 {
3669 if (res == ERROR_SUCCESS && lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite)
3670 HTTP_ReceiveRequestData(lpwhr, TRUE);
3671 else
3672 {
3673 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3674 iar.dwError = res;
3675
3676 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3677 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3678 sizeof(INTERNET_ASYNC_RESULT));
3679 }
3680 }
3681
3682 TRACE("<--\n");
3683 return res;
3684 }
3685
3686 /***********************************************************************
3687 *
3688 * Helper functions for the HttpSendRequest(Ex) functions
3689 *
3690 */
3691 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
3692 {
3693 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
3694 http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
3695
3696 TRACE("%p\n", lpwhr);
3697
3698 HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
3699 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
3700 req->dwContentLength, req->bEndRequest);
3701
3702 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
3703 }
3704
3705
3706 static DWORD HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
3707 {
3708 INT responseLen;
3709 DWORD dwBufferSize;
3710 INTERNET_ASYNC_RESULT iar;
3711 DWORD res = ERROR_SUCCESS;
3712
3713 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3714 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3715
3716 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3717 if (!responseLen)
3718 res = ERROR_HTTP_HEADER_NOT_FOUND;
3719
3720 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3721 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
3722
3723 /* process cookies here. Is this right? */
3724 HTTP_ProcessCookies(lpwhr);
3725
3726 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3727
3728 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
3729 {
3730 DWORD dwCode,dwCodeLength = sizeof(DWORD);
3731 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
3732 && (dwCode == 302 || dwCode == 301 || dwCode == 303))
3733 {
3734 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3735 dwBufferSize=sizeof(szNewLocation);
3736 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
3737 {
3738 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3739 {
3740 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3741 lpwhr->lpszVerb = heap_strdupW(szGET);
3742 }
3743 HTTP_DrainContent(lpwhr);
3744 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3745 {
3746 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3747 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3748 res = HTTP_HandleRedirect(lpwhr, new_url);
3749 if (res == ERROR_SUCCESS)
3750 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
3751 HeapFree( GetProcessHeap(), 0, new_url );
3752 }
3753 }
3754 }
3755 }
3756
3757 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3758 iar.dwError = res;
3759
3760 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3761 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3762 sizeof(INTERNET_ASYNC_RESULT));
3763 return res;
3764 }
3765
3766 /***********************************************************************
3767 * HttpEndRequestA (WININET.@)
3768 *
3769 * Ends an HTTP request that was started by HttpSendRequestEx
3770 *
3771 * RETURNS
3772 * TRUE if successful
3773 * FALSE on failure
3774 *
3775 */
3776 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
3777 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3778 {
3779 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
3780
3781 if (lpBuffersOut)
3782 {
3783 SetLastError(ERROR_INVALID_PARAMETER);
3784 return FALSE;
3785 }
3786
3787 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
3788 }
3789
3790 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
3791 {
3792 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
3793 http_request_t *lpwhr = (http_request_t*)work->hdr;
3794
3795 TRACE("%p\n", lpwhr);
3796
3797 HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
3798 }
3799
3800 /***********************************************************************
3801 * HttpEndRequestW (WININET.@)
3802 *
3803 * Ends an HTTP request that was started by HttpSendRequestEx
3804 *
3805 * RETURNS
3806 * TRUE if successful
3807 * FALSE on failure
3808 *
3809 */
3810 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
3811 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3812 {
3813 http_request_t *lpwhr;
3814 DWORD res;
3815
3816 TRACE("-->\n");
3817
3818 if (lpBuffersOut)
3819 {
3820 SetLastError(ERROR_INVALID_PARAMETER);
3821 return FALSE;
3822 }
3823
3824 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3825
3826 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3827 {
3828 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3829 if (lpwhr)
3830 WININET_Release( &lpwhr->hdr );
3831 return FALSE;
3832 }
3833 lpwhr->hdr.dwFlags |= dwFlags;
3834
3835 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3836 {
3837 WORKREQUEST work;
3838 struct WORKREQ_HTTPENDREQUESTW *request;
3839
3840 work.asyncproc = AsyncHttpEndRequestProc;
3841 work.hdr = WININET_AddRef( &lpwhr->hdr );
3842
3843 request = &work.u.HttpEndRequestW;
3844 request->dwFlags = dwFlags;
3845 request->dwContext = dwContext;
3846
3847 INTERNET_AsyncCall(&work);
3848 res = ERROR_IO_PENDING;
3849 }
3850 else
3851 res = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
3852
3853 WININET_Release( &lpwhr->hdr );
3854 TRACE("%u <--\n", res);
3855 if(res != ERROR_SUCCESS)
3856 SetLastError(res);
3857 return res == ERROR_SUCCESS;
3858 }
3859
3860 /***********************************************************************
3861 * HttpSendRequestExA (WININET.@)
3862 *
3863 * Sends the specified request to the HTTP server and allows chunked
3864 * transfers.
3865 *
3866 * RETURNS
3867 * Success: TRUE
3868 * Failure: FALSE, call GetLastError() for more information.
3869 */
3870 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3871 LPINTERNET_BUFFERSA lpBuffersIn,
3872 LPINTERNET_BUFFERSA lpBuffersOut,
3873 DWORD dwFlags, DWORD_PTR dwContext)
3874 {
3875 INTERNET_BUFFERSW BuffersInW;
3876 BOOL rc = FALSE;
3877 DWORD headerlen;
3878 LPWSTR header = NULL;
3879
3880 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3881 lpBuffersOut, dwFlags, dwContext);
3882
3883 if (lpBuffersIn)
3884 {
3885 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3886 if (lpBuffersIn->lpcszHeader)
3887 {
3888 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3889 lpBuffersIn->dwHeadersLength,0,0);
3890 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3891 if (!(BuffersInW.lpcszHeader = header))
3892 {
3893 SetLastError(ERROR_OUTOFMEMORY);
3894 return FALSE;
3895 }
3896 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3897 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3898 header, headerlen);
3899 }
3900 else
3901 BuffersInW.lpcszHeader = NULL;
3902 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3903 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3904 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3905 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3906 BuffersInW.Next = NULL;
3907 }
3908
3909 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3910
3911 HeapFree(GetProcessHeap(),0,header);
3912
3913 return rc;
3914 }
3915
3916 /***********************************************************************
3917 * HttpSendRequestExW (WININET.@)
3918 *
3919 * Sends the specified request to the HTTP server and allows chunked
3920 * transfers
3921 *
3922 * RETURNS
3923 * Success: TRUE
3924 * Failure: FALSE, call GetLastError() for more information.
3925 */
3926 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3927 LPINTERNET_BUFFERSW lpBuffersIn,
3928 LPINTERNET_BUFFERSW lpBuffersOut,
3929 DWORD dwFlags, DWORD_PTR dwContext)
3930 {
3931 http_request_t *lpwhr;
3932 http_session_t *lpwhs;
3933 appinfo_t *hIC;
3934 DWORD res;
3935
3936 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3937 lpBuffersOut, dwFlags, dwContext);
3938
3939 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3940
3941 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3942 {
3943 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3944 goto lend;
3945 }
3946
3947 lpwhs = lpwhr->lpHttpSession;
3948 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3949 hIC = lpwhs->lpAppInfo;
3950 assert(hIC->hdr.htype == WH_HINIT);
3951
3952 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3953 {
3954 WORKREQUEST workRequest;
3955 struct WORKREQ_HTTPSENDREQUESTW *req;
3956
3957 workRequest.asyncproc = AsyncHttpSendRequestProc;
3958 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3959 req = &workRequest.u.HttpSendRequestW;
3960 if (lpBuffersIn)
3961 {
3962 DWORD size = 0;
3963
3964 if (lpBuffersIn->lpcszHeader)
3965 {
3966 if (lpBuffersIn->dwHeadersLength == ~0u)
3967 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
3968 else
3969 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
3970
3971 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
3972 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
3973 }
3974 else req->lpszHeader = NULL;
3975
3976 req->dwHeaderLength = size / sizeof(WCHAR);
3977 req->lpOptional = lpBuffersIn->lpvBuffer;
3978 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3979 req->dwContentLength = lpBuffersIn->dwBufferTotal;
3980 }
3981 else
3982 {
3983 req->lpszHeader = NULL;
3984 req->dwHeaderLength = 0;
3985 req->lpOptional = NULL;
3986 req->dwOptionalLength = 0;
3987 req->dwContentLength = 0;
3988 }
3989
3990 req->bEndRequest = FALSE;
3991
3992 INTERNET_AsyncCall(&workRequest);
3993 /*
3994 * This is from windows.
3995 */
3996 res = ERROR_IO_PENDING;
3997 }
3998 else
3999 {
4000 if (lpBuffersIn)
4001 res = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4002 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
4003 lpBuffersIn->dwBufferTotal, FALSE);
4004 else
4005 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
4006 }
4007
4008 lend:
4009 if ( lpwhr )
4010 WININET_Release( &lpwhr->hdr );
4011
4012 TRACE("<---\n");
4013 SetLastError(res);
4014 return res == ERROR_SUCCESS;
4015 }
4016
4017 /***********************************************************************
4018 * HttpSendRequestW (WININET.@)
4019 *
4020 * Sends the specified request to the HTTP server
4021 *
4022 * RETURNS
4023 * TRUE on success
4024 * FALSE on failure
4025 *
4026 */
4027 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4028 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4029 {
4030 http_request_t *lpwhr;
4031 http_session_t *lpwhs = NULL;
4032 appinfo_t *hIC = NULL;
4033 DWORD res = ERROR_SUCCESS;
4034
4035 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4036 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4037
4038 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
4039 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4040 {
4041 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4042 goto lend;
4043 }
4044
4045 lpwhs = lpwhr->lpHttpSession;
4046 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
4047 {
4048 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4049 goto lend;
4050 }
4051
4052 hIC = lpwhs->lpAppInfo;
4053 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
4054 {
4055 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4056 goto lend;
4057 }
4058
4059 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4060 {
4061 WORKREQUEST workRequest;
4062 struct WORKREQ_HTTPSENDREQUESTW *req;
4063
4064 workRequest.asyncproc = AsyncHttpSendRequestProc;
4065 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4066 req = &workRequest.u.HttpSendRequestW;
4067 if (lpszHeaders)
4068 {
4069 DWORD size;
4070
4071 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4072 else size = dwHeaderLength * sizeof(WCHAR);
4073
4074 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4075 memcpy(req->lpszHeader, lpszHeaders, size);
4076 }
4077 else
4078 req->lpszHeader = 0;
4079 req->dwHeaderLength = dwHeaderLength;
4080 req->lpOptional = lpOptional;
4081 req->dwOptionalLength = dwOptionalLength;
4082 req->dwContentLength = dwOptionalLength;
4083 req->bEndRequest = TRUE;
4084
4085 INTERNET_AsyncCall(&workRequest);
4086 /*
4087 * This is from windows.
4088 */
4089 res = ERROR_IO_PENDING;
4090 }
4091 else
4092 {
4093 res = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
4094 dwHeaderLength, lpOptional, dwOptionalLength,
4095 dwOptionalLength, TRUE);
4096 }
4097 lend:
4098 if( lpwhr )
4099 WININET_Release( &lpwhr->hdr );
4100
4101 SetLastError(res);
4102 return res == ERROR_SUCCESS;
4103 }
4104
4105 /***********************************************************************
4106 * HttpSendRequestA (WININET.@)
4107 *
4108 * Sends the specified request to the HTTP server
4109 *
4110 * RETURNS
4111 * TRUE on success
4112 * FALSE on failure
4113 *
4114 */
4115 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4116 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4117 {
4118 BOOL result;
4119 LPWSTR szHeaders=NULL;
4120 DWORD nLen=dwHeaderLength;
4121 if(lpszHeaders!=NULL)
4122 {
4123 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4124 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4125 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4126 }
4127 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4128 HeapFree(GetProcessHeap(),0,szHeaders);
4129 return result;
4130 }
4131
4132 /***********************************************************************
4133 * HTTPSESSION_Destroy (internal)
4134 *
4135 * Deallocate session handle
4136 *
4137 */
4138 static void HTTPSESSION_Destroy(object_header_t *hdr)
4139 {
4140 http_session_t *lpwhs = (http_session_t*) hdr;
4141
4142 TRACE("%p\n", lpwhs);
4143
4144 WININET_Release(&lpwhs->lpAppInfo->hdr);
4145
4146 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4147 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4148 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4149 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4150 HeapFree(GetProcessHeap(), 0, lpwhs);
4151 }
4152
4153 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4154 {
4155 switch(option) {
4156 case INTERNET_OPTION_HANDLE_TYPE:
4157 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4158
4159 if (*size < sizeof(ULONG))
4160 return ERROR_INSUFFICIENT_BUFFER;
4161
4162 *size = sizeof(DWORD);
4163 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4164 return ERROR_SUCCESS;
4165 }
4166
4167 return INET_QueryOption(option, buffer, size, unicode);
4168 }
4169
4170 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4171 {
4172 http_session_t *ses = (http_session_t*)hdr;
4173
4174 switch(option) {
4175 case INTERNET_OPTION_USERNAME:
4176 {
4177 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4178 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4179 return ERROR_SUCCESS;
4180 }
4181 case INTERNET_OPTION_PASSWORD:
4182 {
4183 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4184 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4185 return ERROR_SUCCESS;
4186 }
4187 default: break;
4188 }
4189
4190 return ERROR_INTERNET_INVALID_OPTION;
4191 }
4192
4193 static const object_vtbl_t HTTPSESSIONVtbl = {
4194 HTTPSESSION_Destroy,
4195 NULL,
4196 HTTPSESSION_QueryOption,
4197 HTTPSESSION_SetOption,
4198 NULL,
4199 NULL,
4200 NULL,
4201 NULL,
4202 NULL
4203 };
4204
4205
4206 /***********************************************************************
4207 * HTTP_Connect (internal)
4208 *
4209 * Create http session handle
4210 *
4211 * RETURNS
4212 * HINTERNET a session handle on success
4213 * NULL on failure
4214 *
4215 */
4216 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4217 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4218 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4219 DWORD dwInternalFlags, HINTERNET *ret)
4220 {
4221 http_session_t *lpwhs = NULL;
4222 HINTERNET handle = NULL;
4223 DWORD res = ERROR_SUCCESS;
4224
4225 TRACE("-->\n");
4226
4227 if (!lpszServerName || !lpszServerName[0])
4228 return ERROR_INVALID_PARAMETER;
4229
4230 assert( hIC->hdr.htype == WH_HINIT );
4231
4232 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4233 if (!lpwhs)
4234 return ERROR_OUTOFMEMORY;
4235
4236 /*
4237 * According to my tests. The name is not resolved until a request is sent
4238 */
4239
4240 lpwhs->hdr.htype = WH_HHTTPSESSION;
4241 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4242 lpwhs->hdr.dwFlags = dwFlags;
4243 lpwhs->hdr.dwContext = dwContext;
4244 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4245 lpwhs->hdr.refs = 1;
4246 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4247
4248 WININET_AddRef( &hIC->hdr );
4249 lpwhs->lpAppInfo = hIC;
4250 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4251
4252 handle = WININET_AllocHandle( &lpwhs->hdr );
4253 if (NULL == handle)
4254 {
4255 ERR("Failed to alloc handle\n");
4256 res = ERROR_OUTOFMEMORY;
4257 goto lerror;
4258 }
4259
4260 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4261 if(hIC->lpszProxyBypass)
4262 FIXME("Proxy bypass is ignored.\n");
4263 }
4264 if (lpszServerName && lpszServerName[0])
4265 {
4266 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4267 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4268 }
4269 if (lpszUserName && lpszUserName[0])
4270 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4271 if (lpszPassword && lpszPassword[0])
4272 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4273 lpwhs->nServerPort = nServerPort;
4274 lpwhs->nHostPort = nServerPort;
4275
4276 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4277 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4278 {
4279 INTERNET_SendCallback(&hIC->hdr, dwContext,
4280 INTERNET_STATUS_HANDLE_CREATED, &handle,
4281 sizeof(handle));
4282 }
4283
4284 lerror:
4285 if( lpwhs )
4286 WININET_Release( &lpwhs->hdr );
4287
4288 /*
4289 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4290 * windows
4291 */
4292
4293 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4294
4295 if(res == ERROR_SUCCESS)
4296 *ret = handle;
4297 return res;
4298 }
4299
4300
4301 /***********************************************************************
4302 * HTTP_OpenConnection (internal)
4303 *
4304 * Connect to a web server
4305 *
4306 * RETURNS
4307 *
4308 * TRUE on success
4309 * FALSE on failure
4310 */
4311 static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
4312 {
4313 http_session_t *lpwhs;
4314 appinfo_t *hIC = NULL;
4315 char szaddr[INET6_ADDRSTRLEN];
4316 const void *addr;
4317 DWORD res = ERROR_SUCCESS;
4318
4319 TRACE("-->\n");
4320
4321
4322 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4323 {
4324 res = ERROR_INVALID_PARAMETER;
4325 goto lend;
4326 }
4327
4328 if (NETCON_connected(&lpwhr->netConnection))
4329 goto lend;
4330 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
4331
4332 lpwhs = lpwhr->lpHttpSession;
4333
4334 hIC = lpwhs->lpAppInfo;
4335 switch (lpwhs->socketAddress.ss_family)
4336 {
4337 case AF_INET:
4338 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4339 break;
4340 case AF_INET6:
4341 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4342 break;
4343 default:
4344 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4345 return ERROR_INTERNET_NAME_NOT_RESOLVED;
4346 }
4347 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4348 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4349 INTERNET_STATUS_CONNECTING_TO_SERVER,
4350 szaddr,
4351 strlen(szaddr)+1);
4352
4353 res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
4354 if (res != ERROR_SUCCESS)
4355 {
4356 WARN("Socket creation failed: %u\n", res);
4357 goto lend;
4358 }
4359
4360 res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4361 lpwhs->sa_len);
4362 if(res != ERROR_SUCCESS)
4363 goto lend;
4364
4365 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4366 {
4367 /* Note: we differ from Microsoft's WinINet here. they seem to have
4368 * a bug that causes no status callbacks to be sent when starting
4369 * a tunnel to a proxy server using the CONNECT verb. i believe our
4370 * behaviour to be more correct and to not cause any incompatibilities
4371 * because using a secure connection through a proxy server is a rare
4372 * case that would be hard for anyone to depend on */
4373 if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS)
4374 goto lend;
4375
4376 res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
4377 if(res != ERROR_SUCCESS)
4378 {
4379 WARN("Couldn't connect securely to host\n");
4380 goto lend;
4381 }
4382 }
4383
4384 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4385 INTERNET_STATUS_CONNECTED_TO_SERVER,
4386 szaddr, strlen(szaddr)+1);
4387
4388 lend:
4389 lpwhr->read_pos = lpwhr->read_size = 0;
4390 lpwhr->read_chunked = FALSE;
4391
4392 TRACE("%d <--\n", res);
4393 return res;
4394 }
4395
4396
4397 /***********************************************************************
4398 * HTTP_clear_response_headers (internal)
4399 *
4400 * clear out any old response headers
4401 */
4402 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4403 {
4404 DWORD i;
4405
4406 for( i=0; i<lpwhr->nCustHeaders; i++)
4407 {
4408 if( !lpwhr->pCustHeaders[i].lpszField )
4409 continue;
4410 if( !lpwhr->pCustHeaders[i].lpszValue )
4411 continue;
4412 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4413 continue;
4414 HTTP_DeleteCustomHeader( lpwhr, i );
4415 i--;
4416 }
4417 }
4418
4419 /***********************************************************************
4420 * HTTP_GetResponseHeaders (internal)
4421 *
4422 * Read server response
4423 *
4424 * RETURNS
4425 *
4426 * TRUE on success
4427 * FALSE on error
4428 */
4429 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4430 {
4431 INT cbreaks = 0;
4432 WCHAR buffer[MAX_REPLY_LEN];
4433 DWORD buflen = MAX_REPLY_LEN;
4434 BOOL bSuccess = FALSE;
4435 INT rc = 0;
4436 char bufferA[MAX_REPLY_LEN];
4437 LPWSTR status_code = NULL, status_text = NULL;
4438 DWORD cchMaxRawHeaders = 1024;
4439 LPWSTR lpszRawHeaders = NULL;
4440 LPWSTR temp;
4441 DWORD cchRawHeaders = 0;
4442 BOOL codeHundred = FALSE;
4443
4444 TRACE("-->\n");
4445
4446 /* clear old response headers (eg. from a redirect response) */
4447 if (clear) HTTP_clear_response_headers( lpwhr );
4448
4449 if (!NETCON_connected(&lpwhr->netConnection))
4450 goto lend;
4451
4452 do {
4453 static const WCHAR szHundred[] = {'1','0','0',0};
4454 /*
4455 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4456 */
4457 buflen = MAX_REPLY_LEN;
4458 if (!read_line(lpwhr, bufferA, &buflen))
4459 goto lend;
4460 rc += buflen;
4461 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4462 /* check is this a status code line? */
4463 if (!strncmpW(buffer, g_szHttp1_0, 4))
4464 {
4465 /* split the version from the status code */
4466 status_code = strchrW( buffer, ' ' );
4467 if( !status_code )
4468 goto lend;
4469 *status_code++=0;
4470
4471 /* split the status code from the status text */
4472 status_text = strchrW( status_code, ' ' );
4473 if( !status_text )
4474 goto lend;
4475 *status_text++=0;
4476
4477 TRACE("version [%s] status code [%s] status text [%s]\n",
4478 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4479
4480 codeHundred = (!strcmpW(status_code, szHundred));
4481 }
4482 else if (!codeHundred)
4483 {
4484 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
4485
4486 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
4487 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
4488
4489 lpwhr->lpszVersion = heap_strdupW(g_szHttp1_0);
4490 lpwhr->lpszStatusText = heap_strdupW(szOK);
4491
4492 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4493 lpwhr->lpszRawHeaders = heap_strdupW(szDefaultHeader);
4494
4495 bSuccess = TRUE;
4496 goto lend;
4497 }
4498 } while (codeHundred);
4499
4500 /* Add status code */
4501 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4502 HTTP_ADDHDR_FLAG_REPLACE);
4503
4504 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4505 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4506
4507 lpwhr->lpszVersion = heap_strdupW(buffer);
4508 lpwhr->lpszStatusText = heap_strdupW(status_text);
4509
4510 /* Restore the spaces */
4511 *(status_code-1) = ' ';
4512 *(status_text-1) = ' ';
4513
4514 /* regenerate raw headers */
4515 lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4516 if (!lpszRawHeaders) goto lend;
4517
4518 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4519 cchMaxRawHeaders *= 2;
4520 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4521 if (temp == NULL) goto lend;
4522 lpszRawHeaders = temp;
4523 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4524 cchRawHeaders += (buflen-1);
4525 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4526 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4527 lpszRawHeaders[cchRawHeaders] = '\0';
4528
4529 /* Parse each response line */
4530 do
4531 {
4532 buflen = MAX_REPLY_LEN;
4533 if (read_line(lpwhr, bufferA, &buflen))
4534 {
4535 LPWSTR * pFieldAndValue;
4536
4537 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4538
4539 if (!bufferA[0]) break;
4540 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4541
4542 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4543 if (pFieldAndValue)
4544 {
4545 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4546 cchMaxRawHeaders *= 2;
4547 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4548 if (temp == NULL) goto lend;
4549 lpszRawHeaders = temp;
4550 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4551 cchRawHeaders += (buflen-1);
4552 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4553 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4554 lpszRawHeaders[cchRawHeaders] = '\0';
4555
4556 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4557 HTTP_ADDREQ_FLAG_ADD );
4558
4559 HTTP_FreeTokens(pFieldAndValue);
4560 }
4561 }
4562 else
4563 {
4564 cbreaks++;
4565 if (cbreaks >= 2)
4566 break;
4567 }
4568 }while(1);
4569
4570 /* make sure the response header is terminated with an empty line. Some apps really
4571 truly care about that empty line being there for some reason. Just add it to the
4572 header. */
4573 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4574 {
4575 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4576 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4577 if (temp == NULL) goto lend;
4578 lpszRawHeaders = temp;
4579 }
4580
4581 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4582
4583 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4584 lpwhr->lpszRawHeaders = lpszRawHeaders;
4585 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4586 bSuccess = TRUE;
4587
4588 lend:
4589
4590 TRACE("<--\n");
4591 if (bSuccess)
4592 return rc;
4593 else
4594 {
4595 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4596 return 0;
4597 }
4598 }
4599
4600 /***********************************************************************
4601 * HTTP_InterpretHttpHeader (internal)
4602 *
4603 * Parse server response
4604 *
4605 * RETURNS
4606 *
4607 * Pointer to array of field, value, NULL on success.
4608 * NULL on error.
4609 */
4610 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4611 {
4612 LPWSTR * pTokenPair;
4613 LPWSTR pszColon;
4614 INT len;
4615
4616 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4617
4618 pszColon = strchrW(buffer, ':');
4619 /* must have two tokens */
4620 if (!pszColon)
4621 {
4622 HTTP_FreeTokens(pTokenPair);
4623 if (buffer[0])
4624 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4625 return NULL;
4626 }
4627
4628 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4629 if (!pTokenPair[0])
4630 {
4631 HTTP_FreeTokens(pTokenPair);
4632 return NULL;
4633 }
4634 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4635 pTokenPair[0][pszColon - buffer] = '\0';
4636
4637 /* skip colon */
4638 pszColon++;
4639 len = strlenW(pszColon);
4640 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4641 if (!pTokenPair[1])
4642 {
4643 HTTP_FreeTokens(pTokenPair);
4644 return NULL;
4645 }
4646 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4647
4648 strip_spaces(pTokenPair[0]);
4649 strip_spaces(pTokenPair[1]);
4650
4651 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4652 return pTokenPair;
4653 }
4654
4655 /***********************************************************************
4656 * HTTP_ProcessHeader (internal)
4657 *
4658 * Stuff header into header tables according to <dwModifier>
4659 *
4660 */
4661
4662 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4663
4664 static DWORD HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4665 {
4666 LPHTTPHEADERW lphttpHdr = NULL;
4667 INT index = -1;
4668 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4669 DWORD res = ERROR_HTTP_INVALID_HEADER;
4670
4671 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4672
4673 /* REPLACE wins out over ADD */
4674 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4675 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4676
4677 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4678 index = -1;
4679 else
4680 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4681
4682 if (index >= 0)
4683 {
4684 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4685 return ERROR_HTTP_INVALID_HEADER;
4686 lphttpHdr = &lpwhr->pCustHeaders[index];
4687 }
4688 else if (value)
4689 {
4690 HTTPHEADERW hdr;
4691
4692 hdr.lpszField = (LPWSTR)field;
4693 hdr.lpszValue = (LPWSTR)value;
4694 hdr.wFlags = hdr.wCount = 0;
4695
4696 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4697 hdr.wFlags |= HDR_ISREQUEST;
4698
4699 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4700 }
4701 /* no value to delete */
4702 else return ERROR_SUCCESS;
4703
4704 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4705 lphttpHdr->wFlags |= HDR_ISREQUEST;
4706 else
4707 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4708
4709 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4710 {
4711 HTTP_DeleteCustomHeader( lpwhr, index );
4712
4713 if (value)
4714 {
4715 HTTPHEADERW hdr;
4716
4717 hdr.lpszField = (LPWSTR)field;
4718 hdr.lpszValue = (LPWSTR)value;
4719 hdr.wFlags = hdr.wCount = 0;
4720
4721 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4722 hdr.wFlags |= HDR_ISREQUEST;
4723
4724 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4725 }
4726
4727 return ERROR_SUCCESS;
4728 }
4729 else if (dwModifier & COALESCEFLAGS)
4730 {
4731 LPWSTR lpsztmp;
4732 WCHAR ch = 0;
4733 INT len = 0;
4734 INT origlen = strlenW(lphttpHdr->lpszValue);
4735 INT valuelen = strlenW(value);
4736
4737 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4738 {
4739 ch = ',';
4740 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4741 }
4742 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4743 {
4744 ch = ';';
4745 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4746 }
4747
4748 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4749
4750 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4751 if (lpsztmp)
4752 {
4753 lphttpHdr->lpszValue = lpsztmp;
4754 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4755 if (ch > 0)
4756 {
4757 lphttpHdr->lpszValue[origlen] = ch;
4758 origlen++;
4759 lphttpHdr->lpszValue[origlen] = ' ';
4760 origlen++;
4761 }
4762
4763 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4764 lphttpHdr->lpszValue[len] = '\0';
4765 res = ERROR_SUCCESS;
4766 }
4767 else
4768 {
4769 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4770 res = ERROR_OUTOFMEMORY;
4771 }
4772 }
4773 TRACE("<-- %d\n", res);
4774 return res;
4775 }
4776
4777
4778 /***********************************************************************
4779 * HTTP_FinishedReading (internal)
4780 *
4781 * Called when all content from server has been read by client.
4782 *
4783 */
4784 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4785 {
4786 BOOL keepalive = HTTP_KeepAlive(lpwhr);
4787
4788 TRACE("\n");
4789
4790
4791 if (!keepalive)
4792 {
4793 HTTPREQ_CloseConnection(&lpwhr->hdr);
4794 }
4795
4796 /* FIXME: store data in the URL cache here */
4797
4798 return TRUE;
4799 }
4800
4801
4802 /***********************************************************************
4803 * HTTP_GetCustomHeaderIndex (internal)
4804 *
4805 * Return index of custom header from header array
4806 *
4807 */
4808 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
4809 int requested_index, BOOL request_only)
4810 {
4811 DWORD index;
4812
4813 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
4814
4815 for (index = 0; index < lpwhr->nCustHeaders; index++)
4816 {
4817 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
4818 continue;
4819
4820 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4821 continue;
4822
4823 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4824 continue;
4825
4826 if (requested_index == 0)
4827 break;
4828 requested_index --;
4829 }
4830
4831 if (index >= lpwhr->nCustHeaders)
4832 index = -1;
4833
4834 TRACE("Return: %d\n", index);
4835 return index;
4836 }
4837
4838
4839 /***********************************************************************
4840 * HTTP_InsertCustomHeader (internal)
4841 *
4842 * Insert header into array
4843 *
4844 */
4845 static DWORD HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
4846 {
4847 INT count;
4848 LPHTTPHEADERW lph = NULL;
4849
4850 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
4851 count = lpwhr->nCustHeaders + 1;
4852 if (count > 1)
4853 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
4854 else
4855 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
4856
4857 if (!lph)
4858 return ERROR_OUTOFMEMORY;
4859
4860 lpwhr->pCustHeaders = lph;
4861 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
4862 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
4863 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
4864 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
4865 lpwhr->nCustHeaders++;
4866
4867 return ERROR_SUCCESS;
4868 }
4869
4870
4871 /***********************************************************************
4872 * HTTP_DeleteCustomHeader (internal)
4873 *
4874 * Delete header from array
4875 * If this function is called, the indexs may change.
4876 */
4877 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
4878 {
4879 if( lpwhr->nCustHeaders <= 0 )
4880 return FALSE;
4881 if( index >= lpwhr->nCustHeaders )
4882 return FALSE;
4883 lpwhr->nCustHeaders--;
4884
4885 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
4886 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
4887
4888 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
4889 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
4890 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
4891
4892 return TRUE;
4893 }
4894
4895
4896 /***********************************************************************
4897 * HTTP_VerifyValidHeader (internal)
4898 *
4899 * Verify the given header is not invalid for the given http request
4900 *
4901 */
4902 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
4903 {
4904 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4905 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
4906 return ERROR_HTTP_INVALID_HEADER;
4907
4908 return ERROR_SUCCESS;
4909 }
4910
4911 /***********************************************************************
4912 * IsHostInProxyBypassList (@)
4913 *
4914 * Undocumented
4915 *
4916 */
4917 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
4918 {
4919 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
4920 return FALSE;
4921 }