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