dplayx: Code to forward player creation
[wine/gsoc_dplay.git] / dlls / wininet / http.c
blob681658e315d4496836ed4a68a48ba79dc24b7097
1 /*
2 * Wininet - Http Implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2002 CodeWeavers Inc.
6 * Copyright 2002 TransGaming Technologies Inc.
7 * Copyright 2004 Mike McCormack for CodeWeavers
8 * Copyright 2005 Aric Stewart for CodeWeavers
9 * Copyright 2006 Robert Shearman for CodeWeavers
11 * Ulrich Czekalla
12 * David Hammerton
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
32 #if defined(__MINGW32__) || defined (_MSC_VER)
33 #include <ws2tcpip.h>
34 #endif
36 #include <sys/types.h>
37 #ifdef HAVE_SYS_SOCKET_H
38 # include <sys/socket.h>
39 #endif
40 #ifdef HAVE_ARPA_INET_H
41 # include <arpa/inet.h>
42 #endif
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #ifdef HAVE_UNISTD_H
47 # include <unistd.h>
48 #endif
49 #include <time.h>
50 #include <assert.h>
51 #ifdef HAVE_ZLIB
52 # include <zlib.h>
53 #endif
55 #include "windef.h"
56 #include "winbase.h"
57 #include "wininet.h"
58 #include "winerror.h"
59 #define NO_SHLWAPI_STREAM
60 #define NO_SHLWAPI_REG
61 #define NO_SHLWAPI_STRFCNS
62 #define NO_SHLWAPI_GDI
63 #include "shlwapi.h"
64 #include "sspi.h"
65 #include "wincrypt.h"
67 #include "internet.h"
68 #include "wine/debug.h"
69 #include "wine/exception.h"
70 #include "wine/unicode.h"
72 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
74 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
75 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
76 static const WCHAR hostW[] = { 'H','o','s','t',0 };
77 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
78 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
79 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
80 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
81 static const WCHAR szGET[] = { 'G','E','T', 0 };
82 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
83 static const WCHAR szCrLf[] = {'\r','\n', 0};
85 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
86 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
87 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
88 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
89 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
90 static const WCHAR szAge[] = { 'A','g','e',0 };
91 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
92 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
93 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
94 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
95 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
96 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
97 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
98 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
99 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
100 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
101 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
102 static const WCHAR szContent_Transfer_Encoding[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
103 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
104 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
105 static const WCHAR szDate[] = { 'D','a','t','e',0 };
106 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
107 static const WCHAR szETag[] = { 'E','T','a','g',0 };
108 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
109 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
110 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
111 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
112 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
113 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
114 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
115 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
116 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
117 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
118 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
119 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
120 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
121 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
122 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
123 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
124 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
125 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
126 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
127 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
128 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
129 static const WCHAR szUnless_Modified_Since[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
130 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
131 static const WCHAR szURI[] = { 'U','R','I',0 };
132 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
133 static const WCHAR szVary[] = { 'V','a','r','y',0 };
134 static const WCHAR szVia[] = { 'V','i','a',0 };
135 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
136 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
138 #define MAXHOSTNAME 100
139 #define MAX_FIELD_VALUE_LEN 256
140 #define MAX_FIELD_LEN 256
142 #define HTTP_REFERER szReferer
143 #define HTTP_ACCEPT szAccept
144 #define HTTP_USERAGENT szUser_Agent
146 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
147 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
148 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
149 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
150 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
151 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
152 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
154 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
156 struct HttpAuthInfo
158 LPWSTR scheme;
159 CredHandle cred;
160 CtxtHandle ctx;
161 TimeStamp exp;
162 ULONG attr;
163 ULONG max_token;
164 void *auth_data;
165 unsigned int auth_data_len;
166 BOOL finished; /* finished authenticating */
170 struct gzip_stream_t {
171 #ifdef HAVE_ZLIB
172 z_stream zstream;
173 #endif
174 BYTE buf[8192];
175 DWORD buf_size;
176 DWORD buf_pos;
177 BOOL end_of_data;
180 static BOOL HTTP_OpenConnection(http_request_t *req);
181 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
182 static BOOL HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
183 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
184 static BOOL HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
185 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
186 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
187 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
188 static BOOL HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
189 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
190 static BOOL HTTP_HandleRedirect(http_request_t *req, LPCWSTR lpszUrl);
191 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
192 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
193 static void HTTP_DrainContent(http_request_t *req);
194 static BOOL HTTP_FinishedReading(http_request_t *req);
196 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
198 int HeaderIndex = 0;
199 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
200 if (HeaderIndex == -1)
201 return NULL;
202 else
203 return &req->pCustHeaders[HeaderIndex];
206 #ifdef HAVE_ZLIB
208 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
210 return HeapAlloc(GetProcessHeap(), 0, items*size);
213 static void wininet_zfree(voidpf opaque, voidpf address)
215 HeapFree(GetProcessHeap(), 0, address);
218 static void init_gzip_stream(http_request_t *req)
220 gzip_stream_t *gzip_stream;
221 int index, zres;
223 gzip_stream = HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t));
224 gzip_stream->zstream.zalloc = wininet_zalloc;
225 gzip_stream->zstream.zfree = wininet_zfree;
226 gzip_stream->zstream.opaque = NULL;
227 gzip_stream->zstream.next_in = NULL;
228 gzip_stream->zstream.avail_in = 0;
229 gzip_stream->zstream.next_out = NULL;
230 gzip_stream->zstream.avail_out = 0;
231 gzip_stream->buf_pos = 0;
232 gzip_stream->buf_size = 0;
233 gzip_stream->end_of_data = FALSE;
235 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
236 if(zres != Z_OK) {
237 ERR("inflateInit failed: %d\n", zres);
238 HeapFree(GetProcessHeap(), 0, gzip_stream);
239 return;
242 req->gzip_stream = gzip_stream;
244 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
245 if(index != -1)
246 HTTP_DeleteCustomHeader(req, index);
249 #else
251 static void init_gzip_stream(http_request_t *req)
253 ERR("gzip stream not supported, missing zlib.\n");
256 #endif
258 /* set the request content length based on the headers */
259 static DWORD set_content_length( http_request_t *lpwhr )
261 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
262 WCHAR encoding[20];
263 DWORD size;
265 size = sizeof(lpwhr->dwContentLength);
266 if (!HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
267 &lpwhr->dwContentLength, &size, NULL))
268 lpwhr->dwContentLength = ~0u;
270 size = sizeof(encoding);
271 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) &&
272 !strcmpiW(encoding, szChunked))
274 lpwhr->dwContentLength = ~0u;
275 lpwhr->read_chunked = TRUE;
278 if(lpwhr->decoding) {
279 int encoding_idx;
281 static const WCHAR gzipW[] = {'g','z','i','p',0};
283 encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
284 if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
285 init_gzip_stream(lpwhr);
288 return lpwhr->dwContentLength;
291 /***********************************************************************
292 * HTTP_Tokenize (internal)
294 * Tokenize a string, allocating memory for the tokens.
296 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
298 LPWSTR * token_array;
299 int tokens = 0;
300 int i;
301 LPCWSTR next_token;
303 if (string)
305 /* empty string has no tokens */
306 if (*string)
307 tokens++;
308 /* count tokens */
309 for (i = 0; string[i]; i++)
311 if (!strncmpW(string+i, token_string, strlenW(token_string)))
313 DWORD j;
314 tokens++;
315 /* we want to skip over separators, but not the null terminator */
316 for (j = 0; j < strlenW(token_string) - 1; j++)
317 if (!string[i+j])
318 break;
319 i += j;
324 /* add 1 for terminating NULL */
325 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
326 token_array[tokens] = NULL;
327 if (!tokens)
328 return token_array;
329 for (i = 0; i < tokens; i++)
331 int len;
332 next_token = strstrW(string, token_string);
333 if (!next_token) next_token = string+strlenW(string);
334 len = next_token - string;
335 token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
336 memcpy(token_array[i], string, len*sizeof(WCHAR));
337 token_array[i][len] = '\0';
338 string = next_token+strlenW(token_string);
340 return token_array;
343 /***********************************************************************
344 * HTTP_FreeTokens (internal)
346 * Frees memory returned from HTTP_Tokenize.
348 static void HTTP_FreeTokens(LPWSTR * token_array)
350 int i;
351 for (i = 0; token_array[i]; i++)
352 HeapFree(GetProcessHeap(), 0, token_array[i]);
353 HeapFree(GetProcessHeap(), 0, token_array);
356 /* **********************************************************************
358 * Helper functions for the HttpSendRequest(Ex) functions
361 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
363 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
364 http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
366 TRACE("%p\n", lpwhr);
368 HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
369 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
370 req->dwContentLength, req->bEndRequest);
372 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
375 static void HTTP_FixURL(http_request_t *lpwhr)
377 static const WCHAR szSlash[] = { '/',0 };
378 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
380 /* If we don't have a path we set it to root */
381 if (NULL == lpwhr->lpszPath)
382 lpwhr->lpszPath = heap_strdupW(szSlash);
383 else /* remove \r and \n*/
385 int nLen = strlenW(lpwhr->lpszPath);
386 while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
388 nLen--;
389 lpwhr->lpszPath[nLen]='\0';
391 /* Replace '\' with '/' */
392 while (nLen>0) {
393 nLen--;
394 if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
398 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
399 lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
400 && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
402 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
403 (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
404 *fixurl = '/';
405 strcpyW(fixurl + 1, lpwhr->lpszPath);
406 HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
407 lpwhr->lpszPath = fixurl;
411 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
413 LPWSTR requestString;
414 DWORD len, n;
415 LPCWSTR *req;
416 UINT i;
417 LPWSTR p;
419 static const WCHAR szSpace[] = { ' ',0 };
420 static const WCHAR szColon[] = { ':',' ',0 };
421 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
423 /* allocate space for an array of all the string pointers to be added */
424 len = (lpwhr->nCustHeaders)*4 + 10;
425 req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
427 /* add the verb, path and HTTP version string */
428 n = 0;
429 req[n++] = verb;
430 req[n++] = szSpace;
431 req[n++] = path;
432 req[n++] = szSpace;
433 req[n++] = version;
435 /* Append custom request headers */
436 for (i = 0; i < lpwhr->nCustHeaders; i++)
438 if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
440 req[n++] = szCrLf;
441 req[n++] = lpwhr->pCustHeaders[i].lpszField;
442 req[n++] = szColon;
443 req[n++] = lpwhr->pCustHeaders[i].lpszValue;
445 TRACE("Adding custom header %s (%s)\n",
446 debugstr_w(lpwhr->pCustHeaders[i].lpszField),
447 debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
451 if( n >= len )
452 ERR("oops. buffer overrun\n");
454 req[n] = NULL;
455 requestString = HTTP_build_req( req, 4 );
456 HeapFree( GetProcessHeap(), 0, req );
459 * Set (header) termination string for request
460 * Make sure there's exactly two new lines at the end of the request
462 p = &requestString[strlenW(requestString)-1];
463 while ( (*p == '\n') || (*p == '\r') )
464 p--;
465 strcpyW( p+1, sztwocrlf );
467 return requestString;
470 static void HTTP_ProcessCookies( http_request_t *lpwhr )
472 int HeaderIndex;
473 int numCookies = 0;
474 LPHTTPHEADERW setCookieHeader;
476 while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
478 setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
480 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
482 int len;
483 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
484 LPWSTR buf_url;
485 LPHTTPHEADERW Host;
487 Host = HTTP_GetHeader(lpwhr, hostW);
488 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
489 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
490 sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
491 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
493 HeapFree(GetProcessHeap(), 0, buf_url);
495 numCookies++;
499 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue )
501 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
502 return !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
503 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
506 static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
507 struct HttpAuthInfo **ppAuthInfo,
508 LPWSTR domain_and_username, LPWSTR password )
510 SECURITY_STATUS sec_status;
511 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
512 BOOL first = FALSE;
514 TRACE("%s\n", debugstr_w(pszAuthValue));
516 if (!pAuthInfo)
518 TimeStamp exp;
520 first = TRUE;
521 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
522 if (!pAuthInfo)
523 return FALSE;
525 SecInvalidateHandle(&pAuthInfo->cred);
526 SecInvalidateHandle(&pAuthInfo->ctx);
527 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
528 pAuthInfo->attr = 0;
529 pAuthInfo->auth_data = NULL;
530 pAuthInfo->auth_data_len = 0;
531 pAuthInfo->finished = FALSE;
533 if (is_basic_auth_value(pszAuthValue))
535 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
536 pAuthInfo->scheme = heap_strdupW(szBasic);
537 if (!pAuthInfo->scheme)
539 HeapFree(GetProcessHeap(), 0, pAuthInfo);
540 return FALSE;
543 else
545 PVOID pAuthData;
546 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
548 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
549 if (!pAuthInfo->scheme)
551 HeapFree(GetProcessHeap(), 0, pAuthInfo);
552 return FALSE;
555 if (domain_and_username)
557 WCHAR *user = strchrW(domain_and_username, '\\');
558 WCHAR *domain = domain_and_username;
560 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
562 pAuthData = &nt_auth_identity;
564 if (user) user++;
565 else
567 user = domain_and_username;
568 domain = NULL;
571 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
572 nt_auth_identity.User = user;
573 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
574 nt_auth_identity.Domain = domain;
575 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
576 nt_auth_identity.Password = password;
577 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
579 else
580 /* use default credentials */
581 pAuthData = NULL;
583 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
584 SECPKG_CRED_OUTBOUND, NULL,
585 pAuthData, NULL,
586 NULL, &pAuthInfo->cred,
587 &exp);
588 if (sec_status == SEC_E_OK)
590 PSecPkgInfoW sec_pkg_info;
591 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
592 if (sec_status == SEC_E_OK)
594 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
595 FreeContextBuffer(sec_pkg_info);
598 if (sec_status != SEC_E_OK)
600 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
601 debugstr_w(pAuthInfo->scheme), sec_status);
602 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
603 HeapFree(GetProcessHeap(), 0, pAuthInfo);
604 return FALSE;
607 *ppAuthInfo = pAuthInfo;
609 else if (pAuthInfo->finished)
610 return FALSE;
612 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
613 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
615 ERR("authentication scheme changed from %s to %s\n",
616 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
617 return FALSE;
620 if (is_basic_auth_value(pszAuthValue))
622 int userlen;
623 int passlen;
624 char *auth_data;
626 TRACE("basic authentication\n");
628 /* we don't cache credentials for basic authentication, so we can't
629 * retrieve them if the application didn't pass us any credentials */
630 if (!domain_and_username) return FALSE;
632 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
633 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
635 /* length includes a nul terminator, which will be re-used for the ':' */
636 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
637 if (!auth_data)
638 return FALSE;
640 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
641 auth_data[userlen] = ':';
642 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
644 pAuthInfo->auth_data = auth_data;
645 pAuthInfo->auth_data_len = userlen + 1 + passlen;
646 pAuthInfo->finished = TRUE;
648 return TRUE;
650 else
652 LPCWSTR pszAuthData;
653 SecBufferDesc out_desc, in_desc;
654 SecBuffer out, in;
655 unsigned char *buffer;
656 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
657 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
659 in.BufferType = SECBUFFER_TOKEN;
660 in.cbBuffer = 0;
661 in.pvBuffer = NULL;
663 in_desc.ulVersion = 0;
664 in_desc.cBuffers = 1;
665 in_desc.pBuffers = &in;
667 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
668 if (*pszAuthData == ' ')
670 pszAuthData++;
671 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
672 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
673 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
676 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
678 out.BufferType = SECBUFFER_TOKEN;
679 out.cbBuffer = pAuthInfo->max_token;
680 out.pvBuffer = buffer;
682 out_desc.ulVersion = 0;
683 out_desc.cBuffers = 1;
684 out_desc.pBuffers = &out;
686 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
687 first ? NULL : &pAuthInfo->ctx,
688 first ? lpwhr->lpHttpSession->lpszServerName : NULL,
689 context_req, 0, SECURITY_NETWORK_DREP,
690 in.pvBuffer ? &in_desc : NULL,
691 0, &pAuthInfo->ctx, &out_desc,
692 &pAuthInfo->attr, &pAuthInfo->exp);
693 if (sec_status == SEC_E_OK)
695 pAuthInfo->finished = TRUE;
696 pAuthInfo->auth_data = out.pvBuffer;
697 pAuthInfo->auth_data_len = out.cbBuffer;
698 TRACE("sending last auth packet\n");
700 else if (sec_status == SEC_I_CONTINUE_NEEDED)
702 pAuthInfo->auth_data = out.pvBuffer;
703 pAuthInfo->auth_data_len = out.cbBuffer;
704 TRACE("sending next auth packet\n");
706 else
708 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
709 pAuthInfo->finished = TRUE;
710 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
711 return FALSE;
715 return TRUE;
718 /***********************************************************************
719 * HTTP_HttpAddRequestHeadersW (internal)
721 static BOOL HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
722 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
724 LPWSTR lpszStart;
725 LPWSTR lpszEnd;
726 LPWSTR buffer;
727 BOOL bSuccess = FALSE;
728 DWORD len;
730 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
732 if( dwHeaderLength == ~0U )
733 len = strlenW(lpszHeader);
734 else
735 len = dwHeaderLength;
736 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
737 lstrcpynW( buffer, lpszHeader, len + 1);
739 lpszStart = buffer;
743 LPWSTR * pFieldAndValue;
745 lpszEnd = lpszStart;
747 while (*lpszEnd != '\0')
749 if (*lpszEnd == '\r' || *lpszEnd == '\n')
750 break;
751 lpszEnd++;
754 if (*lpszStart == '\0')
755 break;
757 if (*lpszEnd == '\r' || *lpszEnd == '\n')
759 *lpszEnd = '\0';
760 lpszEnd++; /* Jump over newline */
762 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
763 if (*lpszStart == '\0')
765 /* Skip 0-length headers */
766 lpszStart = lpszEnd;
767 bSuccess = TRUE;
768 continue;
770 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
771 if (pFieldAndValue)
773 bSuccess = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
774 if (bSuccess)
775 bSuccess = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
776 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
777 HTTP_FreeTokens(pFieldAndValue);
780 lpszStart = lpszEnd;
781 } while (bSuccess);
783 HeapFree(GetProcessHeap(), 0, buffer);
785 return bSuccess;
788 /***********************************************************************
789 * HttpAddRequestHeadersW (WININET.@)
791 * Adds one or more HTTP header to the request handler
793 * NOTE
794 * On Windows if dwHeaderLength includes the trailing '\0', then
795 * HttpAddRequestHeadersW() adds it too. However this results in an
796 * invalid Http header which is rejected by some servers so we probably
797 * don't need to match Windows on that point.
799 * RETURNS
800 * TRUE on success
801 * FALSE on failure
804 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
805 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
807 BOOL bSuccess = FALSE;
808 http_request_t *lpwhr;
810 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
812 if (!lpszHeader)
813 return TRUE;
815 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
816 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
818 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
819 goto lend;
821 bSuccess = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
822 lend:
823 if( lpwhr )
824 WININET_Release( &lpwhr->hdr );
826 return bSuccess;
829 /***********************************************************************
830 * HttpAddRequestHeadersA (WININET.@)
832 * Adds one or more HTTP header to the request handler
834 * RETURNS
835 * TRUE on success
836 * FALSE on failure
839 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
840 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
842 DWORD len;
843 LPWSTR hdr;
844 BOOL r;
846 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
848 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
849 hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
850 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
851 if( dwHeaderLength != ~0U )
852 dwHeaderLength = len;
854 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
856 HeapFree( GetProcessHeap(), 0, hdr );
858 return r;
861 /***********************************************************************
862 * HttpEndRequestA (WININET.@)
864 * Ends an HTTP request that was started by HttpSendRequestEx
866 * RETURNS
867 * TRUE if successful
868 * FALSE on failure
871 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
872 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
874 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
876 if (lpBuffersOut)
878 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
879 return FALSE;
882 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
885 static BOOL HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
887 BOOL rc = FALSE;
888 INT responseLen;
889 DWORD dwBufferSize;
890 INTERNET_ASYNC_RESULT iar;
892 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
893 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
895 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
896 if (responseLen)
897 rc = TRUE;
899 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
900 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
902 /* process cookies here. Is this right? */
903 HTTP_ProcessCookies(lpwhr);
905 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
907 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
909 DWORD dwCode,dwCodeLength = sizeof(DWORD);
910 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) &&
911 (dwCode == 302 || dwCode == 301 || dwCode == 303))
913 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
914 dwBufferSize=sizeof(szNewLocation);
915 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL))
917 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
919 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
920 lpwhr->lpszVerb = heap_strdupW(szGET);
922 HTTP_DrainContent(lpwhr);
923 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
925 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
926 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
927 rc = HTTP_HandleRedirect(lpwhr, new_url);
928 if (rc)
929 rc = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
930 HeapFree( GetProcessHeap(), 0, new_url );
936 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
937 iar.dwError = rc ? 0 : INTERNET_GetLastError();
939 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
940 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
941 sizeof(INTERNET_ASYNC_RESULT));
942 return rc;
945 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
947 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
948 http_request_t *lpwhr = (http_request_t*)work->hdr;
950 TRACE("%p\n", lpwhr);
952 HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
955 /***********************************************************************
956 * HttpEndRequestW (WININET.@)
958 * Ends an HTTP request that was started by HttpSendRequestEx
960 * RETURNS
961 * TRUE if successful
962 * FALSE on failure
965 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
966 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
968 BOOL rc = FALSE;
969 http_request_t *lpwhr;
971 TRACE("-->\n");
973 if (lpBuffersOut)
975 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
976 return FALSE;
979 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
981 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
983 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
984 if (lpwhr)
985 WININET_Release( &lpwhr->hdr );
986 return FALSE;
988 lpwhr->hdr.dwFlags |= dwFlags;
990 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
992 WORKREQUEST work;
993 struct WORKREQ_HTTPENDREQUESTW *request;
995 work.asyncproc = AsyncHttpEndRequestProc;
996 work.hdr = WININET_AddRef( &lpwhr->hdr );
998 request = &work.u.HttpEndRequestW;
999 request->dwFlags = dwFlags;
1000 request->dwContext = dwContext;
1002 INTERNET_AsyncCall(&work);
1003 INTERNET_SetLastError(ERROR_IO_PENDING);
1005 else
1006 rc = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
1008 WININET_Release( &lpwhr->hdr );
1009 TRACE("%i <--\n",rc);
1010 return rc;
1013 /***********************************************************************
1014 * HttpOpenRequestW (WININET.@)
1016 * Open a HTTP request handle
1018 * RETURNS
1019 * HINTERNET a HTTP request handle on success
1020 * NULL on failure
1023 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
1024 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
1025 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
1026 DWORD dwFlags, DWORD_PTR dwContext)
1028 http_session_t *lpwhs;
1029 HINTERNET handle = NULL;
1031 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1032 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
1033 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
1034 dwFlags, dwContext);
1035 if(lpszAcceptTypes!=NULL)
1037 int i;
1038 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
1039 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
1042 lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
1043 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
1045 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1046 goto lend;
1050 * My tests seem to show that the windows version does not
1051 * become asynchronous until after this point. And anyhow
1052 * if this call was asynchronous then how would you get the
1053 * necessary HINTERNET pointer returned by this function.
1056 handle = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
1057 lpszVersion, lpszReferrer, lpszAcceptTypes,
1058 dwFlags, dwContext);
1059 lend:
1060 if( lpwhs )
1061 WININET_Release( &lpwhs->hdr );
1062 TRACE("returning %p\n", handle);
1063 return handle;
1067 /***********************************************************************
1068 * HttpOpenRequestA (WININET.@)
1070 * Open a HTTP request handle
1072 * RETURNS
1073 * HINTERNET a HTTP request handle on success
1074 * NULL on failure
1077 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1078 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1079 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1080 DWORD dwFlags, DWORD_PTR dwContext)
1082 LPWSTR szVerb = NULL, szObjectName = NULL;
1083 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1084 INT acceptTypesCount;
1085 HINTERNET rc = FALSE;
1086 LPCSTR *types;
1088 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1089 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1090 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1091 dwFlags, dwContext);
1093 if (lpszVerb)
1095 szVerb = heap_strdupAtoW(lpszVerb);
1096 if ( !szVerb )
1097 goto end;
1100 if (lpszObjectName)
1102 szObjectName = heap_strdupAtoW(lpszObjectName);
1103 if ( !szObjectName )
1104 goto end;
1107 if (lpszVersion)
1109 szVersion = heap_strdupAtoW(lpszVersion);
1110 if ( !szVersion )
1111 goto end;
1114 if (lpszReferrer)
1116 szReferrer = heap_strdupAtoW(lpszReferrer);
1117 if ( !szReferrer )
1118 goto end;
1121 if (lpszAcceptTypes)
1123 acceptTypesCount = 0;
1124 types = lpszAcceptTypes;
1125 while (*types)
1127 __TRY
1129 /* find out how many there are */
1130 if (*types && **types)
1132 TRACE("accept type: %s\n", debugstr_a(*types));
1133 acceptTypesCount++;
1136 __EXCEPT_PAGE_FAULT
1138 WARN("invalid accept type pointer\n");
1140 __ENDTRY;
1141 types++;
1143 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1144 if (!szAcceptTypes) goto end;
1146 acceptTypesCount = 0;
1147 types = lpszAcceptTypes;
1148 while (*types)
1150 __TRY
1152 if (*types && **types)
1153 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1155 __EXCEPT_PAGE_FAULT
1157 /* ignore invalid pointer */
1159 __ENDTRY;
1160 types++;
1162 szAcceptTypes[acceptTypesCount] = NULL;
1165 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1166 szVersion, szReferrer,
1167 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1169 end:
1170 if (szAcceptTypes)
1172 acceptTypesCount = 0;
1173 while (szAcceptTypes[acceptTypesCount])
1175 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1176 acceptTypesCount++;
1178 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1180 HeapFree(GetProcessHeap(), 0, szReferrer);
1181 HeapFree(GetProcessHeap(), 0, szVersion);
1182 HeapFree(GetProcessHeap(), 0, szObjectName);
1183 HeapFree(GetProcessHeap(), 0, szVerb);
1185 return rc;
1188 /***********************************************************************
1189 * HTTP_EncodeBase64
1191 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1193 UINT n = 0, x;
1194 static const CHAR HTTP_Base64Enc[] =
1195 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1197 while( len > 0 )
1199 /* first 6 bits, all from bin[0] */
1200 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1201 x = (bin[0] & 3) << 4;
1203 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1204 if( len == 1 )
1206 base64[n++] = HTTP_Base64Enc[x];
1207 base64[n++] = '=';
1208 base64[n++] = '=';
1209 break;
1211 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1212 x = ( bin[1] & 0x0f ) << 2;
1214 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1215 if( len == 2 )
1217 base64[n++] = HTTP_Base64Enc[x];
1218 base64[n++] = '=';
1219 break;
1221 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1223 /* last 6 bits, all from bin [2] */
1224 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1225 bin += 3;
1226 len -= 3;
1228 base64[n] = 0;
1229 return n;
1232 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1233 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1234 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1235 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1236 static const signed char HTTP_Base64Dec[256] =
1238 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1239 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1240 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1241 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1242 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1243 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1244 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1245 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1246 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1247 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1248 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1249 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1250 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1251 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1252 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1253 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1254 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1255 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1256 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1257 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1258 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1259 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1260 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1261 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1262 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1263 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1265 #undef CH
1267 /***********************************************************************
1268 * HTTP_DecodeBase64
1270 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1272 unsigned int n = 0;
1274 while(*base64)
1276 signed char in[4];
1278 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1279 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1280 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1281 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1283 WARN("invalid base64: %s\n", debugstr_w(base64));
1284 return 0;
1286 if (bin)
1287 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1288 n++;
1290 if ((base64[2] == '=') && (base64[3] == '='))
1291 break;
1292 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1293 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1295 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1296 return 0;
1298 if (bin)
1299 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1300 n++;
1302 if (base64[3] == '=')
1303 break;
1304 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1305 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1307 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1308 return 0;
1310 if (bin)
1311 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1312 n++;
1314 base64 += 4;
1317 return n;
1320 /***********************************************************************
1321 * HTTP_InsertAuthorization
1323 * Insert or delete the authorization field in the request header.
1325 static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1327 if (pAuthInfo)
1329 static const WCHAR wszSpace[] = {' ',0};
1330 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1331 unsigned int len;
1332 WCHAR *authorization = NULL;
1334 if (pAuthInfo->auth_data_len)
1336 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1337 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1338 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1339 if (!authorization)
1340 return FALSE;
1342 strcpyW(authorization, pAuthInfo->scheme);
1343 strcatW(authorization, wszSpace);
1344 HTTP_EncodeBase64(pAuthInfo->auth_data,
1345 pAuthInfo->auth_data_len,
1346 authorization+strlenW(authorization));
1348 /* clear the data as it isn't valid now that it has been sent to the
1349 * server, unless it's Basic authentication which doesn't do
1350 * connection tracking */
1351 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1353 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1354 pAuthInfo->auth_data = NULL;
1355 pAuthInfo->auth_data_len = 0;
1359 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1361 HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1363 HeapFree(GetProcessHeap(), 0, authorization);
1365 return TRUE;
1368 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1370 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1371 DWORD size;
1373 size = sizeof(new_location);
1374 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL))
1376 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1377 strcpyW( url, new_location );
1379 else
1381 static const WCHAR slash[] = { '/',0 };
1382 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1383 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1384 http_session_t *session = req->lpHttpSession;
1386 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1387 size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
1389 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1391 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1392 sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
1393 else
1394 sprintfW( url, format, session->lpszHostName, session->nHostPort );
1395 if (req->lpszPath[0] != '/') strcatW( url, slash );
1396 strcatW( url, req->lpszPath );
1398 TRACE("url=%s\n", debugstr_w(url));
1399 return url;
1402 /***********************************************************************
1403 * HTTP_DealWithProxy
1405 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
1407 WCHAR buf[MAXHOSTNAME];
1408 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1409 static WCHAR szNul[] = { 0 };
1410 URL_COMPONENTSW UrlComponents;
1411 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1412 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1414 memset( &UrlComponents, 0, sizeof UrlComponents );
1415 UrlComponents.dwStructSize = sizeof UrlComponents;
1416 UrlComponents.lpszHostName = buf;
1417 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1419 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1420 hIC->lpszProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1421 sprintfW(proxy, szFormat, hIC->lpszProxy);
1422 else
1423 strcpyW(proxy, hIC->lpszProxy);
1424 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1425 return FALSE;
1426 if( UrlComponents.dwHostNameLength == 0 )
1427 return FALSE;
1429 if( !lpwhr->lpszPath )
1430 lpwhr->lpszPath = szNul;
1432 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1433 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1435 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1436 lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1437 lpwhs->nServerPort = UrlComponents.nPort;
1439 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1440 return TRUE;
1443 #ifndef INET6_ADDRSTRLEN
1444 #define INET6_ADDRSTRLEN 46
1445 #endif
1447 static BOOL HTTP_ResolveName(http_request_t *lpwhr)
1449 char szaddr[INET6_ADDRSTRLEN];
1450 http_session_t *lpwhs = lpwhr->lpHttpSession;
1451 const void *addr;
1453 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1454 INTERNET_STATUS_RESOLVING_NAME,
1455 lpwhs->lpszServerName,
1456 strlenW(lpwhs->lpszServerName)+1);
1458 lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1459 if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1460 (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1462 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1463 return FALSE;
1466 switch (lpwhs->socketAddress.ss_family)
1468 case AF_INET:
1469 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1470 break;
1471 case AF_INET6:
1472 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1473 break;
1474 default:
1475 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1476 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1477 return FALSE;
1479 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1480 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1481 INTERNET_STATUS_NAME_RESOLVED,
1482 szaddr, strlen(szaddr)+1);
1484 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1485 return TRUE;
1489 /***********************************************************************
1490 * HTTPREQ_Destroy (internal)
1492 * Deallocate request handle
1495 static void HTTPREQ_Destroy(object_header_t *hdr)
1497 http_request_t *lpwhr = (http_request_t*) hdr;
1498 DWORD i;
1500 TRACE("\n");
1502 if(lpwhr->hCacheFile)
1503 CloseHandle(lpwhr->hCacheFile);
1505 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1507 DeleteCriticalSection( &lpwhr->read_section );
1508 WININET_Release(&lpwhr->lpHttpSession->hdr);
1510 if (lpwhr->pAuthInfo)
1512 if (SecIsValidHandle(&lpwhr->pAuthInfo->ctx))
1513 DeleteSecurityContext(&lpwhr->pAuthInfo->ctx);
1514 if (SecIsValidHandle(&lpwhr->pAuthInfo->cred))
1515 FreeCredentialsHandle(&lpwhr->pAuthInfo->cred);
1517 HeapFree(GetProcessHeap(), 0, lpwhr->pAuthInfo->auth_data);
1518 HeapFree(GetProcessHeap(), 0, lpwhr->pAuthInfo->scheme);
1519 HeapFree(GetProcessHeap(), 0, lpwhr->pAuthInfo);
1520 lpwhr->pAuthInfo = NULL;
1523 if (lpwhr->pProxyAuthInfo)
1525 if (SecIsValidHandle(&lpwhr->pProxyAuthInfo->ctx))
1526 DeleteSecurityContext(&lpwhr->pProxyAuthInfo->ctx);
1527 if (SecIsValidHandle(&lpwhr->pProxyAuthInfo->cred))
1528 FreeCredentialsHandle(&lpwhr->pProxyAuthInfo->cred);
1530 HeapFree(GetProcessHeap(), 0, lpwhr->pProxyAuthInfo->auth_data);
1531 HeapFree(GetProcessHeap(), 0, lpwhr->pProxyAuthInfo->scheme);
1532 HeapFree(GetProcessHeap(), 0, lpwhr->pProxyAuthInfo);
1533 lpwhr->pProxyAuthInfo = NULL;
1536 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1537 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1538 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1539 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1540 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1542 for (i = 0; i < lpwhr->nCustHeaders; i++)
1544 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1545 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1548 #ifdef HAVE_ZLIB
1549 if(lpwhr->gzip_stream) {
1550 if(!lpwhr->gzip_stream->end_of_data)
1551 inflateEnd(&lpwhr->gzip_stream->zstream);
1552 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1554 #endif
1556 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1557 HeapFree(GetProcessHeap(), 0, lpwhr);
1560 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1562 http_request_t *lpwhr = (http_request_t*) hdr;
1564 TRACE("%p\n",lpwhr);
1566 if (!NETCON_connected(&lpwhr->netConnection))
1567 return;
1569 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1570 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1572 NETCON_close(&lpwhr->netConnection);
1574 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1575 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1578 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1580 LPHTTPHEADERW host_header;
1582 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1584 host_header = HTTP_GetHeader(req, hostW);
1585 if(!host_header)
1586 return FALSE;
1588 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1589 return TRUE;
1592 static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
1594 WCHAR szVersion[10];
1595 WCHAR szConnectionResponse[20];
1596 DWORD dwBufferSize = sizeof(szVersion);
1597 BOOL keepalive = FALSE;
1599 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1600 * the connection is keep-alive by default */
1601 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion,
1602 &dwBufferSize, NULL) &&
1603 !strcmpiW(szVersion, g_szHttp1_1))
1605 keepalive = TRUE;
1608 dwBufferSize = sizeof(szConnectionResponse);
1609 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) ||
1610 HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL))
1612 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1615 return keepalive;
1618 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1620 http_request_t *req = (http_request_t*)hdr;
1622 switch(option) {
1623 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1625 http_session_t *lpwhs = req->lpHttpSession;
1626 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1628 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1630 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1631 return ERROR_INSUFFICIENT_BUFFER;
1632 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1633 /* FIXME: can't get a SOCKET from our connection since we don't use
1634 * winsock
1636 info->Socket = 0;
1637 /* FIXME: get source port from req->netConnection */
1638 info->SourcePort = 0;
1639 info->DestPort = lpwhs->nHostPort;
1640 info->Flags = 0;
1641 if (HTTP_KeepAlive(req))
1642 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1643 if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
1644 info->Flags |= IDSI_FLAG_PROXY;
1645 if (req->netConnection.useSSL)
1646 info->Flags |= IDSI_FLAG_SECURE;
1648 return ERROR_SUCCESS;
1651 case INTERNET_OPTION_SECURITY_FLAGS:
1653 http_session_t *lpwhs;
1654 lpwhs = req->lpHttpSession;
1656 if (*size < sizeof(ULONG))
1657 return ERROR_INSUFFICIENT_BUFFER;
1659 *size = sizeof(DWORD);
1660 if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1661 *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1662 else
1663 *(DWORD*)buffer = 0;
1664 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1665 return ERROR_SUCCESS;
1668 case INTERNET_OPTION_HANDLE_TYPE:
1669 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1671 if (*size < sizeof(ULONG))
1672 return ERROR_INSUFFICIENT_BUFFER;
1674 *size = sizeof(DWORD);
1675 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1676 return ERROR_SUCCESS;
1678 case INTERNET_OPTION_URL: {
1679 WCHAR url[INTERNET_MAX_URL_LENGTH];
1680 HTTPHEADERW *host;
1681 DWORD len;
1682 WCHAR *pch;
1684 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1686 TRACE("INTERNET_OPTION_URL\n");
1688 host = HTTP_GetHeader(req, hostW);
1689 strcpyW(url, httpW);
1690 strcatW(url, host->lpszValue);
1691 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1692 *pch = 0;
1693 strcatW(url, req->lpszPath);
1695 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1697 if(unicode) {
1698 len = (strlenW(url)+1) * sizeof(WCHAR);
1699 if(*size < len)
1700 return ERROR_INSUFFICIENT_BUFFER;
1702 *size = len;
1703 strcpyW(buffer, url);
1704 return ERROR_SUCCESS;
1705 }else {
1706 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1707 if(len > *size)
1708 return ERROR_INSUFFICIENT_BUFFER;
1710 *size = len;
1711 return ERROR_SUCCESS;
1715 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1716 INTERNET_CACHE_ENTRY_INFOW *info;
1717 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1718 WCHAR url[INTERNET_MAX_URL_LENGTH];
1719 DWORD nbytes, error;
1720 BOOL ret;
1722 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1724 if (*size < sizeof(*ts))
1726 *size = sizeof(*ts);
1727 return ERROR_INSUFFICIENT_BUFFER;
1729 nbytes = 0;
1730 HTTP_GetRequestURL(req, url);
1731 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1732 error = GetLastError();
1733 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1735 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1736 return ERROR_OUTOFMEMORY;
1738 GetUrlCacheEntryInfoW(url, info, &nbytes);
1740 ts->ftExpires = info->ExpireTime;
1741 ts->ftLastModified = info->LastModifiedTime;
1743 HeapFree(GetProcessHeap(), 0, info);
1744 *size = sizeof(*ts);
1745 return ERROR_SUCCESS;
1747 return error;
1750 case INTERNET_OPTION_DATAFILE_NAME: {
1751 DWORD req_size;
1753 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1755 if(!req->lpszCacheFile) {
1756 *size = 0;
1757 return ERROR_INTERNET_ITEM_NOT_FOUND;
1760 if(unicode) {
1761 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1762 if(*size < req_size)
1763 return ERROR_INSUFFICIENT_BUFFER;
1765 *size = req_size;
1766 memcpy(buffer, req->lpszCacheFile, *size);
1767 return ERROR_SUCCESS;
1768 }else {
1769 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1770 if (req_size > *size)
1771 return ERROR_INSUFFICIENT_BUFFER;
1773 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1774 -1, buffer, *size, NULL, NULL);
1775 return ERROR_SUCCESS;
1779 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1780 PCCERT_CONTEXT context;
1782 if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1783 *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1784 return ERROR_INSUFFICIENT_BUFFER;
1787 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1788 if(context) {
1789 INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1790 DWORD len;
1792 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1793 info->ftExpiry = context->pCertInfo->NotAfter;
1794 info->ftStart = context->pCertInfo->NotBefore;
1795 if(unicode) {
1796 len = CertNameToStrW(context->dwCertEncodingType,
1797 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1798 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1799 if(info->lpszSubjectInfo)
1800 CertNameToStrW(context->dwCertEncodingType,
1801 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1802 info->lpszSubjectInfo, len);
1803 len = CertNameToStrW(context->dwCertEncodingType,
1804 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1805 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1806 if (info->lpszIssuerInfo)
1807 CertNameToStrW(context->dwCertEncodingType,
1808 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1809 info->lpszIssuerInfo, len);
1810 }else {
1811 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1813 len = CertNameToStrA(context->dwCertEncodingType,
1814 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1815 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1816 if(infoA->lpszSubjectInfo)
1817 CertNameToStrA(context->dwCertEncodingType,
1818 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1819 infoA->lpszSubjectInfo, len);
1820 len = CertNameToStrA(context->dwCertEncodingType,
1821 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1822 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1823 if(infoA->lpszIssuerInfo)
1824 CertNameToStrA(context->dwCertEncodingType,
1825 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1826 infoA->lpszIssuerInfo, len);
1830 * Contrary to MSDN, these do not appear to be set.
1831 * lpszProtocolName
1832 * lpszSignatureAlgName
1833 * lpszEncryptionAlgName
1834 * dwKeySize
1836 CertFreeCertificateContext(context);
1837 return ERROR_SUCCESS;
1842 return INET_QueryOption(option, buffer, size, unicode);
1845 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1847 http_request_t *req = (http_request_t*)hdr;
1849 switch(option) {
1850 case INTERNET_OPTION_SEND_TIMEOUT:
1851 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1852 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1854 if (size != sizeof(DWORD))
1855 return ERROR_INVALID_PARAMETER;
1857 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1858 *(DWORD*)buffer);
1860 case INTERNET_OPTION_USERNAME:
1861 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1862 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1863 return ERROR_SUCCESS;
1865 case INTERNET_OPTION_PASSWORD:
1866 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1867 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1868 return ERROR_SUCCESS;
1869 case INTERNET_OPTION_HTTP_DECODING:
1870 if(size != sizeof(BOOL))
1871 return ERROR_INVALID_PARAMETER;
1872 req->decoding = *(BOOL*)buffer;
1873 return ERROR_SUCCESS;
1876 return ERROR_INTERNET_INVALID_OPTION;
1879 /* read some more data into the read buffer (the read section must be held) */
1880 static BOOL read_more_data( http_request_t *req, int maxlen )
1882 int len;
1884 if (req->read_pos)
1886 /* move existing data to the start of the buffer */
1887 if(req->read_size)
1888 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1889 req->read_pos = 0;
1892 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1894 if(!NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1895 maxlen - req->read_size, 0, &len ))
1896 return FALSE;
1898 req->read_size += len;
1899 return TRUE;
1902 /* remove some amount of data from the read buffer (the read section must be held) */
1903 static void remove_data( http_request_t *req, int count )
1905 if (!(req->read_size -= count)) req->read_pos = 0;
1906 else req->read_pos += count;
1909 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1911 int count, bytes_read, pos = 0;
1913 EnterCriticalSection( &req->read_section );
1914 for (;;)
1916 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1918 if (eol)
1920 count = eol - (req->read_buf + req->read_pos);
1921 bytes_read = count + 1;
1923 else count = bytes_read = req->read_size;
1925 count = min( count, *len - pos );
1926 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1927 pos += count;
1928 remove_data( req, bytes_read );
1929 if (eol) break;
1931 if (!read_more_data( req, -1 ) || !req->read_size)
1933 *len = 0;
1934 TRACE( "returning empty string\n" );
1935 LeaveCriticalSection( &req->read_section );
1936 return FALSE;
1939 LeaveCriticalSection( &req->read_section );
1941 if (pos < *len)
1943 if (pos && buffer[pos - 1] == '\r') pos--;
1944 *len = pos + 1;
1946 buffer[*len - 1] = 0;
1947 TRACE( "returning %s\n", debugstr_a(buffer));
1948 return TRUE;
1951 /* discard data contents until we reach end of line (the read section must be held) */
1952 static BOOL discard_eol( http_request_t *req )
1956 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1957 if (eol)
1959 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
1960 break;
1962 req->read_pos = req->read_size = 0; /* discard everything */
1963 if (!read_more_data( req, -1 )) return FALSE;
1964 } while (req->read_size);
1965 return TRUE;
1968 /* read the size of the next chunk (the read section must be held) */
1969 static BOOL start_next_chunk( http_request_t *req )
1971 DWORD chunk_size = 0;
1973 if (!req->dwContentLength) return TRUE;
1974 if (req->dwContentLength == req->dwContentRead)
1976 /* read terminator for the previous chunk */
1977 if (!discard_eol( req )) return FALSE;
1978 req->dwContentLength = ~0u;
1979 req->dwContentRead = 0;
1981 for (;;)
1983 while (req->read_size)
1985 char ch = req->read_buf[req->read_pos];
1986 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
1987 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
1988 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
1989 else if (ch == ';' || ch == '\r' || ch == '\n')
1991 TRACE( "reading %u byte chunk\n", chunk_size );
1992 req->dwContentLength = chunk_size;
1993 req->dwContentRead = 0;
1994 if (!discard_eol( req )) return FALSE;
1995 return TRUE;
1997 remove_data( req, 1 );
1999 if (!read_more_data( req, -1 )) return FALSE;
2000 if (!req->read_size)
2002 req->dwContentLength = req->dwContentRead = 0;
2003 return TRUE;
2008 /* check if we have reached the end of the data to read (the read section must be held) */
2009 static BOOL end_of_read_data( http_request_t *req )
2011 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2012 if (req->read_chunked) return (req->dwContentLength == 0);
2013 if (req->dwContentLength == ~0u) return FALSE;
2014 return (req->dwContentLength == req->dwContentRead);
2017 /* fetch some more data into the read buffer (the read section must be held) */
2018 static BOOL refill_buffer( http_request_t *req )
2020 int len = sizeof(req->read_buf);
2022 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2024 if (!start_next_chunk( req )) return FALSE;
2027 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
2028 if (len <= req->read_size) return TRUE;
2030 if (!read_more_data( req, len )) return FALSE;
2031 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
2032 return TRUE;
2035 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
2037 DWORD ret = ERROR_SUCCESS;
2038 int read = 0;
2040 #ifdef HAVE_ZLIB
2041 z_stream *zstream = &req->gzip_stream->zstream;
2042 int zres;
2044 while(read < size && !req->gzip_stream->end_of_data) {
2045 if(!req->read_size) {
2046 if(!sync || !refill_buffer(req))
2047 break;
2050 zstream->next_in = req->read_buf+req->read_pos;
2051 zstream->avail_in = req->read_size;
2052 zstream->next_out = buf+read;
2053 zstream->avail_out = size-read;
2054 zres = inflate(zstream, Z_FULL_FLUSH);
2055 read = size - zstream->avail_out;
2056 remove_data(req, req->read_size-zstream->avail_in);
2057 if(zres == Z_STREAM_END) {
2058 TRACE("end of data\n");
2059 req->gzip_stream->end_of_data = TRUE;
2060 inflateEnd(&req->gzip_stream->zstream);
2061 }else if(zres != Z_OK) {
2062 WARN("inflate failed %d\n", zres);
2063 if(!read)
2064 ret = ERROR_INTERNET_DECODING_FAILED;
2065 break;
2068 #endif
2070 *read_ret = read;
2071 return ret;
2074 static void refill_gzip_buffer(http_request_t *req)
2076 DWORD res;
2077 int len;
2079 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2080 return;
2082 if(req->gzip_stream->buf_pos) {
2083 if(req->gzip_stream->buf_size)
2084 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2085 req->gzip_stream->buf_pos = 0;
2088 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2089 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2090 if(res == ERROR_SUCCESS)
2091 req->gzip_stream->buf_size += len;
2094 /* return the size of data available to be read immediately (the read section must be held) */
2095 static DWORD get_avail_data( http_request_t *req )
2097 if (req->gzip_stream) {
2098 refill_gzip_buffer(req);
2099 return req->gzip_stream->buf_size;
2101 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2102 return 0;
2103 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2106 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2108 INTERNET_ASYNC_RESULT iar;
2110 TRACE("%p\n", req);
2112 EnterCriticalSection( &req->read_section );
2113 if (refill_buffer( req )) {
2114 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2115 iar.dwError = first_notif ? 0 : get_avail_data(req);
2116 }else {
2117 iar.dwResult = 0;
2118 iar.dwError = INTERNET_GetLastError();
2120 LeaveCriticalSection( &req->read_section );
2122 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2123 sizeof(INTERNET_ASYNC_RESULT));
2126 /* read data from the http connection (the read section must be held) */
2127 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2129 BOOL finished_reading = FALSE;
2130 int len, bytes_read = 0;
2131 DWORD ret = ERROR_SUCCESS;
2133 EnterCriticalSection( &req->read_section );
2135 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2137 if (!start_next_chunk( req )) goto done;
2140 if(req->gzip_stream) {
2141 if(req->gzip_stream->buf_size) {
2142 bytes_read = min(req->gzip_stream->buf_size, size);
2143 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2144 req->gzip_stream->buf_pos += bytes_read;
2145 req->gzip_stream->buf_size -= bytes_read;
2146 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2147 refill_buffer(req);
2150 if(size > bytes_read) {
2151 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2152 if(ret == ERROR_SUCCESS)
2153 bytes_read += len;
2156 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2157 }else {
2158 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2160 if (req->read_size) {
2161 bytes_read = min( req->read_size, size );
2162 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2163 remove_data( req, bytes_read );
2166 if (size > bytes_read && (!bytes_read || sync)) {
2167 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2168 sync ? MSG_WAITALL : 0, &len))
2169 bytes_read += len;
2170 /* always return success, even if the network layer returns an error */
2173 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2175 done:
2176 req->dwContentRead += bytes_read;
2177 *read = bytes_read;
2179 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2180 LeaveCriticalSection( &req->read_section );
2182 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2183 BOOL res;
2184 DWORD dwBytesWritten;
2186 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2187 if(!res)
2188 WARN("WriteFile failed: %u\n", GetLastError());
2191 if(finished_reading)
2192 HTTP_FinishedReading(req);
2194 return ret;
2198 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2200 http_request_t *req = (http_request_t*)hdr;
2201 return HTTPREQ_Read(req, buffer, size, read, TRUE);
2204 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2206 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2207 http_request_t *req = (http_request_t*)workRequest->hdr;
2208 INTERNET_ASYNC_RESULT iar;
2209 DWORD res;
2211 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2213 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2214 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2216 iar.dwResult = res == ERROR_SUCCESS;
2217 iar.dwError = res;
2219 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2220 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2221 sizeof(INTERNET_ASYNC_RESULT));
2224 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2225 DWORD flags, DWORD_PTR context)
2227 http_request_t *req = (http_request_t*)hdr;
2228 DWORD res;
2230 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2231 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2233 if (buffers->dwStructSize != sizeof(*buffers))
2234 return ERROR_INVALID_PARAMETER;
2236 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2238 if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
2240 WORKREQUEST workRequest;
2242 if (TryEnterCriticalSection( &req->read_section ))
2244 if (get_avail_data(req))
2246 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2247 &buffers->dwBufferLength, FALSE);
2248 LeaveCriticalSection( &req->read_section );
2249 goto done;
2251 LeaveCriticalSection( &req->read_section );
2254 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2255 workRequest.hdr = WININET_AddRef(&req->hdr);
2256 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2258 INTERNET_AsyncCall(&workRequest);
2260 return ERROR_IO_PENDING;
2263 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2264 !(flags & IRF_NO_WAIT));
2266 done:
2267 if (res == ERROR_SUCCESS) {
2268 DWORD size = buffers->dwBufferLength;
2269 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2270 &size, sizeof(size));
2273 return res;
2276 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2278 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2279 http_request_t *req = (http_request_t*)workRequest->hdr;
2280 INTERNET_ASYNC_RESULT iar;
2281 DWORD res;
2283 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2285 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2286 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2288 iar.dwResult = res == ERROR_SUCCESS;
2289 iar.dwError = res;
2291 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2292 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2293 sizeof(INTERNET_ASYNC_RESULT));
2296 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2297 DWORD flags, DWORD_PTR context)
2300 http_request_t *req = (http_request_t*)hdr;
2301 DWORD res;
2303 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2304 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2306 if (buffers->dwStructSize != sizeof(*buffers))
2307 return ERROR_INVALID_PARAMETER;
2309 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2311 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2313 WORKREQUEST workRequest;
2315 if (TryEnterCriticalSection( &req->read_section ))
2317 if (get_avail_data(req))
2319 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2320 &buffers->dwBufferLength, FALSE);
2321 LeaveCriticalSection( &req->read_section );
2322 goto done;
2324 LeaveCriticalSection( &req->read_section );
2327 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2328 workRequest.hdr = WININET_AddRef(&req->hdr);
2329 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2331 INTERNET_AsyncCall(&workRequest);
2333 return ERROR_IO_PENDING;
2336 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2337 !(flags & IRF_NO_WAIT));
2339 done:
2340 if (res == ERROR_SUCCESS) {
2341 DWORD size = buffers->dwBufferLength;
2342 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2343 &size, sizeof(size));
2346 return res;
2349 static BOOL HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2351 BOOL ret;
2352 http_request_t *lpwhr = (http_request_t*)hdr;
2354 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2356 *written = 0;
2357 if ((ret = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written)))
2358 lpwhr->dwBytesWritten += *written;
2360 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2361 return ret;
2364 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2366 http_request_t *req = (http_request_t*)workRequest->hdr;
2368 HTTP_ReceiveRequestData(req, FALSE);
2371 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2373 http_request_t *req = (http_request_t*)hdr;
2375 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2377 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2379 WORKREQUEST workRequest;
2381 /* never wait, if we can't enter the section we queue an async request right away */
2382 if (TryEnterCriticalSection( &req->read_section ))
2384 if ((*available = get_avail_data( req ))) goto done;
2385 if (end_of_read_data( req )) goto done;
2386 LeaveCriticalSection( &req->read_section );
2389 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2390 workRequest.hdr = WININET_AddRef( &req->hdr );
2392 INTERNET_AsyncCall(&workRequest);
2394 return ERROR_IO_PENDING;
2397 EnterCriticalSection( &req->read_section );
2399 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2401 refill_buffer( req );
2402 *available = get_avail_data( req );
2405 done:
2406 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2408 DWORD extra;
2409 if (NETCON_query_data_available(&req->netConnection, &extra))
2410 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2412 LeaveCriticalSection( &req->read_section );
2414 TRACE( "returning %u\n", *available );
2415 return ERROR_SUCCESS;
2418 static const object_vtbl_t HTTPREQVtbl = {
2419 HTTPREQ_Destroy,
2420 HTTPREQ_CloseConnection,
2421 HTTPREQ_QueryOption,
2422 HTTPREQ_SetOption,
2423 HTTPREQ_ReadFile,
2424 HTTPREQ_ReadFileExA,
2425 HTTPREQ_ReadFileExW,
2426 HTTPREQ_WriteFile,
2427 HTTPREQ_QueryDataAvailable,
2428 NULL
2431 /***********************************************************************
2432 * HTTP_HttpOpenRequestW (internal)
2434 * Open a HTTP request handle
2436 * RETURNS
2437 * HINTERNET a HTTP request handle on success
2438 * NULL on failure
2441 HINTERNET WINAPI HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2442 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2443 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2444 DWORD dwFlags, DWORD_PTR dwContext)
2446 appinfo_t *hIC = NULL;
2447 http_request_t *lpwhr;
2448 LPWSTR lpszHostName = NULL;
2449 HINTERNET handle = NULL;
2450 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2451 DWORD len;
2453 TRACE("-->\n");
2455 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2456 hIC = lpwhs->lpAppInfo;
2458 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2459 if (NULL == lpwhr)
2461 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2462 goto lend;
2464 lpwhr->hdr.htype = WH_HHTTPREQ;
2465 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2466 lpwhr->hdr.dwFlags = dwFlags;
2467 lpwhr->hdr.dwContext = dwContext;
2468 lpwhr->hdr.refs = 1;
2469 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2470 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2471 lpwhr->dwContentLength = ~0u;
2472 InitializeCriticalSection( &lpwhr->read_section );
2474 WININET_AddRef( &lpwhs->hdr );
2475 lpwhr->lpHttpSession = lpwhs;
2476 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2478 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2479 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2480 if (NULL == lpszHostName)
2482 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2483 goto lend;
2486 handle = WININET_AllocHandle( &lpwhr->hdr );
2487 if (NULL == handle)
2489 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2490 goto lend;
2493 if (!NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE))
2495 InternetCloseHandle( handle );
2496 handle = NULL;
2497 goto lend;
2500 if (lpszObjectName && *lpszObjectName) {
2501 HRESULT rc;
2503 len = 0;
2504 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2505 if (rc != E_POINTER)
2506 len = strlenW(lpszObjectName)+1;
2507 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2508 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2509 URL_ESCAPE_SPACES_ONLY);
2510 if (rc != S_OK)
2512 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2513 strcpyW(lpwhr->lpszPath,lpszObjectName);
2515 }else {
2516 static const WCHAR slashW[] = {'/',0};
2518 lpwhr->lpszPath = heap_strdupW(slashW);
2521 if (lpszReferrer && *lpszReferrer)
2522 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2524 if (lpszAcceptTypes)
2526 int i;
2527 for (i = 0; lpszAcceptTypes[i]; i++)
2529 if (!*lpszAcceptTypes[i]) continue;
2530 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2531 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2532 HTTP_ADDHDR_FLAG_REQ |
2533 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2537 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2538 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2540 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2541 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2542 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2544 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2545 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2546 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2548 else
2549 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2550 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2552 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2553 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2554 INTERNET_DEFAULT_HTTPS_PORT :
2555 INTERNET_DEFAULT_HTTP_PORT);
2557 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2558 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2559 INTERNET_DEFAULT_HTTPS_PORT :
2560 INTERNET_DEFAULT_HTTP_PORT);
2562 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2563 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2565 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2566 INTERNET_STATUS_HANDLE_CREATED, &handle,
2567 sizeof(handle));
2569 lend:
2570 HeapFree(GetProcessHeap(), 0, lpszHostName);
2571 if( lpwhr )
2572 WININET_Release( &lpwhr->hdr );
2574 TRACE("<-- %p (%p)\n", handle, lpwhr);
2575 return handle;
2578 /* read any content returned by the server so that the connection can be
2579 * reused */
2580 static void HTTP_DrainContent(http_request_t *req)
2582 DWORD bytes_read;
2584 if (!NETCON_connected(&req->netConnection)) return;
2586 if (req->dwContentLength == -1)
2588 NETCON_close(&req->netConnection);
2589 return;
2591 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2595 char buffer[2048];
2596 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2597 return;
2598 } while (bytes_read);
2601 static const LPCWSTR header_lookup[] = {
2602 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2603 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2604 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2605 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2606 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2607 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2608 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2609 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2610 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2611 szDate, /* HTTP_QUERY_DATE = 9 */
2612 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2613 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2614 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2615 szURI, /* HTTP_QUERY_URI = 13 */
2616 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2617 NULL, /* HTTP_QUERY_COST = 15 */
2618 NULL, /* HTTP_QUERY_LINK = 16 */
2619 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2620 NULL, /* HTTP_QUERY_VERSION = 18 */
2621 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2622 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2623 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2624 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2625 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2626 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2627 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2628 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2629 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2630 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2631 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2632 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2633 NULL, /* HTTP_QUERY_FROM = 31 */
2634 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2635 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2636 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2637 szReferer, /* HTTP_QUERY_REFERER = 35 */
2638 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2639 szServer, /* HTTP_QUERY_SERVER = 37 */
2640 NULL, /* HTTP_TITLE = 38 */
2641 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2642 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2643 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2644 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2645 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2646 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2647 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2648 NULL, /* HTTP_QUERY_REFRESH = 46 */
2649 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2650 szAge, /* HTTP_QUERY_AGE = 48 */
2651 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2652 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2653 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2654 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2655 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2656 szETag, /* HTTP_QUERY_ETAG = 54 */
2657 hostW, /* HTTP_QUERY_HOST = 55 */
2658 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2659 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2660 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2661 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2662 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2663 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2664 szRange, /* HTTP_QUERY_RANGE = 62 */
2665 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2666 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2667 szVary, /* HTTP_QUERY_VARY = 65 */
2668 szVia, /* HTTP_QUERY_VIA = 66 */
2669 szWarning, /* HTTP_QUERY_WARNING = 67 */
2670 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2671 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2672 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2675 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2677 /***********************************************************************
2678 * HTTP_HttpQueryInfoW (internal)
2680 static BOOL HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2681 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2683 LPHTTPHEADERW lphttpHdr = NULL;
2684 BOOL bSuccess = FALSE;
2685 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2686 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2687 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2688 INT index = -1;
2690 /* Find requested header structure */
2691 switch (level)
2693 case HTTP_QUERY_CUSTOM:
2694 if (!lpBuffer) return FALSE;
2695 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2696 break;
2697 case HTTP_QUERY_RAW_HEADERS_CRLF:
2699 LPWSTR headers;
2700 DWORD len = 0;
2701 BOOL ret = FALSE;
2703 if (request_only)
2704 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2705 else
2706 headers = lpwhr->lpszRawHeaders;
2708 if (headers)
2709 len = strlenW(headers) * sizeof(WCHAR);
2711 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2713 len += sizeof(WCHAR);
2714 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2715 ret = FALSE;
2717 else if (lpBuffer)
2719 if (headers)
2720 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2721 else
2723 len = strlenW(szCrLf) * sizeof(WCHAR);
2724 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2726 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2727 ret = TRUE;
2729 *lpdwBufferLength = len;
2731 if (request_only)
2732 HeapFree(GetProcessHeap(), 0, headers);
2733 return ret;
2735 case HTTP_QUERY_RAW_HEADERS:
2737 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2738 DWORD i, size = 0;
2739 LPWSTR pszString = lpBuffer;
2741 for (i = 0; ppszRawHeaderLines[i]; i++)
2742 size += strlenW(ppszRawHeaderLines[i]) + 1;
2744 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2746 HTTP_FreeTokens(ppszRawHeaderLines);
2747 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2748 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2749 return FALSE;
2751 if (pszString)
2753 for (i = 0; ppszRawHeaderLines[i]; i++)
2755 DWORD len = strlenW(ppszRawHeaderLines[i]);
2756 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2757 pszString += len+1;
2759 *pszString = '\0';
2760 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2762 *lpdwBufferLength = size * sizeof(WCHAR);
2763 HTTP_FreeTokens(ppszRawHeaderLines);
2765 return TRUE;
2767 case HTTP_QUERY_STATUS_TEXT:
2768 if (lpwhr->lpszStatusText)
2770 DWORD len = strlenW(lpwhr->lpszStatusText);
2771 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2773 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2774 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2775 return FALSE;
2777 if (lpBuffer)
2779 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2780 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2782 *lpdwBufferLength = len * sizeof(WCHAR);
2783 return TRUE;
2785 break;
2786 case HTTP_QUERY_VERSION:
2787 if (lpwhr->lpszVersion)
2789 DWORD len = strlenW(lpwhr->lpszVersion);
2790 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2792 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2793 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2794 return FALSE;
2796 if (lpBuffer)
2798 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2799 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2801 *lpdwBufferLength = len * sizeof(WCHAR);
2802 return TRUE;
2804 break;
2805 case HTTP_QUERY_CONTENT_ENCODING:
2806 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2807 requested_index,request_only);
2808 break;
2809 default:
2810 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2812 if (level < LAST_TABLE_HEADER && header_lookup[level])
2813 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2814 requested_index,request_only);
2817 if (index >= 0)
2818 lphttpHdr = &lpwhr->pCustHeaders[index];
2820 /* Ensure header satisfies requested attributes */
2821 if (!lphttpHdr ||
2822 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2823 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2825 INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
2826 return bSuccess;
2829 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2831 /* coalesce value to requested type */
2832 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2834 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2835 TRACE(" returning number: %d\n", *(int *)lpBuffer);
2836 bSuccess = TRUE;
2838 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2840 time_t tmpTime;
2841 struct tm tmpTM;
2842 SYSTEMTIME *STHook;
2844 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2846 tmpTM = *gmtime(&tmpTime);
2847 STHook = (SYSTEMTIME *)lpBuffer;
2848 STHook->wDay = tmpTM.tm_mday;
2849 STHook->wHour = tmpTM.tm_hour;
2850 STHook->wMilliseconds = 0;
2851 STHook->wMinute = tmpTM.tm_min;
2852 STHook->wDayOfWeek = tmpTM.tm_wday;
2853 STHook->wMonth = tmpTM.tm_mon + 1;
2854 STHook->wSecond = tmpTM.tm_sec;
2855 STHook->wYear = tmpTM.tm_year;
2856 bSuccess = TRUE;
2858 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
2859 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
2860 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
2862 else if (lphttpHdr->lpszValue)
2864 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
2866 if (len > *lpdwBufferLength)
2868 *lpdwBufferLength = len;
2869 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2870 return bSuccess;
2872 if (lpBuffer)
2874 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
2875 TRACE(" returning string: %s\n", debugstr_w(lpBuffer));
2877 *lpdwBufferLength = len - sizeof(WCHAR);
2878 bSuccess = TRUE;
2880 return bSuccess;
2883 /***********************************************************************
2884 * HttpQueryInfoW (WININET.@)
2886 * Queries for information about an HTTP request
2888 * RETURNS
2889 * TRUE on success
2890 * FALSE on failure
2893 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
2894 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2896 BOOL bSuccess = FALSE;
2897 http_request_t *lpwhr;
2899 if (TRACE_ON(wininet)) {
2900 #define FE(x) { x, #x }
2901 static const wininet_flag_info query_flags[] = {
2902 FE(HTTP_QUERY_MIME_VERSION),
2903 FE(HTTP_QUERY_CONTENT_TYPE),
2904 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
2905 FE(HTTP_QUERY_CONTENT_ID),
2906 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
2907 FE(HTTP_QUERY_CONTENT_LENGTH),
2908 FE(HTTP_QUERY_CONTENT_LANGUAGE),
2909 FE(HTTP_QUERY_ALLOW),
2910 FE(HTTP_QUERY_PUBLIC),
2911 FE(HTTP_QUERY_DATE),
2912 FE(HTTP_QUERY_EXPIRES),
2913 FE(HTTP_QUERY_LAST_MODIFIED),
2914 FE(HTTP_QUERY_MESSAGE_ID),
2915 FE(HTTP_QUERY_URI),
2916 FE(HTTP_QUERY_DERIVED_FROM),
2917 FE(HTTP_QUERY_COST),
2918 FE(HTTP_QUERY_LINK),
2919 FE(HTTP_QUERY_PRAGMA),
2920 FE(HTTP_QUERY_VERSION),
2921 FE(HTTP_QUERY_STATUS_CODE),
2922 FE(HTTP_QUERY_STATUS_TEXT),
2923 FE(HTTP_QUERY_RAW_HEADERS),
2924 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
2925 FE(HTTP_QUERY_CONNECTION),
2926 FE(HTTP_QUERY_ACCEPT),
2927 FE(HTTP_QUERY_ACCEPT_CHARSET),
2928 FE(HTTP_QUERY_ACCEPT_ENCODING),
2929 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
2930 FE(HTTP_QUERY_AUTHORIZATION),
2931 FE(HTTP_QUERY_CONTENT_ENCODING),
2932 FE(HTTP_QUERY_FORWARDED),
2933 FE(HTTP_QUERY_FROM),
2934 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
2935 FE(HTTP_QUERY_LOCATION),
2936 FE(HTTP_QUERY_ORIG_URI),
2937 FE(HTTP_QUERY_REFERER),
2938 FE(HTTP_QUERY_RETRY_AFTER),
2939 FE(HTTP_QUERY_SERVER),
2940 FE(HTTP_QUERY_TITLE),
2941 FE(HTTP_QUERY_USER_AGENT),
2942 FE(HTTP_QUERY_WWW_AUTHENTICATE),
2943 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
2944 FE(HTTP_QUERY_ACCEPT_RANGES),
2945 FE(HTTP_QUERY_SET_COOKIE),
2946 FE(HTTP_QUERY_COOKIE),
2947 FE(HTTP_QUERY_REQUEST_METHOD),
2948 FE(HTTP_QUERY_REFRESH),
2949 FE(HTTP_QUERY_CONTENT_DISPOSITION),
2950 FE(HTTP_QUERY_AGE),
2951 FE(HTTP_QUERY_CACHE_CONTROL),
2952 FE(HTTP_QUERY_CONTENT_BASE),
2953 FE(HTTP_QUERY_CONTENT_LOCATION),
2954 FE(HTTP_QUERY_CONTENT_MD5),
2955 FE(HTTP_QUERY_CONTENT_RANGE),
2956 FE(HTTP_QUERY_ETAG),
2957 FE(HTTP_QUERY_HOST),
2958 FE(HTTP_QUERY_IF_MATCH),
2959 FE(HTTP_QUERY_IF_NONE_MATCH),
2960 FE(HTTP_QUERY_IF_RANGE),
2961 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
2962 FE(HTTP_QUERY_MAX_FORWARDS),
2963 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
2964 FE(HTTP_QUERY_RANGE),
2965 FE(HTTP_QUERY_TRANSFER_ENCODING),
2966 FE(HTTP_QUERY_UPGRADE),
2967 FE(HTTP_QUERY_VARY),
2968 FE(HTTP_QUERY_VIA),
2969 FE(HTTP_QUERY_WARNING),
2970 FE(HTTP_QUERY_CUSTOM)
2972 static const wininet_flag_info modifier_flags[] = {
2973 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
2974 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
2975 FE(HTTP_QUERY_FLAG_NUMBER),
2976 FE(HTTP_QUERY_FLAG_COALESCE)
2978 #undef FE
2979 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
2980 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
2981 DWORD i;
2983 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, dwInfoLevel);
2984 TRACE(" Attribute:");
2985 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
2986 if (query_flags[i].val == info) {
2987 TRACE(" %s", query_flags[i].name);
2988 break;
2991 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
2992 TRACE(" Unknown (%08x)", info);
2995 TRACE(" Modifier:");
2996 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
2997 if (modifier_flags[i].val & info_mod) {
2998 TRACE(" %s", modifier_flags[i].name);
2999 info_mod &= ~ modifier_flags[i].val;
3003 if (info_mod) {
3004 TRACE(" Unknown (%08x)", info_mod);
3006 TRACE("\n");
3009 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3010 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3012 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3013 goto lend;
3016 if (lpBuffer == NULL)
3017 *lpdwBufferLength = 0;
3018 bSuccess = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
3019 lpBuffer, lpdwBufferLength, lpdwIndex);
3021 lend:
3022 if( lpwhr )
3023 WININET_Release( &lpwhr->hdr );
3025 TRACE("%d <--\n", bSuccess);
3026 return bSuccess;
3029 /***********************************************************************
3030 * HttpQueryInfoA (WININET.@)
3032 * Queries for information about an HTTP request
3034 * RETURNS
3035 * TRUE on success
3036 * FALSE on failure
3039 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3040 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3042 BOOL result;
3043 DWORD len;
3044 WCHAR* bufferW;
3046 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3047 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3049 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3050 lpdwBufferLength, lpdwIndex );
3053 if (lpBuffer)
3055 DWORD alloclen;
3056 len = (*lpdwBufferLength)*sizeof(WCHAR);
3057 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3059 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3060 if (alloclen < len)
3061 alloclen = len;
3063 else
3064 alloclen = len;
3065 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3066 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3067 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3068 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3069 } else
3071 bufferW = NULL;
3072 len = 0;
3075 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3076 &len, lpdwIndex );
3077 if( result )
3079 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3080 lpBuffer, *lpdwBufferLength, NULL, NULL );
3081 *lpdwBufferLength = len - 1;
3083 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3085 else
3086 /* since the strings being returned from HttpQueryInfoW should be
3087 * only ASCII characters, it is reasonable to assume that all of
3088 * the Unicode characters can be reduced to a single byte */
3089 *lpdwBufferLength = len / sizeof(WCHAR);
3091 HeapFree(GetProcessHeap(), 0, bufferW );
3093 return result;
3096 /***********************************************************************
3097 * HttpSendRequestExA (WININET.@)
3099 * Sends the specified request to the HTTP server and allows chunked
3100 * transfers.
3102 * RETURNS
3103 * Success: TRUE
3104 * Failure: FALSE, call GetLastError() for more information.
3106 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3107 LPINTERNET_BUFFERSA lpBuffersIn,
3108 LPINTERNET_BUFFERSA lpBuffersOut,
3109 DWORD dwFlags, DWORD_PTR dwContext)
3111 INTERNET_BUFFERSW BuffersInW;
3112 BOOL rc = FALSE;
3113 DWORD headerlen;
3114 LPWSTR header = NULL;
3116 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3117 lpBuffersOut, dwFlags, dwContext);
3119 if (lpBuffersIn)
3121 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3122 if (lpBuffersIn->lpcszHeader)
3124 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3125 lpBuffersIn->dwHeadersLength,0,0);
3126 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3127 if (!(BuffersInW.lpcszHeader = header))
3129 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3130 return FALSE;
3132 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3133 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3134 header, headerlen);
3136 else
3137 BuffersInW.lpcszHeader = NULL;
3138 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3139 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3140 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3141 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3142 BuffersInW.Next = NULL;
3145 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3147 HeapFree(GetProcessHeap(),0,header);
3149 return rc;
3152 /***********************************************************************
3153 * HttpSendRequestExW (WININET.@)
3155 * Sends the specified request to the HTTP server and allows chunked
3156 * transfers
3158 * RETURNS
3159 * Success: TRUE
3160 * Failure: FALSE, call GetLastError() for more information.
3162 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3163 LPINTERNET_BUFFERSW lpBuffersIn,
3164 LPINTERNET_BUFFERSW lpBuffersOut,
3165 DWORD dwFlags, DWORD_PTR dwContext)
3167 BOOL ret = FALSE;
3168 http_request_t *lpwhr;
3169 http_session_t *lpwhs;
3170 appinfo_t *hIC;
3172 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3173 lpBuffersOut, dwFlags, dwContext);
3175 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3177 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3179 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3180 goto lend;
3183 lpwhs = lpwhr->lpHttpSession;
3184 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3185 hIC = lpwhs->lpAppInfo;
3186 assert(hIC->hdr.htype == WH_HINIT);
3188 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3190 WORKREQUEST workRequest;
3191 struct WORKREQ_HTTPSENDREQUESTW *req;
3193 workRequest.asyncproc = AsyncHttpSendRequestProc;
3194 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3195 req = &workRequest.u.HttpSendRequestW;
3196 if (lpBuffersIn)
3198 /* FIXME: this should use dwHeadersLength or may not be necessary at all */
3199 req->lpszHeader = heap_strdupW(lpBuffersIn->lpcszHeader);
3200 req->dwHeaderLength = lpBuffersIn->dwHeadersLength;
3201 req->lpOptional = lpBuffersIn->lpvBuffer;
3202 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3203 req->dwContentLength = lpBuffersIn->dwBufferTotal;
3205 else
3207 req->lpszHeader = NULL;
3208 req->dwHeaderLength = 0;
3209 req->lpOptional = NULL;
3210 req->dwOptionalLength = 0;
3211 req->dwContentLength = 0;
3214 req->bEndRequest = FALSE;
3216 INTERNET_AsyncCall(&workRequest);
3218 * This is from windows.
3220 INTERNET_SetLastError(ERROR_IO_PENDING);
3222 else
3224 if (lpBuffersIn)
3225 ret = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3226 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
3227 lpBuffersIn->dwBufferTotal, FALSE);
3228 else
3229 ret = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
3232 lend:
3233 if ( lpwhr )
3234 WININET_Release( &lpwhr->hdr );
3236 TRACE("<---\n");
3237 return ret;
3240 /***********************************************************************
3241 * HttpSendRequestW (WININET.@)
3243 * Sends the specified request to the HTTP server
3245 * RETURNS
3246 * TRUE on success
3247 * FALSE on failure
3250 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
3251 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3253 http_request_t *lpwhr;
3254 http_session_t *lpwhs = NULL;
3255 appinfo_t *hIC = NULL;
3256 BOOL r;
3258 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
3259 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
3261 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3262 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3264 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3265 r = FALSE;
3266 goto lend;
3269 lpwhs = lpwhr->lpHttpSession;
3270 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
3272 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3273 r = FALSE;
3274 goto lend;
3277 hIC = lpwhs->lpAppInfo;
3278 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
3280 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3281 r = FALSE;
3282 goto lend;
3285 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3287 WORKREQUEST workRequest;
3288 struct WORKREQ_HTTPSENDREQUESTW *req;
3290 workRequest.asyncproc = AsyncHttpSendRequestProc;
3291 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3292 req = &workRequest.u.HttpSendRequestW;
3293 if (lpszHeaders)
3295 DWORD size;
3297 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
3298 else size = dwHeaderLength * sizeof(WCHAR);
3300 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
3301 memcpy(req->lpszHeader, lpszHeaders, size);
3303 else
3304 req->lpszHeader = 0;
3305 req->dwHeaderLength = dwHeaderLength;
3306 req->lpOptional = lpOptional;
3307 req->dwOptionalLength = dwOptionalLength;
3308 req->dwContentLength = dwOptionalLength;
3309 req->bEndRequest = TRUE;
3311 INTERNET_AsyncCall(&workRequest);
3313 * This is from windows.
3315 INTERNET_SetLastError(ERROR_IO_PENDING);
3316 r = FALSE;
3318 else
3320 r = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
3321 dwHeaderLength, lpOptional, dwOptionalLength,
3322 dwOptionalLength, TRUE);
3324 lend:
3325 if( lpwhr )
3326 WININET_Release( &lpwhr->hdr );
3327 return r;
3330 /***********************************************************************
3331 * HttpSendRequestA (WININET.@)
3333 * Sends the specified request to the HTTP server
3335 * RETURNS
3336 * TRUE on success
3337 * FALSE on failure
3340 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
3341 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3343 BOOL result;
3344 LPWSTR szHeaders=NULL;
3345 DWORD nLen=dwHeaderLength;
3346 if(lpszHeaders!=NULL)
3348 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
3349 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
3350 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
3352 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
3353 HeapFree(GetProcessHeap(),0,szHeaders);
3354 return result;
3357 /***********************************************************************
3358 * HTTP_GetRedirectURL (internal)
3360 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3362 static WCHAR szHttp[] = {'h','t','t','p',0};
3363 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3364 http_session_t *lpwhs = lpwhr->lpHttpSession;
3365 URL_COMPONENTSW urlComponents;
3366 DWORD url_length = 0;
3367 LPWSTR orig_url;
3368 LPWSTR combined_url;
3370 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3371 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3372 urlComponents.dwSchemeLength = 0;
3373 urlComponents.lpszHostName = lpwhs->lpszHostName;
3374 urlComponents.dwHostNameLength = 0;
3375 urlComponents.nPort = lpwhs->nHostPort;
3376 urlComponents.lpszUserName = lpwhs->lpszUserName;
3377 urlComponents.dwUserNameLength = 0;
3378 urlComponents.lpszPassword = NULL;
3379 urlComponents.dwPasswordLength = 0;
3380 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3381 urlComponents.dwUrlPathLength = 0;
3382 urlComponents.lpszExtraInfo = NULL;
3383 urlComponents.dwExtraInfoLength = 0;
3385 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3386 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3387 return NULL;
3389 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3391 /* convert from bytes to characters */
3392 url_length = url_length / sizeof(WCHAR) - 1;
3393 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3395 HeapFree(GetProcessHeap(), 0, orig_url);
3396 return NULL;
3399 url_length = 0;
3400 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3401 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3403 HeapFree(GetProcessHeap(), 0, orig_url);
3404 return NULL;
3406 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3408 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3410 HeapFree(GetProcessHeap(), 0, orig_url);
3411 HeapFree(GetProcessHeap(), 0, combined_url);
3412 return NULL;
3414 HeapFree(GetProcessHeap(), 0, orig_url);
3415 return combined_url;
3419 /***********************************************************************
3420 * HTTP_HandleRedirect (internal)
3422 static BOOL HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3424 http_session_t *lpwhs = lpwhr->lpHttpSession;
3425 appinfo_t *hIC = lpwhs->lpAppInfo;
3426 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3427 WCHAR path[INTERNET_MAX_URL_LENGTH];
3428 int index;
3430 if(lpszUrl[0]=='/')
3432 /* if it's an absolute path, keep the same session info */
3433 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3435 else
3437 URL_COMPONENTSW urlComponents;
3438 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3439 static WCHAR szHttp[] = {'h','t','t','p',0};
3440 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3442 userName[0] = 0;
3443 hostName[0] = 0;
3444 protocol[0] = 0;
3446 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3447 urlComponents.lpszScheme = protocol;
3448 urlComponents.dwSchemeLength = 32;
3449 urlComponents.lpszHostName = hostName;
3450 urlComponents.dwHostNameLength = MAXHOSTNAME;
3451 urlComponents.lpszUserName = userName;
3452 urlComponents.dwUserNameLength = 1024;
3453 urlComponents.lpszPassword = NULL;
3454 urlComponents.dwPasswordLength = 0;
3455 urlComponents.lpszUrlPath = path;
3456 urlComponents.dwUrlPathLength = 2048;
3457 urlComponents.lpszExtraInfo = NULL;
3458 urlComponents.dwExtraInfoLength = 0;
3459 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3460 return FALSE;
3462 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3463 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3465 TRACE("redirect from secure page to non-secure page\n");
3466 /* FIXME: warn about from secure redirect to non-secure page */
3467 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3469 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3470 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3472 TRACE("redirect from non-secure page to secure page\n");
3473 /* FIXME: notify about redirect to secure page */
3474 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3477 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3479 if (lstrlenW(protocol)>4) /*https*/
3480 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3481 else /*http*/
3482 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3485 #if 0
3487 * This upsets redirects to binary files on sourceforge.net
3488 * and gives an html page instead of the target file
3489 * Examination of the HTTP request sent by native wininet.dll
3490 * reveals that it doesn't send a referrer in that case.
3491 * Maybe there's a flag that enables this, or maybe a referrer
3492 * shouldn't be added in case of a redirect.
3495 /* consider the current host as the referrer */
3496 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3497 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3498 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3499 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3500 #endif
3502 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3503 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3504 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3506 int len;
3507 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3508 len = lstrlenW(hostName);
3509 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3510 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3511 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3513 else
3514 lpwhs->lpszHostName = heap_strdupW(hostName);
3516 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3518 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3519 lpwhs->lpszUserName = NULL;
3520 if (userName[0])
3521 lpwhs->lpszUserName = heap_strdupW(userName);
3523 if (!using_proxy)
3525 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3527 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3528 lpwhs->lpszServerName = heap_strdupW(hostName);
3529 lpwhs->nServerPort = urlComponents.nPort;
3531 NETCON_close(&lpwhr->netConnection);
3532 if (!HTTP_ResolveName(lpwhr)) return FALSE;
3533 if (!NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)) return FALSE;
3534 lpwhr->read_pos = lpwhr->read_size = 0;
3535 lpwhr->read_chunked = FALSE;
3538 else
3539 TRACE("Redirect through proxy\n");
3542 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3543 lpwhr->lpszPath=NULL;
3544 if (*path)
3546 DWORD needed = 0;
3547 HRESULT rc;
3549 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3550 if (rc != E_POINTER)
3551 needed = strlenW(path)+1;
3552 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3553 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3554 URL_ESCAPE_SPACES_ONLY);
3555 if (rc != S_OK)
3557 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3558 strcpyW(lpwhr->lpszPath,path);
3562 /* Remove custom content-type/length headers on redirects. */
3563 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3564 if (0 <= index)
3565 HTTP_DeleteCustomHeader(lpwhr, index);
3566 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3567 if (0 <= index)
3568 HTTP_DeleteCustomHeader(lpwhr, index);
3570 return TRUE;
3573 /***********************************************************************
3574 * HTTP_build_req (internal)
3576 * concatenate all the strings in the request together
3578 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3580 LPCWSTR *t;
3581 LPWSTR str;
3583 for( t = list; *t ; t++ )
3584 len += strlenW( *t );
3585 len++;
3587 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3588 *str = 0;
3590 for( t = list; *t ; t++ )
3591 strcatW( str, *t );
3593 return str;
3596 static BOOL HTTP_SecureProxyConnect(http_request_t *lpwhr)
3598 LPWSTR lpszPath;
3599 LPWSTR requestString;
3600 INT len;
3601 INT cnt;
3602 INT responseLen;
3603 char *ascii_req;
3604 BOOL ret;
3605 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3606 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3607 http_session_t *lpwhs = lpwhr->lpHttpSession;
3609 TRACE("\n");
3611 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3612 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3613 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3614 HeapFree( GetProcessHeap(), 0, lpszPath );
3616 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3617 NULL, 0, NULL, NULL );
3618 len--; /* the nul terminator isn't needed */
3619 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3620 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3621 ascii_req, len, NULL, NULL );
3622 HeapFree( GetProcessHeap(), 0, requestString );
3624 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3626 ret = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3627 HeapFree( GetProcessHeap(), 0, ascii_req );
3628 if (!ret || cnt < 0)
3629 return FALSE;
3631 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3632 if (!responseLen)
3633 return FALSE;
3635 return TRUE;
3638 static void HTTP_InsertCookies(http_request_t *lpwhr)
3640 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3641 LPWSTR lpszCookies, lpszUrl = NULL;
3642 DWORD nCookieSize, size;
3643 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3645 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3646 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3647 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3649 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3651 int cnt = 0;
3652 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3654 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3655 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3657 cnt += sprintfW(lpszCookies, szCookie);
3658 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3659 strcatW(lpszCookies, szCrLf);
3661 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3662 HeapFree(GetProcessHeap(), 0, lpszCookies);
3665 HeapFree(GetProcessHeap(), 0, lpszUrl);
3668 /***********************************************************************
3669 * HTTP_HttpSendRequestW (internal)
3671 * Sends the specified request to the HTTP server
3673 * RETURNS
3674 * TRUE on success
3675 * FALSE on failure
3678 BOOL WINAPI HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3679 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3680 DWORD dwContentLength, BOOL bEndRequest)
3682 INT cnt;
3683 BOOL bSuccess = FALSE, redirected = FALSE;
3684 LPWSTR requestString = NULL;
3685 INT responseLen;
3686 BOOL loop_next;
3687 INTERNET_ASYNC_RESULT iar;
3688 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3689 static const WCHAR szContentLength[] =
3690 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3691 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3693 TRACE("--> %p\n", lpwhr);
3695 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3697 /* if the verb is NULL default to GET */
3698 if (!lpwhr->lpszVerb)
3699 lpwhr->lpszVerb = heap_strdupW(szGET);
3701 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3703 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3704 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3705 lpwhr->dwBytesToWrite = dwContentLength;
3707 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3709 WCHAR *agent_header;
3710 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3711 int len;
3713 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3714 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3715 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3717 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3718 HeapFree(GetProcessHeap(), 0, agent_header);
3720 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3722 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3723 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3725 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3727 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3728 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3729 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3734 DWORD len;
3735 char *ascii_req;
3737 loop_next = FALSE;
3739 /* like native, just in case the caller forgot to call InternetReadFile
3740 * for all the data */
3741 HTTP_DrainContent(lpwhr);
3742 lpwhr->dwContentRead = 0;
3744 if (TRACE_ON(wininet))
3746 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3747 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3750 HTTP_FixURL(lpwhr);
3751 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3753 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3755 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3756 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3758 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3759 HTTP_InsertCookies(lpwhr);
3761 /* add the headers the caller supplied */
3762 if( lpszHeaders && dwHeaderLength )
3764 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3765 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3768 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3770 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3771 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3772 HeapFree(GetProcessHeap(), 0, url);
3774 else
3775 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3778 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3780 /* Send the request and store the results */
3781 if (!HTTP_OpenConnection(lpwhr))
3782 goto lend;
3784 /* send the request as ASCII, tack on the optional data */
3785 if (!lpOptional || redirected)
3786 dwOptionalLength = 0;
3787 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3788 NULL, 0, NULL, NULL );
3789 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3790 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3791 ascii_req, len, NULL, NULL );
3792 if( lpOptional )
3793 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3794 len = (len + dwOptionalLength - 1);
3795 ascii_req[len] = 0;
3796 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3798 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3799 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3801 NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3802 HeapFree( GetProcessHeap(), 0, ascii_req );
3804 lpwhr->dwBytesWritten = dwOptionalLength;
3806 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3807 INTERNET_STATUS_REQUEST_SENT,
3808 &len, sizeof(DWORD));
3810 if (bEndRequest)
3812 DWORD dwBufferSize;
3813 DWORD dwStatusCode;
3815 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3816 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3818 if (cnt < 0)
3819 goto lend;
3821 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3822 if (responseLen)
3823 bSuccess = TRUE;
3825 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3826 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3827 sizeof(DWORD));
3829 HTTP_ProcessCookies(lpwhr);
3831 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3833 dwBufferSize = sizeof(dwStatusCode);
3834 if (!HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3835 &dwStatusCode,&dwBufferSize,NULL))
3836 dwStatusCode = 0;
3838 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && bSuccess)
3840 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3841 dwBufferSize=sizeof(szNewLocation);
3842 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
3843 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL))
3845 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3847 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3848 lpwhr->lpszVerb = heap_strdupW(szGET);
3850 HTTP_DrainContent(lpwhr);
3851 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3853 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3854 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3855 bSuccess = HTTP_HandleRedirect(lpwhr, new_url);
3856 if (bSuccess)
3858 HeapFree(GetProcessHeap(), 0, requestString);
3859 loop_next = TRUE;
3861 HeapFree( GetProcessHeap(), 0, new_url );
3863 redirected = TRUE;
3866 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && bSuccess)
3868 WCHAR szAuthValue[2048];
3869 dwBufferSize=2048;
3870 if (dwStatusCode == HTTP_STATUS_DENIED)
3872 DWORD dwIndex = 0;
3873 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
3875 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3876 &lpwhr->pAuthInfo,
3877 lpwhr->lpHttpSession->lpszUserName,
3878 lpwhr->lpHttpSession->lpszPassword))
3880 loop_next = TRUE;
3881 break;
3885 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3887 DWORD dwIndex = 0;
3888 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
3890 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3891 &lpwhr->pProxyAuthInfo,
3892 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3893 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword))
3895 loop_next = TRUE;
3896 break;
3902 else
3903 bSuccess = TRUE;
3905 while (loop_next);
3907 if(bSuccess) {
3908 WCHAR url[INTERNET_MAX_URL_LENGTH];
3909 WCHAR cacheFileName[MAX_PATH+1];
3910 BOOL b;
3912 b = HTTP_GetRequestURL(lpwhr, url);
3913 if(!b) {
3914 WARN("Could not get URL\n");
3915 goto lend;
3918 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3919 if(b) {
3920 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3921 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3922 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3923 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3924 WARN("Could not create file: %u\n", GetLastError());
3925 lpwhr->hCacheFile = NULL;
3927 }else {
3928 WARN("Could not create cache entry: %08x\n", GetLastError());
3932 lend:
3934 HeapFree(GetProcessHeap(), 0, requestString);
3936 /* TODO: send notification for P3P header */
3938 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3940 if (bSuccess)
3942 if (lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite) HTTP_ReceiveRequestData(lpwhr, TRUE);
3943 else
3945 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3946 iar.dwError = 0;
3948 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3949 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3950 sizeof(INTERNET_ASYNC_RESULT));
3953 else
3955 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3956 iar.dwError = INTERNET_GetLastError();
3958 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3959 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3960 sizeof(INTERNET_ASYNC_RESULT));
3964 TRACE("<--\n");
3965 if (bSuccess) INTERNET_SetLastError(ERROR_SUCCESS);
3966 return bSuccess;
3969 /***********************************************************************
3970 * HTTPSESSION_Destroy (internal)
3972 * Deallocate session handle
3975 static void HTTPSESSION_Destroy(object_header_t *hdr)
3977 http_session_t *lpwhs = (http_session_t*) hdr;
3979 TRACE("%p\n", lpwhs);
3981 WININET_Release(&lpwhs->lpAppInfo->hdr);
3983 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3984 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3985 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
3986 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3987 HeapFree(GetProcessHeap(), 0, lpwhs);
3990 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3992 switch(option) {
3993 case INTERNET_OPTION_HANDLE_TYPE:
3994 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3996 if (*size < sizeof(ULONG))
3997 return ERROR_INSUFFICIENT_BUFFER;
3999 *size = sizeof(DWORD);
4000 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4001 return ERROR_SUCCESS;
4004 return INET_QueryOption(option, buffer, size, unicode);
4007 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4009 http_session_t *ses = (http_session_t*)hdr;
4011 switch(option) {
4012 case INTERNET_OPTION_USERNAME:
4014 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4015 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4016 return ERROR_SUCCESS;
4018 case INTERNET_OPTION_PASSWORD:
4020 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4021 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4022 return ERROR_SUCCESS;
4024 default: break;
4027 return ERROR_INTERNET_INVALID_OPTION;
4030 static const object_vtbl_t HTTPSESSIONVtbl = {
4031 HTTPSESSION_Destroy,
4032 NULL,
4033 HTTPSESSION_QueryOption,
4034 HTTPSESSION_SetOption,
4035 NULL,
4036 NULL,
4037 NULL,
4038 NULL,
4039 NULL
4043 /***********************************************************************
4044 * HTTP_Connect (internal)
4046 * Create http session handle
4048 * RETURNS
4049 * HINTERNET a session handle on success
4050 * NULL on failure
4053 HINTERNET HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4054 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4055 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4056 DWORD dwInternalFlags)
4058 http_session_t *lpwhs = NULL;
4059 HINTERNET handle = NULL;
4061 TRACE("-->\n");
4063 if (!lpszServerName || !lpszServerName[0])
4065 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4066 goto lerror;
4069 assert( hIC->hdr.htype == WH_HINIT );
4071 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4072 if (NULL == lpwhs)
4074 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4075 goto lerror;
4079 * According to my tests. The name is not resolved until a request is sent
4082 lpwhs->hdr.htype = WH_HHTTPSESSION;
4083 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4084 lpwhs->hdr.dwFlags = dwFlags;
4085 lpwhs->hdr.dwContext = dwContext;
4086 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4087 lpwhs->hdr.refs = 1;
4088 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4090 WININET_AddRef( &hIC->hdr );
4091 lpwhs->lpAppInfo = hIC;
4092 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4094 handle = WININET_AllocHandle( &lpwhs->hdr );
4095 if (NULL == handle)
4097 ERR("Failed to alloc handle\n");
4098 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4099 goto lerror;
4102 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4103 if(strchrW(hIC->lpszProxy, ' '))
4104 FIXME("Several proxies not implemented.\n");
4105 if(hIC->lpszProxyBypass)
4106 FIXME("Proxy bypass is ignored.\n");
4108 if (lpszServerName && lpszServerName[0])
4110 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4111 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4113 if (lpszUserName && lpszUserName[0])
4114 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4115 if (lpszPassword && lpszPassword[0])
4116 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4117 lpwhs->nServerPort = nServerPort;
4118 lpwhs->nHostPort = nServerPort;
4120 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4121 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4123 INTERNET_SendCallback(&hIC->hdr, dwContext,
4124 INTERNET_STATUS_HANDLE_CREATED, &handle,
4125 sizeof(handle));
4128 lerror:
4129 if( lpwhs )
4130 WININET_Release( &lpwhs->hdr );
4133 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4134 * windows
4137 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4138 return handle;
4142 /***********************************************************************
4143 * HTTP_OpenConnection (internal)
4145 * Connect to a web server
4147 * RETURNS
4149 * TRUE on success
4150 * FALSE on failure
4152 static BOOL HTTP_OpenConnection(http_request_t *lpwhr)
4154 BOOL bSuccess = FALSE;
4155 http_session_t *lpwhs;
4156 appinfo_t *hIC = NULL;
4157 char szaddr[INET6_ADDRSTRLEN];
4158 const void *addr;
4160 TRACE("-->\n");
4163 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4165 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4166 goto lend;
4169 if (NETCON_connected(&lpwhr->netConnection))
4171 bSuccess = TRUE;
4172 goto lend;
4174 if (!HTTP_ResolveName(lpwhr)) goto lend;
4176 lpwhs = lpwhr->lpHttpSession;
4178 hIC = lpwhs->lpAppInfo;
4179 switch (lpwhs->socketAddress.ss_family)
4181 case AF_INET:
4182 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4183 break;
4184 case AF_INET6:
4185 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4186 break;
4187 default:
4188 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4189 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
4190 return FALSE;
4192 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4193 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4194 INTERNET_STATUS_CONNECTING_TO_SERVER,
4195 szaddr,
4196 strlen(szaddr)+1);
4198 if (!NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family,
4199 SOCK_STREAM, 0))
4201 WARN("Socket creation failed: %u\n", INTERNET_GetLastError());
4202 goto lend;
4205 if (!NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4206 lpwhs->sa_len))
4207 goto lend;
4209 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4211 /* Note: we differ from Microsoft's WinINet here. they seem to have
4212 * a bug that causes no status callbacks to be sent when starting
4213 * a tunnel to a proxy server using the CONNECT verb. i believe our
4214 * behaviour to be more correct and to not cause any incompatibilities
4215 * because using a secure connection through a proxy server is a rare
4216 * case that would be hard for anyone to depend on */
4217 if (hIC->lpszProxy && !HTTP_SecureProxyConnect(lpwhr))
4218 goto lend;
4220 if (!NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName))
4222 WARN("Couldn't connect securely to host\n");
4223 goto lend;
4227 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4228 INTERNET_STATUS_CONNECTED_TO_SERVER,
4229 szaddr, strlen(szaddr)+1);
4231 bSuccess = TRUE;
4233 lend:
4234 lpwhr->read_pos = lpwhr->read_size = 0;
4235 lpwhr->read_chunked = FALSE;
4237 TRACE("%d <--\n", bSuccess);
4238 return bSuccess;
4242 /***********************************************************************
4243 * HTTP_clear_response_headers (internal)
4245 * clear out any old response headers
4247 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4249 DWORD i;
4251 for( i=0; i<lpwhr->nCustHeaders; i++)
4253 if( !lpwhr->pCustHeaders[i].lpszField )
4254 continue;
4255 if( !lpwhr->pCustHeaders[i].lpszValue )
4256 continue;
4257 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4258 continue;
4259 HTTP_DeleteCustomHeader( lpwhr, i );
4260 i--;
4264 /***********************************************************************
4265 * HTTP_GetResponseHeaders (internal)
4267 * Read server response
4269 * RETURNS
4271 * TRUE on success
4272 * FALSE on error
4274 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4276 INT cbreaks = 0;
4277 WCHAR buffer[MAX_REPLY_LEN];
4278 DWORD buflen = MAX_REPLY_LEN;
4279 BOOL bSuccess = FALSE;
4280 INT rc = 0;
4281 char bufferA[MAX_REPLY_LEN];
4282 LPWSTR status_code = NULL, status_text = NULL;
4283 DWORD cchMaxRawHeaders = 1024;
4284 LPWSTR lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4285 LPWSTR temp;
4286 DWORD cchRawHeaders = 0;
4287 BOOL codeHundred = FALSE;
4289 TRACE("-->\n");
4291 /* clear old response headers (eg. from a redirect response) */
4292 if (clear) HTTP_clear_response_headers( lpwhr );
4294 if (!NETCON_connected(&lpwhr->netConnection))
4295 goto lend;
4297 do {
4298 static const WCHAR szHundred[] = {'1','0','0',0};
4300 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4302 buflen = MAX_REPLY_LEN;
4303 if (!read_line(lpwhr, bufferA, &buflen))
4304 goto lend;
4305 rc += buflen;
4306 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4307 /* check is this a status code line? */
4308 if (!strncmpW(buffer, g_szHttp1_0, 4))
4310 /* split the version from the status code */
4311 status_code = strchrW( buffer, ' ' );
4312 if( !status_code )
4313 goto lend;
4314 *status_code++=0;
4316 /* split the status code from the status text */
4317 status_text = strchrW( status_code, ' ' );
4318 if( !status_text )
4319 goto lend;
4320 *status_text++=0;
4322 TRACE("version [%s] status code [%s] status text [%s]\n",
4323 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4325 codeHundred = (!strcmpW(status_code, szHundred));
4327 else if (!codeHundred)
4329 FIXME("Non status line at head of response (%s)\n",debugstr_w(buffer));
4330 continue;
4332 } while (codeHundred);
4334 /* Add status code */
4335 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4336 HTTP_ADDHDR_FLAG_REPLACE);
4338 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4339 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4341 lpwhr->lpszVersion = heap_strdupW(buffer);
4342 lpwhr->lpszStatusText = heap_strdupW(status_text);
4344 /* Restore the spaces */
4345 *(status_code-1) = ' ';
4346 *(status_text-1) = ' ';
4348 /* regenerate raw headers */
4349 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4350 cchMaxRawHeaders *= 2;
4351 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4352 if (temp == NULL) goto lend;
4353 lpszRawHeaders = temp;
4354 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4355 cchRawHeaders += (buflen-1);
4356 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4357 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4358 lpszRawHeaders[cchRawHeaders] = '\0';
4360 /* Parse each response line */
4363 buflen = MAX_REPLY_LEN;
4364 if (read_line(lpwhr, bufferA, &buflen))
4366 LPWSTR * pFieldAndValue;
4368 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4370 if (!bufferA[0]) break;
4371 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4373 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4374 if (pFieldAndValue)
4376 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4377 cchMaxRawHeaders *= 2;
4378 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4379 if (temp == NULL) goto lend;
4380 lpszRawHeaders = temp;
4381 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4382 cchRawHeaders += (buflen-1);
4383 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4384 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4385 lpszRawHeaders[cchRawHeaders] = '\0';
4387 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4388 HTTP_ADDREQ_FLAG_ADD );
4390 HTTP_FreeTokens(pFieldAndValue);
4393 else
4395 cbreaks++;
4396 if (cbreaks >= 2)
4397 break;
4399 }while(1);
4401 /* make sure the response header is terminated with an empty line. Some apps really
4402 truly care about that empty line being there for some reason. Just add it to the
4403 header. */
4404 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4406 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4407 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4408 if (temp == NULL) goto lend;
4409 lpszRawHeaders = temp;
4412 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4414 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4415 lpwhr->lpszRawHeaders = lpszRawHeaders;
4416 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4417 bSuccess = TRUE;
4419 lend:
4421 TRACE("<--\n");
4422 if (bSuccess)
4423 return rc;
4424 else
4426 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4427 return 0;
4432 static void strip_spaces(LPWSTR start)
4434 LPWSTR str = start;
4435 LPWSTR end;
4437 while (*str == ' ' && *str != '\0')
4438 str++;
4440 if (str != start)
4441 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
4443 end = start + strlenW(start) - 1;
4444 while (end >= start && *end == ' ')
4446 *end = '\0';
4447 end--;
4452 /***********************************************************************
4453 * HTTP_InterpretHttpHeader (internal)
4455 * Parse server response
4457 * RETURNS
4459 * Pointer to array of field, value, NULL on success.
4460 * NULL on error.
4462 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4464 LPWSTR * pTokenPair;
4465 LPWSTR pszColon;
4466 INT len;
4468 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4470 pszColon = strchrW(buffer, ':');
4471 /* must have two tokens */
4472 if (!pszColon)
4474 HTTP_FreeTokens(pTokenPair);
4475 if (buffer[0])
4476 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4477 return NULL;
4480 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4481 if (!pTokenPair[0])
4483 HTTP_FreeTokens(pTokenPair);
4484 return NULL;
4486 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4487 pTokenPair[0][pszColon - buffer] = '\0';
4489 /* skip colon */
4490 pszColon++;
4491 len = strlenW(pszColon);
4492 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4493 if (!pTokenPair[1])
4495 HTTP_FreeTokens(pTokenPair);
4496 return NULL;
4498 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4500 strip_spaces(pTokenPair[0]);
4501 strip_spaces(pTokenPair[1]);
4503 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4504 return pTokenPair;
4507 /***********************************************************************
4508 * HTTP_ProcessHeader (internal)
4510 * Stuff header into header tables according to <dwModifier>
4514 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4516 static BOOL HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4518 LPHTTPHEADERW lphttpHdr = NULL;
4519 BOOL bSuccess = FALSE;
4520 INT index = -1;
4521 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4523 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4525 /* REPLACE wins out over ADD */
4526 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4527 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4529 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4530 index = -1;
4531 else
4532 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4534 if (index >= 0)
4536 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4538 return FALSE;
4540 lphttpHdr = &lpwhr->pCustHeaders[index];
4542 else if (value)
4544 HTTPHEADERW hdr;
4546 hdr.lpszField = (LPWSTR)field;
4547 hdr.lpszValue = (LPWSTR)value;
4548 hdr.wFlags = hdr.wCount = 0;
4550 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4551 hdr.wFlags |= HDR_ISREQUEST;
4553 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4555 /* no value to delete */
4556 else return TRUE;
4558 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4559 lphttpHdr->wFlags |= HDR_ISREQUEST;
4560 else
4561 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4563 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4565 HTTP_DeleteCustomHeader( lpwhr, index );
4567 if (value)
4569 HTTPHEADERW hdr;
4571 hdr.lpszField = (LPWSTR)field;
4572 hdr.lpszValue = (LPWSTR)value;
4573 hdr.wFlags = hdr.wCount = 0;
4575 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4576 hdr.wFlags |= HDR_ISREQUEST;
4578 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4581 return TRUE;
4583 else if (dwModifier & COALESCEFLAGS)
4585 LPWSTR lpsztmp;
4586 WCHAR ch = 0;
4587 INT len = 0;
4588 INT origlen = strlenW(lphttpHdr->lpszValue);
4589 INT valuelen = strlenW(value);
4591 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4593 ch = ',';
4594 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4596 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4598 ch = ';';
4599 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4602 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4604 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4605 if (lpsztmp)
4607 lphttpHdr->lpszValue = lpsztmp;
4608 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4609 if (ch > 0)
4611 lphttpHdr->lpszValue[origlen] = ch;
4612 origlen++;
4613 lphttpHdr->lpszValue[origlen] = ' ';
4614 origlen++;
4617 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4618 lphttpHdr->lpszValue[len] = '\0';
4619 bSuccess = TRUE;
4621 else
4623 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4624 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4627 TRACE("<-- %d\n",bSuccess);
4628 return bSuccess;
4632 /***********************************************************************
4633 * HTTP_FinishedReading (internal)
4635 * Called when all content from server has been read by client.
4638 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4640 BOOL keepalive = HTTP_KeepAlive(lpwhr);
4642 TRACE("\n");
4645 if (!keepalive)
4647 HTTPREQ_CloseConnection(&lpwhr->hdr);
4650 /* FIXME: store data in the URL cache here */
4652 return TRUE;
4656 /***********************************************************************
4657 * HTTP_GetCustomHeaderIndex (internal)
4659 * Return index of custom header from header array
4662 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
4663 int requested_index, BOOL request_only)
4665 DWORD index;
4667 TRACE("%s\n", debugstr_w(lpszField));
4669 for (index = 0; index < lpwhr->nCustHeaders; index++)
4671 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
4672 continue;
4674 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4675 continue;
4677 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4678 continue;
4680 if (requested_index == 0)
4681 break;
4682 requested_index --;
4685 if (index >= lpwhr->nCustHeaders)
4686 index = -1;
4688 TRACE("Return: %d\n", index);
4689 return index;
4693 /***********************************************************************
4694 * HTTP_InsertCustomHeader (internal)
4696 * Insert header into array
4699 static BOOL HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
4701 INT count;
4702 LPHTTPHEADERW lph = NULL;
4703 BOOL r = FALSE;
4705 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
4706 count = lpwhr->nCustHeaders + 1;
4707 if (count > 1)
4708 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
4709 else
4710 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
4712 if (NULL != lph)
4714 lpwhr->pCustHeaders = lph;
4715 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
4716 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
4717 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
4718 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
4719 lpwhr->nCustHeaders++;
4720 r = TRUE;
4722 else
4724 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4727 return r;
4731 /***********************************************************************
4732 * HTTP_DeleteCustomHeader (internal)
4734 * Delete header from array
4735 * If this function is called, the indexs may change.
4737 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
4739 if( lpwhr->nCustHeaders <= 0 )
4740 return FALSE;
4741 if( index >= lpwhr->nCustHeaders )
4742 return FALSE;
4743 lpwhr->nCustHeaders--;
4745 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
4746 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
4748 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
4749 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
4750 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
4752 return TRUE;
4756 /***********************************************************************
4757 * HTTP_VerifyValidHeader (internal)
4759 * Verify the given header is not invalid for the given http request
4762 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
4764 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4765 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
4766 return FALSE;
4768 return TRUE;
4771 /***********************************************************************
4772 * IsHostInProxyBypassList (@)
4774 * Undocumented
4777 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
4779 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
4780 return FALSE;