winedbg: Prevent syntax error for setting all debug channels.
[wine/hramrach.git] / dlls / wininet / http.c
blob7e6178c7b61d88c74b7a282a60b9034fa1192460
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 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1549 HeapFree(GetProcessHeap(), 0, lpwhr);
1552 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1554 http_request_t *lpwhr = (http_request_t*) hdr;
1556 TRACE("%p\n",lpwhr);
1558 #ifdef HAVE_ZLIB
1559 if(lpwhr->gzip_stream) {
1560 inflateEnd(&lpwhr->gzip_stream->zstream);
1561 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1562 lpwhr->gzip_stream = NULL;
1564 #endif
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 }else if(zres != Z_OK) {
2061 WARN("inflate failed %d\n", zres);
2062 if(!read)
2063 ret = ERROR_INTERNET_DECODING_FAILED;
2064 break;
2067 #endif
2069 *read_ret = read;
2070 return ret;
2073 static void refill_gzip_buffer(http_request_t *req)
2075 DWORD res;
2076 int len;
2078 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2079 return;
2081 if(req->gzip_stream->buf_pos) {
2082 if(req->gzip_stream->buf_size)
2083 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2084 req->gzip_stream->buf_pos = 0;
2087 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2088 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2089 if(res == ERROR_SUCCESS)
2090 req->gzip_stream->buf_size += len;
2093 /* return the size of data available to be read immediately (the read section must be held) */
2094 static DWORD get_avail_data( http_request_t *req )
2096 if (req->gzip_stream) {
2097 refill_gzip_buffer(req);
2098 return req->gzip_stream->buf_size;
2100 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2101 return 0;
2102 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2105 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2107 INTERNET_ASYNC_RESULT iar;
2109 TRACE("%p\n", req);
2111 EnterCriticalSection( &req->read_section );
2112 if (refill_buffer( req )) {
2113 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2114 iar.dwError = first_notif ? 0 : get_avail_data(req);
2115 }else {
2116 iar.dwResult = 0;
2117 iar.dwError = INTERNET_GetLastError();
2119 LeaveCriticalSection( &req->read_section );
2121 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2122 sizeof(INTERNET_ASYNC_RESULT));
2125 /* read data from the http connection (the read section must be held) */
2126 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2128 BOOL finished_reading = FALSE;
2129 int len, bytes_read = 0;
2130 DWORD ret = ERROR_SUCCESS;
2132 EnterCriticalSection( &req->read_section );
2134 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2136 if (!start_next_chunk( req )) goto done;
2139 if(req->gzip_stream) {
2140 if(req->gzip_stream->buf_size) {
2141 bytes_read = min(req->gzip_stream->buf_size, size);
2142 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2143 req->gzip_stream->buf_pos += bytes_read;
2144 req->gzip_stream->buf_size -= bytes_read;
2145 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2146 refill_buffer(req);
2149 if(size > bytes_read) {
2150 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2151 if(ret == ERROR_SUCCESS)
2152 bytes_read += len;
2155 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2156 }else {
2157 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2159 if (req->read_size) {
2160 bytes_read = min( req->read_size, size );
2161 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2162 remove_data( req, bytes_read );
2165 if (size > bytes_read && (!bytes_read || sync)) {
2166 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2167 sync ? MSG_WAITALL : 0, &len))
2168 bytes_read += len;
2169 /* always return success, even if the network layer returns an error */
2172 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2174 done:
2175 req->dwContentRead += bytes_read;
2176 *read = bytes_read;
2178 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2179 LeaveCriticalSection( &req->read_section );
2181 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2182 BOOL res;
2183 DWORD dwBytesWritten;
2185 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2186 if(!res)
2187 WARN("WriteFile failed: %u\n", GetLastError());
2190 if(finished_reading)
2191 HTTP_FinishedReading(req);
2193 return ret;
2197 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2199 http_request_t *req = (http_request_t*)hdr;
2200 return HTTPREQ_Read(req, buffer, size, read, TRUE);
2203 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2205 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2206 http_request_t *req = (http_request_t*)workRequest->hdr;
2207 INTERNET_ASYNC_RESULT iar;
2208 DWORD res;
2210 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2212 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2213 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2215 iar.dwResult = res == ERROR_SUCCESS;
2216 iar.dwError = res;
2218 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2219 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2220 sizeof(INTERNET_ASYNC_RESULT));
2223 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2224 DWORD flags, DWORD_PTR context)
2226 http_request_t *req = (http_request_t*)hdr;
2227 DWORD res;
2229 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2230 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2232 if (buffers->dwStructSize != sizeof(*buffers))
2233 return ERROR_INVALID_PARAMETER;
2235 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2237 if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
2239 WORKREQUEST workRequest;
2241 if (TryEnterCriticalSection( &req->read_section ))
2243 if (get_avail_data(req))
2245 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2246 &buffers->dwBufferLength, FALSE);
2247 LeaveCriticalSection( &req->read_section );
2248 goto done;
2250 LeaveCriticalSection( &req->read_section );
2253 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2254 workRequest.hdr = WININET_AddRef(&req->hdr);
2255 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2257 INTERNET_AsyncCall(&workRequest);
2259 return ERROR_IO_PENDING;
2262 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2263 !(flags & IRF_NO_WAIT));
2265 done:
2266 if (res == ERROR_SUCCESS) {
2267 DWORD size = buffers->dwBufferLength;
2268 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2269 &size, sizeof(size));
2272 return res;
2275 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2277 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2278 http_request_t *req = (http_request_t*)workRequest->hdr;
2279 INTERNET_ASYNC_RESULT iar;
2280 DWORD res;
2282 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2284 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2285 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2287 iar.dwResult = res == ERROR_SUCCESS;
2288 iar.dwError = res;
2290 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2291 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2292 sizeof(INTERNET_ASYNC_RESULT));
2295 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2296 DWORD flags, DWORD_PTR context)
2299 http_request_t *req = (http_request_t*)hdr;
2300 DWORD res;
2302 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2303 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2305 if (buffers->dwStructSize != sizeof(*buffers))
2306 return ERROR_INVALID_PARAMETER;
2308 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2310 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2312 WORKREQUEST workRequest;
2314 if (TryEnterCriticalSection( &req->read_section ))
2316 if (get_avail_data(req))
2318 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2319 &buffers->dwBufferLength, FALSE);
2320 LeaveCriticalSection( &req->read_section );
2321 goto done;
2323 LeaveCriticalSection( &req->read_section );
2326 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2327 workRequest.hdr = WININET_AddRef(&req->hdr);
2328 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2330 INTERNET_AsyncCall(&workRequest);
2332 return ERROR_IO_PENDING;
2335 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2336 !(flags & IRF_NO_WAIT));
2338 done:
2339 if (res == ERROR_SUCCESS) {
2340 DWORD size = buffers->dwBufferLength;
2341 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2342 &size, sizeof(size));
2345 return res;
2348 static BOOL HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2350 BOOL ret;
2351 http_request_t *lpwhr = (http_request_t*)hdr;
2353 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2355 *written = 0;
2356 if ((ret = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written)))
2357 lpwhr->dwBytesWritten += *written;
2359 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2360 return ret;
2363 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2365 http_request_t *req = (http_request_t*)workRequest->hdr;
2367 HTTP_ReceiveRequestData(req, FALSE);
2370 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2372 http_request_t *req = (http_request_t*)hdr;
2374 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2376 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2378 WORKREQUEST workRequest;
2380 /* never wait, if we can't enter the section we queue an async request right away */
2381 if (TryEnterCriticalSection( &req->read_section ))
2383 if ((*available = get_avail_data( req ))) goto done;
2384 if (end_of_read_data( req )) goto done;
2385 LeaveCriticalSection( &req->read_section );
2388 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2389 workRequest.hdr = WININET_AddRef( &req->hdr );
2391 INTERNET_AsyncCall(&workRequest);
2393 return ERROR_IO_PENDING;
2396 EnterCriticalSection( &req->read_section );
2398 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2400 refill_buffer( req );
2401 *available = get_avail_data( req );
2404 done:
2405 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2407 DWORD extra;
2408 if (NETCON_query_data_available(&req->netConnection, &extra))
2409 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2411 LeaveCriticalSection( &req->read_section );
2413 TRACE( "returning %u\n", *available );
2414 return ERROR_SUCCESS;
2417 static const object_vtbl_t HTTPREQVtbl = {
2418 HTTPREQ_Destroy,
2419 HTTPREQ_CloseConnection,
2420 HTTPREQ_QueryOption,
2421 HTTPREQ_SetOption,
2422 HTTPREQ_ReadFile,
2423 HTTPREQ_ReadFileExA,
2424 HTTPREQ_ReadFileExW,
2425 HTTPREQ_WriteFile,
2426 HTTPREQ_QueryDataAvailable,
2427 NULL
2430 /***********************************************************************
2431 * HTTP_HttpOpenRequestW (internal)
2433 * Open a HTTP request handle
2435 * RETURNS
2436 * HINTERNET a HTTP request handle on success
2437 * NULL on failure
2440 HINTERNET WINAPI HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2441 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2442 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2443 DWORD dwFlags, DWORD_PTR dwContext)
2445 appinfo_t *hIC = NULL;
2446 http_request_t *lpwhr;
2447 LPWSTR lpszHostName = NULL;
2448 HINTERNET handle = NULL;
2449 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2450 DWORD len;
2452 TRACE("-->\n");
2454 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2455 hIC = lpwhs->lpAppInfo;
2457 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2458 if (NULL == lpwhr)
2460 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2461 goto lend;
2463 lpwhr->hdr.htype = WH_HHTTPREQ;
2464 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2465 lpwhr->hdr.dwFlags = dwFlags;
2466 lpwhr->hdr.dwContext = dwContext;
2467 lpwhr->hdr.refs = 1;
2468 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2469 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2470 lpwhr->dwContentLength = ~0u;
2471 InitializeCriticalSection( &lpwhr->read_section );
2473 WININET_AddRef( &lpwhs->hdr );
2474 lpwhr->lpHttpSession = lpwhs;
2475 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2477 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2478 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2479 if (NULL == lpszHostName)
2481 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2482 goto lend;
2485 handle = WININET_AllocHandle( &lpwhr->hdr );
2486 if (NULL == handle)
2488 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2489 goto lend;
2492 if (!NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE))
2494 InternetCloseHandle( handle );
2495 handle = NULL;
2496 goto lend;
2499 if (lpszObjectName && *lpszObjectName) {
2500 HRESULT rc;
2502 len = 0;
2503 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2504 if (rc != E_POINTER)
2505 len = strlenW(lpszObjectName)+1;
2506 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2507 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2508 URL_ESCAPE_SPACES_ONLY);
2509 if (rc != S_OK)
2511 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2512 strcpyW(lpwhr->lpszPath,lpszObjectName);
2514 }else {
2515 static const WCHAR slashW[] = {'/',0};
2517 lpwhr->lpszPath = heap_strdupW(slashW);
2520 if (lpszReferrer && *lpszReferrer)
2521 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2523 if (lpszAcceptTypes)
2525 int i;
2526 for (i = 0; lpszAcceptTypes[i]; i++)
2528 if (!*lpszAcceptTypes[i]) continue;
2529 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2530 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2531 HTTP_ADDHDR_FLAG_REQ |
2532 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2536 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2537 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2539 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2540 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2541 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2543 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2544 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2545 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2547 else
2548 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2549 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2551 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2552 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2553 INTERNET_DEFAULT_HTTPS_PORT :
2554 INTERNET_DEFAULT_HTTP_PORT);
2556 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2557 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2558 INTERNET_DEFAULT_HTTPS_PORT :
2559 INTERNET_DEFAULT_HTTP_PORT);
2561 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2562 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2564 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2565 INTERNET_STATUS_HANDLE_CREATED, &handle,
2566 sizeof(handle));
2568 lend:
2569 HeapFree(GetProcessHeap(), 0, lpszHostName);
2570 if( lpwhr )
2571 WININET_Release( &lpwhr->hdr );
2573 TRACE("<-- %p (%p)\n", handle, lpwhr);
2574 return handle;
2577 /* read any content returned by the server so that the connection can be
2578 * reused */
2579 static void HTTP_DrainContent(http_request_t *req)
2581 DWORD bytes_read;
2583 if (!NETCON_connected(&req->netConnection)) return;
2585 if (req->dwContentLength == -1)
2587 NETCON_close(&req->netConnection);
2588 return;
2590 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2594 char buffer[2048];
2595 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2596 return;
2597 } while (bytes_read);
2600 static const LPCWSTR header_lookup[] = {
2601 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2602 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2603 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2604 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2605 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2606 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2607 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2608 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2609 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2610 szDate, /* HTTP_QUERY_DATE = 9 */
2611 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2612 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2613 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2614 szURI, /* HTTP_QUERY_URI = 13 */
2615 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2616 NULL, /* HTTP_QUERY_COST = 15 */
2617 NULL, /* HTTP_QUERY_LINK = 16 */
2618 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2619 NULL, /* HTTP_QUERY_VERSION = 18 */
2620 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2621 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2622 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2623 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2624 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2625 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2626 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2627 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2628 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2629 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2630 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2631 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2632 NULL, /* HTTP_QUERY_FROM = 31 */
2633 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2634 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2635 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2636 szReferer, /* HTTP_QUERY_REFERER = 35 */
2637 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2638 szServer, /* HTTP_QUERY_SERVER = 37 */
2639 NULL, /* HTTP_TITLE = 38 */
2640 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2641 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2642 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2643 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2644 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2645 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2646 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2647 NULL, /* HTTP_QUERY_REFRESH = 46 */
2648 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2649 szAge, /* HTTP_QUERY_AGE = 48 */
2650 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2651 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2652 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2653 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2654 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2655 szETag, /* HTTP_QUERY_ETAG = 54 */
2656 hostW, /* HTTP_QUERY_HOST = 55 */
2657 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2658 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2659 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2660 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2661 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2662 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2663 szRange, /* HTTP_QUERY_RANGE = 62 */
2664 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2665 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2666 szVary, /* HTTP_QUERY_VARY = 65 */
2667 szVia, /* HTTP_QUERY_VIA = 66 */
2668 szWarning, /* HTTP_QUERY_WARNING = 67 */
2669 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2670 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2671 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2674 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2676 /***********************************************************************
2677 * HTTP_HttpQueryInfoW (internal)
2679 static BOOL HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2680 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2682 LPHTTPHEADERW lphttpHdr = NULL;
2683 BOOL bSuccess = FALSE;
2684 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2685 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2686 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2687 INT index = -1;
2689 /* Find requested header structure */
2690 switch (level)
2692 case HTTP_QUERY_CUSTOM:
2693 if (!lpBuffer) return FALSE;
2694 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2695 break;
2696 case HTTP_QUERY_RAW_HEADERS_CRLF:
2698 LPWSTR headers;
2699 DWORD len = 0;
2700 BOOL ret = FALSE;
2702 if (request_only)
2703 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2704 else
2705 headers = lpwhr->lpszRawHeaders;
2707 if (headers)
2708 len = strlenW(headers) * sizeof(WCHAR);
2710 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2712 len += sizeof(WCHAR);
2713 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2714 ret = FALSE;
2716 else if (lpBuffer)
2718 if (headers)
2719 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2720 else
2722 len = strlenW(szCrLf) * sizeof(WCHAR);
2723 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2725 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2726 ret = TRUE;
2728 *lpdwBufferLength = len;
2730 if (request_only)
2731 HeapFree(GetProcessHeap(), 0, headers);
2732 return ret;
2734 case HTTP_QUERY_RAW_HEADERS:
2736 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2737 DWORD i, size = 0;
2738 LPWSTR pszString = lpBuffer;
2740 for (i = 0; ppszRawHeaderLines[i]; i++)
2741 size += strlenW(ppszRawHeaderLines[i]) + 1;
2743 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2745 HTTP_FreeTokens(ppszRawHeaderLines);
2746 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2747 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2748 return FALSE;
2750 if (pszString)
2752 for (i = 0; ppszRawHeaderLines[i]; i++)
2754 DWORD len = strlenW(ppszRawHeaderLines[i]);
2755 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2756 pszString += len+1;
2758 *pszString = '\0';
2759 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2761 *lpdwBufferLength = size * sizeof(WCHAR);
2762 HTTP_FreeTokens(ppszRawHeaderLines);
2764 return TRUE;
2766 case HTTP_QUERY_STATUS_TEXT:
2767 if (lpwhr->lpszStatusText)
2769 DWORD len = strlenW(lpwhr->lpszStatusText);
2770 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2772 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2773 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2774 return FALSE;
2776 if (lpBuffer)
2778 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2779 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2781 *lpdwBufferLength = len * sizeof(WCHAR);
2782 return TRUE;
2784 break;
2785 case HTTP_QUERY_VERSION:
2786 if (lpwhr->lpszVersion)
2788 DWORD len = strlenW(lpwhr->lpszVersion);
2789 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2791 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2792 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2793 return FALSE;
2795 if (lpBuffer)
2797 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2798 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2800 *lpdwBufferLength = len * sizeof(WCHAR);
2801 return TRUE;
2803 break;
2804 case HTTP_QUERY_CONTENT_ENCODING:
2805 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2806 requested_index,request_only);
2807 break;
2808 default:
2809 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2811 if (level < LAST_TABLE_HEADER && header_lookup[level])
2812 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2813 requested_index,request_only);
2816 if (index >= 0)
2817 lphttpHdr = &lpwhr->pCustHeaders[index];
2819 /* Ensure header satisfies requested attributes */
2820 if (!lphttpHdr ||
2821 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2822 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2824 INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
2825 return bSuccess;
2828 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2830 /* coalesce value to requested type */
2831 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2833 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2834 TRACE(" returning number: %d\n", *(int *)lpBuffer);
2835 bSuccess = TRUE;
2837 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2839 time_t tmpTime;
2840 struct tm tmpTM;
2841 SYSTEMTIME *STHook;
2843 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2845 tmpTM = *gmtime(&tmpTime);
2846 STHook = (SYSTEMTIME *)lpBuffer;
2847 STHook->wDay = tmpTM.tm_mday;
2848 STHook->wHour = tmpTM.tm_hour;
2849 STHook->wMilliseconds = 0;
2850 STHook->wMinute = tmpTM.tm_min;
2851 STHook->wDayOfWeek = tmpTM.tm_wday;
2852 STHook->wMonth = tmpTM.tm_mon + 1;
2853 STHook->wSecond = tmpTM.tm_sec;
2854 STHook->wYear = tmpTM.tm_year;
2855 bSuccess = TRUE;
2857 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
2858 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
2859 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
2861 else if (lphttpHdr->lpszValue)
2863 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
2865 if (len > *lpdwBufferLength)
2867 *lpdwBufferLength = len;
2868 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2869 return bSuccess;
2871 if (lpBuffer)
2873 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
2874 TRACE(" returning string: %s\n", debugstr_w(lpBuffer));
2876 *lpdwBufferLength = len - sizeof(WCHAR);
2877 bSuccess = TRUE;
2879 return bSuccess;
2882 /***********************************************************************
2883 * HttpQueryInfoW (WININET.@)
2885 * Queries for information about an HTTP request
2887 * RETURNS
2888 * TRUE on success
2889 * FALSE on failure
2892 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
2893 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2895 BOOL bSuccess = FALSE;
2896 http_request_t *lpwhr;
2898 if (TRACE_ON(wininet)) {
2899 #define FE(x) { x, #x }
2900 static const wininet_flag_info query_flags[] = {
2901 FE(HTTP_QUERY_MIME_VERSION),
2902 FE(HTTP_QUERY_CONTENT_TYPE),
2903 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
2904 FE(HTTP_QUERY_CONTENT_ID),
2905 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
2906 FE(HTTP_QUERY_CONTENT_LENGTH),
2907 FE(HTTP_QUERY_CONTENT_LANGUAGE),
2908 FE(HTTP_QUERY_ALLOW),
2909 FE(HTTP_QUERY_PUBLIC),
2910 FE(HTTP_QUERY_DATE),
2911 FE(HTTP_QUERY_EXPIRES),
2912 FE(HTTP_QUERY_LAST_MODIFIED),
2913 FE(HTTP_QUERY_MESSAGE_ID),
2914 FE(HTTP_QUERY_URI),
2915 FE(HTTP_QUERY_DERIVED_FROM),
2916 FE(HTTP_QUERY_COST),
2917 FE(HTTP_QUERY_LINK),
2918 FE(HTTP_QUERY_PRAGMA),
2919 FE(HTTP_QUERY_VERSION),
2920 FE(HTTP_QUERY_STATUS_CODE),
2921 FE(HTTP_QUERY_STATUS_TEXT),
2922 FE(HTTP_QUERY_RAW_HEADERS),
2923 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
2924 FE(HTTP_QUERY_CONNECTION),
2925 FE(HTTP_QUERY_ACCEPT),
2926 FE(HTTP_QUERY_ACCEPT_CHARSET),
2927 FE(HTTP_QUERY_ACCEPT_ENCODING),
2928 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
2929 FE(HTTP_QUERY_AUTHORIZATION),
2930 FE(HTTP_QUERY_CONTENT_ENCODING),
2931 FE(HTTP_QUERY_FORWARDED),
2932 FE(HTTP_QUERY_FROM),
2933 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
2934 FE(HTTP_QUERY_LOCATION),
2935 FE(HTTP_QUERY_ORIG_URI),
2936 FE(HTTP_QUERY_REFERER),
2937 FE(HTTP_QUERY_RETRY_AFTER),
2938 FE(HTTP_QUERY_SERVER),
2939 FE(HTTP_QUERY_TITLE),
2940 FE(HTTP_QUERY_USER_AGENT),
2941 FE(HTTP_QUERY_WWW_AUTHENTICATE),
2942 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
2943 FE(HTTP_QUERY_ACCEPT_RANGES),
2944 FE(HTTP_QUERY_SET_COOKIE),
2945 FE(HTTP_QUERY_COOKIE),
2946 FE(HTTP_QUERY_REQUEST_METHOD),
2947 FE(HTTP_QUERY_REFRESH),
2948 FE(HTTP_QUERY_CONTENT_DISPOSITION),
2949 FE(HTTP_QUERY_AGE),
2950 FE(HTTP_QUERY_CACHE_CONTROL),
2951 FE(HTTP_QUERY_CONTENT_BASE),
2952 FE(HTTP_QUERY_CONTENT_LOCATION),
2953 FE(HTTP_QUERY_CONTENT_MD5),
2954 FE(HTTP_QUERY_CONTENT_RANGE),
2955 FE(HTTP_QUERY_ETAG),
2956 FE(HTTP_QUERY_HOST),
2957 FE(HTTP_QUERY_IF_MATCH),
2958 FE(HTTP_QUERY_IF_NONE_MATCH),
2959 FE(HTTP_QUERY_IF_RANGE),
2960 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
2961 FE(HTTP_QUERY_MAX_FORWARDS),
2962 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
2963 FE(HTTP_QUERY_RANGE),
2964 FE(HTTP_QUERY_TRANSFER_ENCODING),
2965 FE(HTTP_QUERY_UPGRADE),
2966 FE(HTTP_QUERY_VARY),
2967 FE(HTTP_QUERY_VIA),
2968 FE(HTTP_QUERY_WARNING),
2969 FE(HTTP_QUERY_CUSTOM)
2971 static const wininet_flag_info modifier_flags[] = {
2972 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
2973 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
2974 FE(HTTP_QUERY_FLAG_NUMBER),
2975 FE(HTTP_QUERY_FLAG_COALESCE)
2977 #undef FE
2978 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
2979 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
2980 DWORD i;
2982 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, dwInfoLevel);
2983 TRACE(" Attribute:");
2984 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
2985 if (query_flags[i].val == info) {
2986 TRACE(" %s", query_flags[i].name);
2987 break;
2990 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
2991 TRACE(" Unknown (%08x)", info);
2994 TRACE(" Modifier:");
2995 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
2996 if (modifier_flags[i].val & info_mod) {
2997 TRACE(" %s", modifier_flags[i].name);
2998 info_mod &= ~ modifier_flags[i].val;
3002 if (info_mod) {
3003 TRACE(" Unknown (%08x)", info_mod);
3005 TRACE("\n");
3008 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3009 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3011 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3012 goto lend;
3015 if (lpBuffer == NULL)
3016 *lpdwBufferLength = 0;
3017 bSuccess = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
3018 lpBuffer, lpdwBufferLength, lpdwIndex);
3020 lend:
3021 if( lpwhr )
3022 WININET_Release( &lpwhr->hdr );
3024 TRACE("%d <--\n", bSuccess);
3025 return bSuccess;
3028 /***********************************************************************
3029 * HttpQueryInfoA (WININET.@)
3031 * Queries for information about an HTTP request
3033 * RETURNS
3034 * TRUE on success
3035 * FALSE on failure
3038 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3039 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3041 BOOL result;
3042 DWORD len;
3043 WCHAR* bufferW;
3045 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3046 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3048 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3049 lpdwBufferLength, lpdwIndex );
3052 if (lpBuffer)
3054 DWORD alloclen;
3055 len = (*lpdwBufferLength)*sizeof(WCHAR);
3056 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3058 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3059 if (alloclen < len)
3060 alloclen = len;
3062 else
3063 alloclen = len;
3064 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3065 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3066 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3067 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3068 } else
3070 bufferW = NULL;
3071 len = 0;
3074 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3075 &len, lpdwIndex );
3076 if( result )
3078 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3079 lpBuffer, *lpdwBufferLength, NULL, NULL );
3080 *lpdwBufferLength = len - 1;
3082 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3084 else
3085 /* since the strings being returned from HttpQueryInfoW should be
3086 * only ASCII characters, it is reasonable to assume that all of
3087 * the Unicode characters can be reduced to a single byte */
3088 *lpdwBufferLength = len / sizeof(WCHAR);
3090 HeapFree(GetProcessHeap(), 0, bufferW );
3092 return result;
3095 /***********************************************************************
3096 * HttpSendRequestExA (WININET.@)
3098 * Sends the specified request to the HTTP server and allows chunked
3099 * transfers.
3101 * RETURNS
3102 * Success: TRUE
3103 * Failure: FALSE, call GetLastError() for more information.
3105 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3106 LPINTERNET_BUFFERSA lpBuffersIn,
3107 LPINTERNET_BUFFERSA lpBuffersOut,
3108 DWORD dwFlags, DWORD_PTR dwContext)
3110 INTERNET_BUFFERSW BuffersInW;
3111 BOOL rc = FALSE;
3112 DWORD headerlen;
3113 LPWSTR header = NULL;
3115 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3116 lpBuffersOut, dwFlags, dwContext);
3118 if (lpBuffersIn)
3120 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3121 if (lpBuffersIn->lpcszHeader)
3123 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3124 lpBuffersIn->dwHeadersLength,0,0);
3125 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3126 if (!(BuffersInW.lpcszHeader = header))
3128 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3129 return FALSE;
3131 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3132 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3133 header, headerlen);
3135 else
3136 BuffersInW.lpcszHeader = NULL;
3137 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3138 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3139 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3140 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3141 BuffersInW.Next = NULL;
3144 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3146 HeapFree(GetProcessHeap(),0,header);
3148 return rc;
3151 /***********************************************************************
3152 * HttpSendRequestExW (WININET.@)
3154 * Sends the specified request to the HTTP server and allows chunked
3155 * transfers
3157 * RETURNS
3158 * Success: TRUE
3159 * Failure: FALSE, call GetLastError() for more information.
3161 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3162 LPINTERNET_BUFFERSW lpBuffersIn,
3163 LPINTERNET_BUFFERSW lpBuffersOut,
3164 DWORD dwFlags, DWORD_PTR dwContext)
3166 BOOL ret = FALSE;
3167 http_request_t *lpwhr;
3168 http_session_t *lpwhs;
3169 appinfo_t *hIC;
3171 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3172 lpBuffersOut, dwFlags, dwContext);
3174 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3176 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3178 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3179 goto lend;
3182 lpwhs = lpwhr->lpHttpSession;
3183 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3184 hIC = lpwhs->lpAppInfo;
3185 assert(hIC->hdr.htype == WH_HINIT);
3187 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3189 WORKREQUEST workRequest;
3190 struct WORKREQ_HTTPSENDREQUESTW *req;
3192 workRequest.asyncproc = AsyncHttpSendRequestProc;
3193 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3194 req = &workRequest.u.HttpSendRequestW;
3195 if (lpBuffersIn)
3197 /* FIXME: this should use dwHeadersLength or may not be necessary at all */
3198 req->lpszHeader = heap_strdupW(lpBuffersIn->lpcszHeader);
3199 req->dwHeaderLength = lpBuffersIn->dwHeadersLength;
3200 req->lpOptional = lpBuffersIn->lpvBuffer;
3201 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
3202 req->dwContentLength = lpBuffersIn->dwBufferTotal;
3204 else
3206 req->lpszHeader = NULL;
3207 req->dwHeaderLength = 0;
3208 req->lpOptional = NULL;
3209 req->dwOptionalLength = 0;
3210 req->dwContentLength = 0;
3213 req->bEndRequest = FALSE;
3215 INTERNET_AsyncCall(&workRequest);
3217 * This is from windows.
3219 INTERNET_SetLastError(ERROR_IO_PENDING);
3221 else
3223 if (lpBuffersIn)
3224 ret = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3225 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
3226 lpBuffersIn->dwBufferTotal, FALSE);
3227 else
3228 ret = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
3231 lend:
3232 if ( lpwhr )
3233 WININET_Release( &lpwhr->hdr );
3235 TRACE("<---\n");
3236 return ret;
3239 /***********************************************************************
3240 * HttpSendRequestW (WININET.@)
3242 * Sends the specified request to the HTTP server
3244 * RETURNS
3245 * TRUE on success
3246 * FALSE on failure
3249 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
3250 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3252 http_request_t *lpwhr;
3253 http_session_t *lpwhs = NULL;
3254 appinfo_t *hIC = NULL;
3255 BOOL r;
3257 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
3258 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
3260 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
3261 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3263 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3264 r = FALSE;
3265 goto lend;
3268 lpwhs = lpwhr->lpHttpSession;
3269 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
3271 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3272 r = FALSE;
3273 goto lend;
3276 hIC = lpwhs->lpAppInfo;
3277 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
3279 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3280 r = FALSE;
3281 goto lend;
3284 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3286 WORKREQUEST workRequest;
3287 struct WORKREQ_HTTPSENDREQUESTW *req;
3289 workRequest.asyncproc = AsyncHttpSendRequestProc;
3290 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3291 req = &workRequest.u.HttpSendRequestW;
3292 if (lpszHeaders)
3294 DWORD size;
3296 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
3297 else size = dwHeaderLength * sizeof(WCHAR);
3299 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
3300 memcpy(req->lpszHeader, lpszHeaders, size);
3302 else
3303 req->lpszHeader = 0;
3304 req->dwHeaderLength = dwHeaderLength;
3305 req->lpOptional = lpOptional;
3306 req->dwOptionalLength = dwOptionalLength;
3307 req->dwContentLength = dwOptionalLength;
3308 req->bEndRequest = TRUE;
3310 INTERNET_AsyncCall(&workRequest);
3312 * This is from windows.
3314 INTERNET_SetLastError(ERROR_IO_PENDING);
3315 r = FALSE;
3317 else
3319 r = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
3320 dwHeaderLength, lpOptional, dwOptionalLength,
3321 dwOptionalLength, TRUE);
3323 lend:
3324 if( lpwhr )
3325 WININET_Release( &lpwhr->hdr );
3326 return r;
3329 /***********************************************************************
3330 * HttpSendRequestA (WININET.@)
3332 * Sends the specified request to the HTTP server
3334 * RETURNS
3335 * TRUE on success
3336 * FALSE on failure
3339 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
3340 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
3342 BOOL result;
3343 LPWSTR szHeaders=NULL;
3344 DWORD nLen=dwHeaderLength;
3345 if(lpszHeaders!=NULL)
3347 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
3348 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
3349 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
3351 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
3352 HeapFree(GetProcessHeap(),0,szHeaders);
3353 return result;
3356 /***********************************************************************
3357 * HTTP_GetRedirectURL (internal)
3359 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3361 static WCHAR szHttp[] = {'h','t','t','p',0};
3362 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3363 http_session_t *lpwhs = lpwhr->lpHttpSession;
3364 URL_COMPONENTSW urlComponents;
3365 DWORD url_length = 0;
3366 LPWSTR orig_url;
3367 LPWSTR combined_url;
3369 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3370 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3371 urlComponents.dwSchemeLength = 0;
3372 urlComponents.lpszHostName = lpwhs->lpszHostName;
3373 urlComponents.dwHostNameLength = 0;
3374 urlComponents.nPort = lpwhs->nHostPort;
3375 urlComponents.lpszUserName = lpwhs->lpszUserName;
3376 urlComponents.dwUserNameLength = 0;
3377 urlComponents.lpszPassword = NULL;
3378 urlComponents.dwPasswordLength = 0;
3379 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3380 urlComponents.dwUrlPathLength = 0;
3381 urlComponents.lpszExtraInfo = NULL;
3382 urlComponents.dwExtraInfoLength = 0;
3384 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3385 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3386 return NULL;
3388 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3390 /* convert from bytes to characters */
3391 url_length = url_length / sizeof(WCHAR) - 1;
3392 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3394 HeapFree(GetProcessHeap(), 0, orig_url);
3395 return NULL;
3398 url_length = 0;
3399 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3400 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3402 HeapFree(GetProcessHeap(), 0, orig_url);
3403 return NULL;
3405 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3407 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3409 HeapFree(GetProcessHeap(), 0, orig_url);
3410 HeapFree(GetProcessHeap(), 0, combined_url);
3411 return NULL;
3413 HeapFree(GetProcessHeap(), 0, orig_url);
3414 return combined_url;
3418 /***********************************************************************
3419 * HTTP_HandleRedirect (internal)
3421 static BOOL HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3423 http_session_t *lpwhs = lpwhr->lpHttpSession;
3424 appinfo_t *hIC = lpwhs->lpAppInfo;
3425 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3426 WCHAR path[INTERNET_MAX_URL_LENGTH];
3427 int index;
3429 if(lpszUrl[0]=='/')
3431 /* if it's an absolute path, keep the same session info */
3432 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3434 else
3436 URL_COMPONENTSW urlComponents;
3437 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3438 static WCHAR szHttp[] = {'h','t','t','p',0};
3439 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3441 userName[0] = 0;
3442 hostName[0] = 0;
3443 protocol[0] = 0;
3445 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3446 urlComponents.lpszScheme = protocol;
3447 urlComponents.dwSchemeLength = 32;
3448 urlComponents.lpszHostName = hostName;
3449 urlComponents.dwHostNameLength = MAXHOSTNAME;
3450 urlComponents.lpszUserName = userName;
3451 urlComponents.dwUserNameLength = 1024;
3452 urlComponents.lpszPassword = NULL;
3453 urlComponents.dwPasswordLength = 0;
3454 urlComponents.lpszUrlPath = path;
3455 urlComponents.dwUrlPathLength = 2048;
3456 urlComponents.lpszExtraInfo = NULL;
3457 urlComponents.dwExtraInfoLength = 0;
3458 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3459 return FALSE;
3461 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3462 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3464 TRACE("redirect from secure page to non-secure page\n");
3465 /* FIXME: warn about from secure redirect to non-secure page */
3466 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3468 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3469 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3471 TRACE("redirect from non-secure page to secure page\n");
3472 /* FIXME: notify about redirect to secure page */
3473 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3476 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3478 if (lstrlenW(protocol)>4) /*https*/
3479 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3480 else /*http*/
3481 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3484 #if 0
3486 * This upsets redirects to binary files on sourceforge.net
3487 * and gives an html page instead of the target file
3488 * Examination of the HTTP request sent by native wininet.dll
3489 * reveals that it doesn't send a referrer in that case.
3490 * Maybe there's a flag that enables this, or maybe a referrer
3491 * shouldn't be added in case of a redirect.
3494 /* consider the current host as the referrer */
3495 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3496 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3497 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3498 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3499 #endif
3501 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3502 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3503 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3505 int len;
3506 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3507 len = lstrlenW(hostName);
3508 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3509 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3510 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3512 else
3513 lpwhs->lpszHostName = heap_strdupW(hostName);
3515 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3517 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3518 lpwhs->lpszUserName = NULL;
3519 if (userName[0])
3520 lpwhs->lpszUserName = heap_strdupW(userName);
3522 if (!using_proxy)
3524 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3526 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3527 lpwhs->lpszServerName = heap_strdupW(hostName);
3528 lpwhs->nServerPort = urlComponents.nPort;
3530 NETCON_close(&lpwhr->netConnection);
3531 if (!HTTP_ResolveName(lpwhr)) return FALSE;
3532 if (!NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)) return FALSE;
3533 lpwhr->read_pos = lpwhr->read_size = 0;
3534 lpwhr->read_chunked = FALSE;
3537 else
3538 TRACE("Redirect through proxy\n");
3541 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3542 lpwhr->lpszPath=NULL;
3543 if (*path)
3545 DWORD needed = 0;
3546 HRESULT rc;
3548 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3549 if (rc != E_POINTER)
3550 needed = strlenW(path)+1;
3551 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3552 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3553 URL_ESCAPE_SPACES_ONLY);
3554 if (rc != S_OK)
3556 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3557 strcpyW(lpwhr->lpszPath,path);
3561 /* Remove custom content-type/length headers on redirects. */
3562 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3563 if (0 <= index)
3564 HTTP_DeleteCustomHeader(lpwhr, index);
3565 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3566 if (0 <= index)
3567 HTTP_DeleteCustomHeader(lpwhr, index);
3569 return TRUE;
3572 /***********************************************************************
3573 * HTTP_build_req (internal)
3575 * concatenate all the strings in the request together
3577 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3579 LPCWSTR *t;
3580 LPWSTR str;
3582 for( t = list; *t ; t++ )
3583 len += strlenW( *t );
3584 len++;
3586 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3587 *str = 0;
3589 for( t = list; *t ; t++ )
3590 strcatW( str, *t );
3592 return str;
3595 static BOOL HTTP_SecureProxyConnect(http_request_t *lpwhr)
3597 LPWSTR lpszPath;
3598 LPWSTR requestString;
3599 INT len;
3600 INT cnt;
3601 INT responseLen;
3602 char *ascii_req;
3603 BOOL ret;
3604 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3605 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3606 http_session_t *lpwhs = lpwhr->lpHttpSession;
3608 TRACE("\n");
3610 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3611 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3612 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3613 HeapFree( GetProcessHeap(), 0, lpszPath );
3615 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3616 NULL, 0, NULL, NULL );
3617 len--; /* the nul terminator isn't needed */
3618 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3619 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3620 ascii_req, len, NULL, NULL );
3621 HeapFree( GetProcessHeap(), 0, requestString );
3623 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3625 ret = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3626 HeapFree( GetProcessHeap(), 0, ascii_req );
3627 if (!ret || cnt < 0)
3628 return FALSE;
3630 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3631 if (!responseLen)
3632 return FALSE;
3634 return TRUE;
3637 static void HTTP_InsertCookies(http_request_t *lpwhr)
3639 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3640 LPWSTR lpszCookies, lpszUrl = NULL;
3641 DWORD nCookieSize, size;
3642 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3644 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3645 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3646 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3648 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3650 int cnt = 0;
3651 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3653 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3654 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3656 cnt += sprintfW(lpszCookies, szCookie);
3657 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3658 strcatW(lpszCookies, szCrLf);
3660 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3661 HeapFree(GetProcessHeap(), 0, lpszCookies);
3664 HeapFree(GetProcessHeap(), 0, lpszUrl);
3667 /***********************************************************************
3668 * HTTP_HttpSendRequestW (internal)
3670 * Sends the specified request to the HTTP server
3672 * RETURNS
3673 * TRUE on success
3674 * FALSE on failure
3677 BOOL WINAPI HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3678 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3679 DWORD dwContentLength, BOOL bEndRequest)
3681 INT cnt;
3682 BOOL bSuccess = FALSE, redirected = FALSE;
3683 LPWSTR requestString = NULL;
3684 INT responseLen;
3685 BOOL loop_next;
3686 INTERNET_ASYNC_RESULT iar;
3687 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3688 static const WCHAR szContentLength[] =
3689 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3690 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3692 TRACE("--> %p\n", lpwhr);
3694 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3696 /* if the verb is NULL default to GET */
3697 if (!lpwhr->lpszVerb)
3698 lpwhr->lpszVerb = heap_strdupW(szGET);
3700 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3702 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3703 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3704 lpwhr->dwBytesToWrite = dwContentLength;
3706 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3708 WCHAR *agent_header;
3709 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3710 int len;
3712 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3713 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3714 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3716 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3717 HeapFree(GetProcessHeap(), 0, agent_header);
3719 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3721 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3722 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3724 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3726 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3727 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3728 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3733 DWORD len;
3734 char *ascii_req;
3736 loop_next = FALSE;
3738 /* like native, just in case the caller forgot to call InternetReadFile
3739 * for all the data */
3740 HTTP_DrainContent(lpwhr);
3741 lpwhr->dwContentRead = 0;
3743 if (TRACE_ON(wininet))
3745 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3746 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3749 HTTP_FixURL(lpwhr);
3750 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3752 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3754 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3755 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3757 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3758 HTTP_InsertCookies(lpwhr);
3760 /* add the headers the caller supplied */
3761 if( lpszHeaders && dwHeaderLength )
3763 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3764 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3767 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3769 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3770 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3771 HeapFree(GetProcessHeap(), 0, url);
3773 else
3774 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3777 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3779 /* Send the request and store the results */
3780 if (!HTTP_OpenConnection(lpwhr))
3781 goto lend;
3783 /* send the request as ASCII, tack on the optional data */
3784 if (!lpOptional || redirected)
3785 dwOptionalLength = 0;
3786 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3787 NULL, 0, NULL, NULL );
3788 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3789 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3790 ascii_req, len, NULL, NULL );
3791 if( lpOptional )
3792 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3793 len = (len + dwOptionalLength - 1);
3794 ascii_req[len] = 0;
3795 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3797 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3798 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3800 NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3801 HeapFree( GetProcessHeap(), 0, ascii_req );
3803 lpwhr->dwBytesWritten = dwOptionalLength;
3805 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3806 INTERNET_STATUS_REQUEST_SENT,
3807 &len, sizeof(DWORD));
3809 if (bEndRequest)
3811 DWORD dwBufferSize;
3812 DWORD dwStatusCode;
3814 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3815 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3817 if (cnt < 0)
3818 goto lend;
3820 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3821 if (responseLen)
3822 bSuccess = TRUE;
3824 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3825 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3826 sizeof(DWORD));
3828 HTTP_ProcessCookies(lpwhr);
3830 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3832 dwBufferSize = sizeof(dwStatusCode);
3833 if (!HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3834 &dwStatusCode,&dwBufferSize,NULL))
3835 dwStatusCode = 0;
3837 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && bSuccess)
3839 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3840 dwBufferSize=sizeof(szNewLocation);
3841 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
3842 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL))
3844 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3846 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3847 lpwhr->lpszVerb = heap_strdupW(szGET);
3849 HTTP_DrainContent(lpwhr);
3850 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3852 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3853 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3854 bSuccess = HTTP_HandleRedirect(lpwhr, new_url);
3855 if (bSuccess)
3857 HeapFree(GetProcessHeap(), 0, requestString);
3858 loop_next = TRUE;
3860 HeapFree( GetProcessHeap(), 0, new_url );
3862 redirected = TRUE;
3865 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && bSuccess)
3867 WCHAR szAuthValue[2048];
3868 dwBufferSize=2048;
3869 if (dwStatusCode == HTTP_STATUS_DENIED)
3871 DWORD dwIndex = 0;
3872 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
3874 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3875 &lpwhr->pAuthInfo,
3876 lpwhr->lpHttpSession->lpszUserName,
3877 lpwhr->lpHttpSession->lpszPassword))
3879 loop_next = TRUE;
3880 break;
3884 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3886 DWORD dwIndex = 0;
3887 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
3889 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3890 &lpwhr->pProxyAuthInfo,
3891 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3892 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword))
3894 loop_next = TRUE;
3895 break;
3901 else
3902 bSuccess = TRUE;
3904 while (loop_next);
3906 if(bSuccess) {
3907 WCHAR url[INTERNET_MAX_URL_LENGTH];
3908 WCHAR cacheFileName[MAX_PATH+1];
3909 BOOL b;
3911 b = HTTP_GetRequestURL(lpwhr, url);
3912 if(!b) {
3913 WARN("Could not get URL\n");
3914 goto lend;
3917 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3918 if(b) {
3919 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3920 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3921 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3922 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3923 WARN("Could not create file: %u\n", GetLastError());
3924 lpwhr->hCacheFile = NULL;
3926 }else {
3927 WARN("Could not create cache entry: %08x\n", GetLastError());
3931 lend:
3933 HeapFree(GetProcessHeap(), 0, requestString);
3935 /* TODO: send notification for P3P header */
3937 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3939 if (bSuccess)
3941 if (lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite) HTTP_ReceiveRequestData(lpwhr, TRUE);
3942 else
3944 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3945 iar.dwError = 0;
3947 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3948 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3949 sizeof(INTERNET_ASYNC_RESULT));
3952 else
3954 iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
3955 iar.dwError = INTERNET_GetLastError();
3957 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3958 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3959 sizeof(INTERNET_ASYNC_RESULT));
3963 TRACE("<--\n");
3964 if (bSuccess) INTERNET_SetLastError(ERROR_SUCCESS);
3965 return bSuccess;
3968 /***********************************************************************
3969 * HTTPSESSION_Destroy (internal)
3971 * Deallocate session handle
3974 static void HTTPSESSION_Destroy(object_header_t *hdr)
3976 http_session_t *lpwhs = (http_session_t*) hdr;
3978 TRACE("%p\n", lpwhs);
3980 WININET_Release(&lpwhs->lpAppInfo->hdr);
3982 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3983 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3984 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
3985 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3986 HeapFree(GetProcessHeap(), 0, lpwhs);
3989 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3991 switch(option) {
3992 case INTERNET_OPTION_HANDLE_TYPE:
3993 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3995 if (*size < sizeof(ULONG))
3996 return ERROR_INSUFFICIENT_BUFFER;
3998 *size = sizeof(DWORD);
3999 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4000 return ERROR_SUCCESS;
4003 return INET_QueryOption(option, buffer, size, unicode);
4006 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4008 http_session_t *ses = (http_session_t*)hdr;
4010 switch(option) {
4011 case INTERNET_OPTION_USERNAME:
4013 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4014 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4015 return ERROR_SUCCESS;
4017 case INTERNET_OPTION_PASSWORD:
4019 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4020 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4021 return ERROR_SUCCESS;
4023 default: break;
4026 return ERROR_INTERNET_INVALID_OPTION;
4029 static const object_vtbl_t HTTPSESSIONVtbl = {
4030 HTTPSESSION_Destroy,
4031 NULL,
4032 HTTPSESSION_QueryOption,
4033 HTTPSESSION_SetOption,
4034 NULL,
4035 NULL,
4036 NULL,
4037 NULL,
4038 NULL
4042 /***********************************************************************
4043 * HTTP_Connect (internal)
4045 * Create http session handle
4047 * RETURNS
4048 * HINTERNET a session handle on success
4049 * NULL on failure
4052 HINTERNET HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4053 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4054 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4055 DWORD dwInternalFlags)
4057 http_session_t *lpwhs = NULL;
4058 HINTERNET handle = NULL;
4060 TRACE("-->\n");
4062 if (!lpszServerName || !lpszServerName[0])
4064 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4065 goto lerror;
4068 assert( hIC->hdr.htype == WH_HINIT );
4070 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4071 if (NULL == lpwhs)
4073 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4074 goto lerror;
4078 * According to my tests. The name is not resolved until a request is sent
4081 lpwhs->hdr.htype = WH_HHTTPSESSION;
4082 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4083 lpwhs->hdr.dwFlags = dwFlags;
4084 lpwhs->hdr.dwContext = dwContext;
4085 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4086 lpwhs->hdr.refs = 1;
4087 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4089 WININET_AddRef( &hIC->hdr );
4090 lpwhs->lpAppInfo = hIC;
4091 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4093 handle = WININET_AllocHandle( &lpwhs->hdr );
4094 if (NULL == handle)
4096 ERR("Failed to alloc handle\n");
4097 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4098 goto lerror;
4101 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4102 if(strchrW(hIC->lpszProxy, ' '))
4103 FIXME("Several proxies not implemented.\n");
4104 if(hIC->lpszProxyBypass)
4105 FIXME("Proxy bypass is ignored.\n");
4107 if (lpszServerName && lpszServerName[0])
4109 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4110 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4112 if (lpszUserName && lpszUserName[0])
4113 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4114 if (lpszPassword && lpszPassword[0])
4115 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4116 lpwhs->nServerPort = nServerPort;
4117 lpwhs->nHostPort = nServerPort;
4119 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4120 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4122 INTERNET_SendCallback(&hIC->hdr, dwContext,
4123 INTERNET_STATUS_HANDLE_CREATED, &handle,
4124 sizeof(handle));
4127 lerror:
4128 if( lpwhs )
4129 WININET_Release( &lpwhs->hdr );
4132 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4133 * windows
4136 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4137 return handle;
4141 /***********************************************************************
4142 * HTTP_OpenConnection (internal)
4144 * Connect to a web server
4146 * RETURNS
4148 * TRUE on success
4149 * FALSE on failure
4151 static BOOL HTTP_OpenConnection(http_request_t *lpwhr)
4153 BOOL bSuccess = FALSE;
4154 http_session_t *lpwhs;
4155 appinfo_t *hIC = NULL;
4156 char szaddr[INET6_ADDRSTRLEN];
4157 const void *addr;
4159 TRACE("-->\n");
4162 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4164 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4165 goto lend;
4168 if (NETCON_connected(&lpwhr->netConnection))
4170 bSuccess = TRUE;
4171 goto lend;
4173 if (!HTTP_ResolveName(lpwhr)) goto lend;
4175 lpwhs = lpwhr->lpHttpSession;
4177 hIC = lpwhs->lpAppInfo;
4178 switch (lpwhs->socketAddress.ss_family)
4180 case AF_INET:
4181 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4182 break;
4183 case AF_INET6:
4184 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4185 break;
4186 default:
4187 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4188 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
4189 return FALSE;
4191 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4192 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4193 INTERNET_STATUS_CONNECTING_TO_SERVER,
4194 szaddr,
4195 strlen(szaddr)+1);
4197 if (!NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family,
4198 SOCK_STREAM, 0))
4200 WARN("Socket creation failed: %u\n", INTERNET_GetLastError());
4201 goto lend;
4204 if (!NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4205 lpwhs->sa_len))
4206 goto lend;
4208 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4210 /* Note: we differ from Microsoft's WinINet here. they seem to have
4211 * a bug that causes no status callbacks to be sent when starting
4212 * a tunnel to a proxy server using the CONNECT verb. i believe our
4213 * behaviour to be more correct and to not cause any incompatibilities
4214 * because using a secure connection through a proxy server is a rare
4215 * case that would be hard for anyone to depend on */
4216 if (hIC->lpszProxy && !HTTP_SecureProxyConnect(lpwhr))
4217 goto lend;
4219 if (!NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName))
4221 WARN("Couldn't connect securely to host\n");
4222 goto lend;
4226 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4227 INTERNET_STATUS_CONNECTED_TO_SERVER,
4228 szaddr, strlen(szaddr)+1);
4230 bSuccess = TRUE;
4232 lend:
4233 lpwhr->read_pos = lpwhr->read_size = 0;
4234 lpwhr->read_chunked = FALSE;
4236 TRACE("%d <--\n", bSuccess);
4237 return bSuccess;
4241 /***********************************************************************
4242 * HTTP_clear_response_headers (internal)
4244 * clear out any old response headers
4246 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4248 DWORD i;
4250 for( i=0; i<lpwhr->nCustHeaders; i++)
4252 if( !lpwhr->pCustHeaders[i].lpszField )
4253 continue;
4254 if( !lpwhr->pCustHeaders[i].lpszValue )
4255 continue;
4256 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4257 continue;
4258 HTTP_DeleteCustomHeader( lpwhr, i );
4259 i--;
4263 /***********************************************************************
4264 * HTTP_GetResponseHeaders (internal)
4266 * Read server response
4268 * RETURNS
4270 * TRUE on success
4271 * FALSE on error
4273 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4275 INT cbreaks = 0;
4276 WCHAR buffer[MAX_REPLY_LEN];
4277 DWORD buflen = MAX_REPLY_LEN;
4278 BOOL bSuccess = FALSE;
4279 INT rc = 0;
4280 char bufferA[MAX_REPLY_LEN];
4281 LPWSTR status_code = NULL, status_text = NULL;
4282 DWORD cchMaxRawHeaders = 1024;
4283 LPWSTR lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4284 LPWSTR temp;
4285 DWORD cchRawHeaders = 0;
4286 BOOL codeHundred = FALSE;
4288 TRACE("-->\n");
4290 /* clear old response headers (eg. from a redirect response) */
4291 if (clear) HTTP_clear_response_headers( lpwhr );
4293 if (!NETCON_connected(&lpwhr->netConnection))
4294 goto lend;
4296 do {
4297 static const WCHAR szHundred[] = {'1','0','0',0};
4299 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4301 buflen = MAX_REPLY_LEN;
4302 if (!read_line(lpwhr, bufferA, &buflen))
4303 goto lend;
4304 rc += buflen;
4305 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4306 /* check is this a status code line? */
4307 if (!strncmpW(buffer, g_szHttp1_0, 4))
4309 /* split the version from the status code */
4310 status_code = strchrW( buffer, ' ' );
4311 if( !status_code )
4312 goto lend;
4313 *status_code++=0;
4315 /* split the status code from the status text */
4316 status_text = strchrW( status_code, ' ' );
4317 if( !status_text )
4318 goto lend;
4319 *status_text++=0;
4321 TRACE("version [%s] status code [%s] status text [%s]\n",
4322 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4324 codeHundred = (!strcmpW(status_code, szHundred));
4326 else if (!codeHundred)
4328 FIXME("Non status line at head of response (%s)\n",debugstr_w(buffer));
4329 continue;
4331 } while (codeHundred);
4333 /* Add status code */
4334 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4335 HTTP_ADDHDR_FLAG_REPLACE);
4337 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4338 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4340 lpwhr->lpszVersion = heap_strdupW(buffer);
4341 lpwhr->lpszStatusText = heap_strdupW(status_text);
4343 /* Restore the spaces */
4344 *(status_code-1) = ' ';
4345 *(status_text-1) = ' ';
4347 /* regenerate raw headers */
4348 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4349 cchMaxRawHeaders *= 2;
4350 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4351 if (temp == NULL) goto lend;
4352 lpszRawHeaders = temp;
4353 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4354 cchRawHeaders += (buflen-1);
4355 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4356 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4357 lpszRawHeaders[cchRawHeaders] = '\0';
4359 /* Parse each response line */
4362 buflen = MAX_REPLY_LEN;
4363 if (read_line(lpwhr, bufferA, &buflen))
4365 LPWSTR * pFieldAndValue;
4367 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4369 if (!bufferA[0]) break;
4370 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4372 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4373 if (pFieldAndValue)
4375 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4376 cchMaxRawHeaders *= 2;
4377 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4378 if (temp == NULL) goto lend;
4379 lpszRawHeaders = temp;
4380 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4381 cchRawHeaders += (buflen-1);
4382 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4383 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4384 lpszRawHeaders[cchRawHeaders] = '\0';
4386 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4387 HTTP_ADDREQ_FLAG_ADD );
4389 HTTP_FreeTokens(pFieldAndValue);
4392 else
4394 cbreaks++;
4395 if (cbreaks >= 2)
4396 break;
4398 }while(1);
4400 /* make sure the response header is terminated with an empty line. Some apps really
4401 truly care about that empty line being there for some reason. Just add it to the
4402 header. */
4403 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4405 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4406 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4407 if (temp == NULL) goto lend;
4408 lpszRawHeaders = temp;
4411 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4413 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4414 lpwhr->lpszRawHeaders = lpszRawHeaders;
4415 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4416 bSuccess = TRUE;
4418 lend:
4420 TRACE("<--\n");
4421 if (bSuccess)
4422 return rc;
4423 else
4425 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4426 return 0;
4431 static void strip_spaces(LPWSTR start)
4433 LPWSTR str = start;
4434 LPWSTR end;
4436 while (*str == ' ' && *str != '\0')
4437 str++;
4439 if (str != start)
4440 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
4442 end = start + strlenW(start) - 1;
4443 while (end >= start && *end == ' ')
4445 *end = '\0';
4446 end--;
4451 /***********************************************************************
4452 * HTTP_InterpretHttpHeader (internal)
4454 * Parse server response
4456 * RETURNS
4458 * Pointer to array of field, value, NULL on success.
4459 * NULL on error.
4461 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4463 LPWSTR * pTokenPair;
4464 LPWSTR pszColon;
4465 INT len;
4467 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4469 pszColon = strchrW(buffer, ':');
4470 /* must have two tokens */
4471 if (!pszColon)
4473 HTTP_FreeTokens(pTokenPair);
4474 if (buffer[0])
4475 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4476 return NULL;
4479 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4480 if (!pTokenPair[0])
4482 HTTP_FreeTokens(pTokenPair);
4483 return NULL;
4485 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4486 pTokenPair[0][pszColon - buffer] = '\0';
4488 /* skip colon */
4489 pszColon++;
4490 len = strlenW(pszColon);
4491 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4492 if (!pTokenPair[1])
4494 HTTP_FreeTokens(pTokenPair);
4495 return NULL;
4497 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4499 strip_spaces(pTokenPair[0]);
4500 strip_spaces(pTokenPair[1]);
4502 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4503 return pTokenPair;
4506 /***********************************************************************
4507 * HTTP_ProcessHeader (internal)
4509 * Stuff header into header tables according to <dwModifier>
4513 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4515 static BOOL HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4517 LPHTTPHEADERW lphttpHdr = NULL;
4518 BOOL bSuccess = FALSE;
4519 INT index = -1;
4520 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4522 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4524 /* REPLACE wins out over ADD */
4525 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4526 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4528 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4529 index = -1;
4530 else
4531 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4533 if (index >= 0)
4535 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4537 return FALSE;
4539 lphttpHdr = &lpwhr->pCustHeaders[index];
4541 else if (value)
4543 HTTPHEADERW hdr;
4545 hdr.lpszField = (LPWSTR)field;
4546 hdr.lpszValue = (LPWSTR)value;
4547 hdr.wFlags = hdr.wCount = 0;
4549 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4550 hdr.wFlags |= HDR_ISREQUEST;
4552 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4554 /* no value to delete */
4555 else return TRUE;
4557 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4558 lphttpHdr->wFlags |= HDR_ISREQUEST;
4559 else
4560 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4562 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4564 HTTP_DeleteCustomHeader( lpwhr, index );
4566 if (value)
4568 HTTPHEADERW hdr;
4570 hdr.lpszField = (LPWSTR)field;
4571 hdr.lpszValue = (LPWSTR)value;
4572 hdr.wFlags = hdr.wCount = 0;
4574 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4575 hdr.wFlags |= HDR_ISREQUEST;
4577 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4580 return TRUE;
4582 else if (dwModifier & COALESCEFLAGS)
4584 LPWSTR lpsztmp;
4585 WCHAR ch = 0;
4586 INT len = 0;
4587 INT origlen = strlenW(lphttpHdr->lpszValue);
4588 INT valuelen = strlenW(value);
4590 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4592 ch = ',';
4593 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4595 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4597 ch = ';';
4598 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4601 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4603 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4604 if (lpsztmp)
4606 lphttpHdr->lpszValue = lpsztmp;
4607 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4608 if (ch > 0)
4610 lphttpHdr->lpszValue[origlen] = ch;
4611 origlen++;
4612 lphttpHdr->lpszValue[origlen] = ' ';
4613 origlen++;
4616 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4617 lphttpHdr->lpszValue[len] = '\0';
4618 bSuccess = TRUE;
4620 else
4622 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4623 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4626 TRACE("<-- %d\n",bSuccess);
4627 return bSuccess;
4631 /***********************************************************************
4632 * HTTP_FinishedReading (internal)
4634 * Called when all content from server has been read by client.
4637 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4639 BOOL keepalive = HTTP_KeepAlive(lpwhr);
4641 TRACE("\n");
4644 if (!keepalive)
4646 HTTPREQ_CloseConnection(&lpwhr->hdr);
4649 /* FIXME: store data in the URL cache here */
4651 return TRUE;
4655 /***********************************************************************
4656 * HTTP_GetCustomHeaderIndex (internal)
4658 * Return index of custom header from header array
4661 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
4662 int requested_index, BOOL request_only)
4664 DWORD index;
4666 TRACE("%s\n", debugstr_w(lpszField));
4668 for (index = 0; index < lpwhr->nCustHeaders; index++)
4670 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
4671 continue;
4673 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4674 continue;
4676 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4677 continue;
4679 if (requested_index == 0)
4680 break;
4681 requested_index --;
4684 if (index >= lpwhr->nCustHeaders)
4685 index = -1;
4687 TRACE("Return: %d\n", index);
4688 return index;
4692 /***********************************************************************
4693 * HTTP_InsertCustomHeader (internal)
4695 * Insert header into array
4698 static BOOL HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
4700 INT count;
4701 LPHTTPHEADERW lph = NULL;
4702 BOOL r = FALSE;
4704 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
4705 count = lpwhr->nCustHeaders + 1;
4706 if (count > 1)
4707 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
4708 else
4709 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
4711 if (NULL != lph)
4713 lpwhr->pCustHeaders = lph;
4714 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
4715 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
4716 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
4717 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
4718 lpwhr->nCustHeaders++;
4719 r = TRUE;
4721 else
4723 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
4726 return r;
4730 /***********************************************************************
4731 * HTTP_DeleteCustomHeader (internal)
4733 * Delete header from array
4734 * If this function is called, the indexs may change.
4736 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
4738 if( lpwhr->nCustHeaders <= 0 )
4739 return FALSE;
4740 if( index >= lpwhr->nCustHeaders )
4741 return FALSE;
4742 lpwhr->nCustHeaders--;
4744 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
4745 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
4747 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
4748 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
4749 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
4751 return TRUE;
4755 /***********************************************************************
4756 * HTTP_VerifyValidHeader (internal)
4758 * Verify the given header is not invalid for the given http request
4761 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
4763 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4764 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
4765 return FALSE;
4767 return TRUE;
4770 /***********************************************************************
4771 * IsHostInProxyBypassList (@)
4773 * Undocumented
4776 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
4778 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
4779 return FALSE;