wininet: Delete authorization data on unsucessful login.
[wine/testsucceed.git] / dlls / wininet / http.c
blob57c4f7563b77b28138e74e846ddca4b665de605d
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 szOK[] = {'O','K',0};
77 static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
78 static const WCHAR hostW[] = { 'H','o','s','t',0 };
79 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
80 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
81 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
82 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
83 static const WCHAR szGET[] = { 'G','E','T', 0 };
84 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
85 static const WCHAR szCrLf[] = {'\r','\n', 0};
87 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
88 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
89 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
90 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
91 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
92 static const WCHAR szAge[] = { 'A','g','e',0 };
93 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
94 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
95 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
96 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
97 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
98 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
99 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
100 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
101 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
102 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
103 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
104 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 };
105 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
106 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
107 static const WCHAR szDate[] = { 'D','a','t','e',0 };
108 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
109 static const WCHAR szETag[] = { 'E','T','a','g',0 };
110 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
111 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
112 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
113 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
114 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
115 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
116 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
117 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
118 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
119 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
120 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
121 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
122 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
123 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
124 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
125 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
126 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
127 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
128 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
129 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
130 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
131 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 };
132 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
133 static const WCHAR szURI[] = { 'U','R','I',0 };
134 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
135 static const WCHAR szVary[] = { 'V','a','r','y',0 };
136 static const WCHAR szVia[] = { 'V','i','a',0 };
137 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
138 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
140 #define MAXHOSTNAME 100
141 #define MAX_FIELD_VALUE_LEN 256
142 #define MAX_FIELD_LEN 256
144 #define HTTP_REFERER szReferer
145 #define HTTP_ACCEPT szAccept
146 #define HTTP_USERAGENT szUser_Agent
148 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
149 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
150 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
151 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
152 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
153 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
154 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
156 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
158 struct HttpAuthInfo
160 LPWSTR scheme;
161 CredHandle cred;
162 CtxtHandle ctx;
163 TimeStamp exp;
164 ULONG attr;
165 ULONG max_token;
166 void *auth_data;
167 unsigned int auth_data_len;
168 BOOL finished; /* finished authenticating */
172 struct gzip_stream_t {
173 #ifdef HAVE_ZLIB
174 z_stream zstream;
175 #endif
176 BYTE buf[8192];
177 DWORD buf_size;
178 DWORD buf_pos;
179 BOOL end_of_data;
182 typedef struct _authorizationData
184 struct list entry;
186 LPWSTR lpszwHost;
187 LPWSTR lpszwRealm;
188 LPSTR lpszAuthorization;
189 UINT AuthorizationLen;
190 } authorizationData;
192 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
194 static CRITICAL_SECTION authcache_cs;
195 static CRITICAL_SECTION_DEBUG critsect_debug =
197 0, 0, &authcache_cs,
198 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
199 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
201 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
203 static DWORD HTTP_OpenConnection(http_request_t *req);
204 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
205 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
206 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
207 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
208 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
209 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
210 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
211 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
212 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
213 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
214 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
215 static void HTTP_DrainContent(http_request_t *req);
216 static BOOL HTTP_FinishedReading(http_request_t *req);
218 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
220 int HeaderIndex = 0;
221 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
222 if (HeaderIndex == -1)
223 return NULL;
224 else
225 return &req->pCustHeaders[HeaderIndex];
228 #ifdef HAVE_ZLIB
230 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
232 return HeapAlloc(GetProcessHeap(), 0, items*size);
235 static void wininet_zfree(voidpf opaque, voidpf address)
237 HeapFree(GetProcessHeap(), 0, address);
240 static void init_gzip_stream(http_request_t *req)
242 gzip_stream_t *gzip_stream;
243 int index, zres;
245 gzip_stream = HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t));
246 gzip_stream->zstream.zalloc = wininet_zalloc;
247 gzip_stream->zstream.zfree = wininet_zfree;
248 gzip_stream->zstream.opaque = NULL;
249 gzip_stream->zstream.next_in = NULL;
250 gzip_stream->zstream.avail_in = 0;
251 gzip_stream->zstream.next_out = NULL;
252 gzip_stream->zstream.avail_out = 0;
253 gzip_stream->buf_pos = 0;
254 gzip_stream->buf_size = 0;
255 gzip_stream->end_of_data = FALSE;
257 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
258 if(zres != Z_OK) {
259 ERR("inflateInit failed: %d\n", zres);
260 HeapFree(GetProcessHeap(), 0, gzip_stream);
261 return;
264 req->gzip_stream = gzip_stream;
266 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
267 if(index != -1)
268 HTTP_DeleteCustomHeader(req, index);
271 #else
273 static void init_gzip_stream(http_request_t *req)
275 ERR("gzip stream not supported, missing zlib.\n");
278 #endif
280 /* set the request content length based on the headers */
281 static DWORD set_content_length( http_request_t *lpwhr )
283 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
284 WCHAR encoding[20];
285 DWORD size;
287 size = sizeof(lpwhr->dwContentLength);
288 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
289 &lpwhr->dwContentLength, &size, NULL) != ERROR_SUCCESS)
290 lpwhr->dwContentLength = ~0u;
292 size = sizeof(encoding);
293 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
294 !strcmpiW(encoding, szChunked))
296 lpwhr->dwContentLength = ~0u;
297 lpwhr->read_chunked = TRUE;
300 if(lpwhr->decoding) {
301 int encoding_idx;
303 static const WCHAR gzipW[] = {'g','z','i','p',0};
305 encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
306 if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
307 init_gzip_stream(lpwhr);
310 return lpwhr->dwContentLength;
313 /***********************************************************************
314 * HTTP_Tokenize (internal)
316 * Tokenize a string, allocating memory for the tokens.
318 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
320 LPWSTR * token_array;
321 int tokens = 0;
322 int i;
323 LPCWSTR next_token;
325 if (string)
327 /* empty string has no tokens */
328 if (*string)
329 tokens++;
330 /* count tokens */
331 for (i = 0; string[i]; i++)
333 if (!strncmpW(string+i, token_string, strlenW(token_string)))
335 DWORD j;
336 tokens++;
337 /* we want to skip over separators, but not the null terminator */
338 for (j = 0; j < strlenW(token_string) - 1; j++)
339 if (!string[i+j])
340 break;
341 i += j;
346 /* add 1 for terminating NULL */
347 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
348 token_array[tokens] = NULL;
349 if (!tokens)
350 return token_array;
351 for (i = 0; i < tokens; i++)
353 int len;
354 next_token = strstrW(string, token_string);
355 if (!next_token) next_token = string+strlenW(string);
356 len = next_token - string;
357 token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
358 memcpy(token_array[i], string, len*sizeof(WCHAR));
359 token_array[i][len] = '\0';
360 string = next_token+strlenW(token_string);
362 return token_array;
365 /***********************************************************************
366 * HTTP_FreeTokens (internal)
368 * Frees memory returned from HTTP_Tokenize.
370 static void HTTP_FreeTokens(LPWSTR * token_array)
372 int i;
373 for (i = 0; token_array[i]; i++)
374 HeapFree(GetProcessHeap(), 0, token_array[i]);
375 HeapFree(GetProcessHeap(), 0, token_array);
378 static void HTTP_FixURL(http_request_t *lpwhr)
380 static const WCHAR szSlash[] = { '/',0 };
381 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
383 /* If we don't have a path we set it to root */
384 if (NULL == lpwhr->lpszPath)
385 lpwhr->lpszPath = heap_strdupW(szSlash);
386 else /* remove \r and \n*/
388 int nLen = strlenW(lpwhr->lpszPath);
389 while ((nLen >0 ) && ((lpwhr->lpszPath[nLen-1] == '\r')||(lpwhr->lpszPath[nLen-1] == '\n')))
391 nLen--;
392 lpwhr->lpszPath[nLen]='\0';
394 /* Replace '\' with '/' */
395 while (nLen>0) {
396 nLen--;
397 if (lpwhr->lpszPath[nLen] == '\\') lpwhr->lpszPath[nLen]='/';
401 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
402 lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
403 && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
405 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
406 (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
407 *fixurl = '/';
408 strcpyW(fixurl + 1, lpwhr->lpszPath);
409 HeapFree( GetProcessHeap(), 0, lpwhr->lpszPath );
410 lpwhr->lpszPath = fixurl;
414 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
416 LPWSTR requestString;
417 DWORD len, n;
418 LPCWSTR *req;
419 UINT i;
420 LPWSTR p;
422 static const WCHAR szSpace[] = { ' ',0 };
423 static const WCHAR szColon[] = { ':',' ',0 };
424 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
426 /* allocate space for an array of all the string pointers to be added */
427 len = (lpwhr->nCustHeaders)*4 + 10;
428 req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
430 /* add the verb, path and HTTP version string */
431 n = 0;
432 req[n++] = verb;
433 req[n++] = szSpace;
434 req[n++] = path;
435 req[n++] = szSpace;
436 req[n++] = version;
438 /* Append custom request headers */
439 for (i = 0; i < lpwhr->nCustHeaders; i++)
441 if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
443 req[n++] = szCrLf;
444 req[n++] = lpwhr->pCustHeaders[i].lpszField;
445 req[n++] = szColon;
446 req[n++] = lpwhr->pCustHeaders[i].lpszValue;
448 TRACE("Adding custom header %s (%s)\n",
449 debugstr_w(lpwhr->pCustHeaders[i].lpszField),
450 debugstr_w(lpwhr->pCustHeaders[i].lpszValue));
454 if( n >= len )
455 ERR("oops. buffer overrun\n");
457 req[n] = NULL;
458 requestString = HTTP_build_req( req, 4 );
459 HeapFree( GetProcessHeap(), 0, req );
462 * Set (header) termination string for request
463 * Make sure there's exactly two new lines at the end of the request
465 p = &requestString[strlenW(requestString)-1];
466 while ( (*p == '\n') || (*p == '\r') )
467 p--;
468 strcpyW( p+1, sztwocrlf );
470 return requestString;
473 static void HTTP_ProcessCookies( http_request_t *lpwhr )
475 int HeaderIndex;
476 int numCookies = 0;
477 LPHTTPHEADERW setCookieHeader;
479 while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
481 setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
483 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
485 int len;
486 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
487 LPWSTR buf_url;
488 LPHTTPHEADERW Host;
490 Host = HTTP_GetHeader(lpwhr, hostW);
491 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
492 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
493 sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
494 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
496 HeapFree(GetProcessHeap(), 0, buf_url);
498 numCookies++;
502 static void strip_spaces(LPWSTR start)
504 LPWSTR str = start;
505 LPWSTR end;
507 while (*str == ' ' && *str != '\0')
508 str++;
510 if (str != start)
511 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
513 end = start + strlenW(start) - 1;
514 while (end >= start && *end == ' ')
516 *end = '\0';
517 end--;
521 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
523 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
524 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
525 BOOL is_basic;
526 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
527 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
528 if (is_basic && pszRealm)
530 LPCWSTR token;
531 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
532 LPCWSTR realm;
533 ptr++;
534 *pszRealm=NULL;
535 token = strchrW(ptr,'=');
536 if (!token)
537 return TRUE;
538 realm = ptr;
539 while (*realm == ' ' && *realm != '\0')
540 realm++;
541 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
542 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
544 token++;
545 while (*token == ' ' && *token != '\0')
546 token++;
547 if (*token == '\0')
548 return TRUE;
549 *pszRealm = heap_strdupW(token);
550 strip_spaces(*pszRealm);
554 return is_basic;
557 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
559 if (!authinfo) return;
561 if (SecIsValidHandle(&authinfo->ctx))
562 DeleteSecurityContext(&authinfo->ctx);
563 if (SecIsValidHandle(&authinfo->cred))
564 FreeCredentialsHandle(&authinfo->cred);
566 HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
567 HeapFree(GetProcessHeap(), 0, authinfo->scheme);
568 HeapFree(GetProcessHeap(), 0, authinfo);
571 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
573 authorizationData *ad;
574 UINT rc = 0;
576 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
578 EnterCriticalSection(&authcache_cs);
579 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, authorizationData, entry)
581 if (!strcmpiW(host,ad->lpszwHost) && !strcmpW(realm,ad->lpszwRealm))
583 TRACE("Authorization found in cache\n");
584 *auth_data = HeapAlloc(GetProcessHeap(),0,ad->AuthorizationLen);
585 memcpy(*auth_data,ad->lpszAuthorization,ad->AuthorizationLen);
586 rc = ad->AuthorizationLen;
587 break;
590 LeaveCriticalSection(&authcache_cs);
591 return rc;
594 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
596 struct list *cursor;
597 authorizationData* ad = NULL;
599 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
601 EnterCriticalSection(&authcache_cs);
602 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
604 authorizationData *check = LIST_ENTRY(cursor,authorizationData,entry);
605 if (!strcmpiW(host,check->lpszwHost) && !strcmpW(realm,check->lpszwRealm))
607 ad = check;
608 break;
612 if (ad)
614 TRACE("Found match in cache, replacing\n");
615 HeapFree(GetProcessHeap(),0,ad->lpszAuthorization);
616 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
617 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
618 ad->AuthorizationLen = auth_data_len;
620 else
622 ad = HeapAlloc(GetProcessHeap(),0,sizeof(authorizationData));
623 ad->lpszwHost = heap_strdupW(host);
624 ad->lpszwRealm = heap_strdupW(realm);
625 ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
626 memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
627 ad->AuthorizationLen = auth_data_len;
628 list_add_head(&basicAuthorizationCache,&ad->entry);
629 TRACE("authorization cached\n");
631 LeaveCriticalSection(&authcache_cs);
634 static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
635 struct HttpAuthInfo **ppAuthInfo,
636 LPWSTR domain_and_username, LPWSTR password,
637 LPWSTR host )
639 SECURITY_STATUS sec_status;
640 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
641 BOOL first = FALSE;
642 LPWSTR szRealm = NULL;
644 TRACE("%s\n", debugstr_w(pszAuthValue));
646 if (!pAuthInfo)
648 TimeStamp exp;
650 first = TRUE;
651 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
652 if (!pAuthInfo)
653 return FALSE;
655 SecInvalidateHandle(&pAuthInfo->cred);
656 SecInvalidateHandle(&pAuthInfo->ctx);
657 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
658 pAuthInfo->attr = 0;
659 pAuthInfo->auth_data = NULL;
660 pAuthInfo->auth_data_len = 0;
661 pAuthInfo->finished = FALSE;
663 if (is_basic_auth_value(pszAuthValue,NULL))
665 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
666 pAuthInfo->scheme = heap_strdupW(szBasic);
667 if (!pAuthInfo->scheme)
669 HeapFree(GetProcessHeap(), 0, pAuthInfo);
670 return FALSE;
673 else
675 PVOID pAuthData;
676 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
678 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
679 if (!pAuthInfo->scheme)
681 HeapFree(GetProcessHeap(), 0, pAuthInfo);
682 return FALSE;
685 if (domain_and_username)
687 WCHAR *user = strchrW(domain_and_username, '\\');
688 WCHAR *domain = domain_and_username;
690 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
692 pAuthData = &nt_auth_identity;
694 if (user) user++;
695 else
697 user = domain_and_username;
698 domain = NULL;
701 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
702 nt_auth_identity.User = user;
703 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
704 nt_auth_identity.Domain = domain;
705 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
706 nt_auth_identity.Password = password;
707 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
709 else
710 /* use default credentials */
711 pAuthData = NULL;
713 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
714 SECPKG_CRED_OUTBOUND, NULL,
715 pAuthData, NULL,
716 NULL, &pAuthInfo->cred,
717 &exp);
718 if (sec_status == SEC_E_OK)
720 PSecPkgInfoW sec_pkg_info;
721 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
722 if (sec_status == SEC_E_OK)
724 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
725 FreeContextBuffer(sec_pkg_info);
728 if (sec_status != SEC_E_OK)
730 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
731 debugstr_w(pAuthInfo->scheme), sec_status);
732 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
733 HeapFree(GetProcessHeap(), 0, pAuthInfo);
734 return FALSE;
737 *ppAuthInfo = pAuthInfo;
739 else if (pAuthInfo->finished)
740 return FALSE;
742 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
743 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
745 ERR("authentication scheme changed from %s to %s\n",
746 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
747 return FALSE;
750 if (is_basic_auth_value(pszAuthValue,&szRealm))
752 int userlen;
753 int passlen;
754 char *auth_data = NULL;
755 UINT auth_data_len = 0;
757 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
759 if (!domain_and_username)
761 if (host && szRealm)
762 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
763 if (auth_data_len == 0)
765 HeapFree(GetProcessHeap(),0,szRealm);
766 return FALSE;
769 else
771 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
772 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
774 /* length includes a nul terminator, which will be re-used for the ':' */
775 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
776 if (!auth_data)
778 HeapFree(GetProcessHeap(),0,szRealm);
779 return FALSE;
782 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
783 auth_data[userlen] = ':';
784 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
785 auth_data_len = userlen + 1 + passlen;
786 if (host && szRealm)
787 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
790 pAuthInfo->auth_data = auth_data;
791 pAuthInfo->auth_data_len = auth_data_len;
792 pAuthInfo->finished = TRUE;
793 HeapFree(GetProcessHeap(),0,szRealm);
795 return TRUE;
797 else
799 LPCWSTR pszAuthData;
800 SecBufferDesc out_desc, in_desc;
801 SecBuffer out, in;
802 unsigned char *buffer;
803 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
804 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
806 in.BufferType = SECBUFFER_TOKEN;
807 in.cbBuffer = 0;
808 in.pvBuffer = NULL;
810 in_desc.ulVersion = 0;
811 in_desc.cBuffers = 1;
812 in_desc.pBuffers = &in;
814 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
815 if (*pszAuthData == ' ')
817 pszAuthData++;
818 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
819 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
820 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
823 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
825 out.BufferType = SECBUFFER_TOKEN;
826 out.cbBuffer = pAuthInfo->max_token;
827 out.pvBuffer = buffer;
829 out_desc.ulVersion = 0;
830 out_desc.cBuffers = 1;
831 out_desc.pBuffers = &out;
833 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
834 first ? NULL : &pAuthInfo->ctx,
835 first ? lpwhr->lpHttpSession->lpszServerName : NULL,
836 context_req, 0, SECURITY_NETWORK_DREP,
837 in.pvBuffer ? &in_desc : NULL,
838 0, &pAuthInfo->ctx, &out_desc,
839 &pAuthInfo->attr, &pAuthInfo->exp);
840 if (sec_status == SEC_E_OK)
842 pAuthInfo->finished = TRUE;
843 pAuthInfo->auth_data = out.pvBuffer;
844 pAuthInfo->auth_data_len = out.cbBuffer;
845 TRACE("sending last auth packet\n");
847 else if (sec_status == SEC_I_CONTINUE_NEEDED)
849 pAuthInfo->auth_data = out.pvBuffer;
850 pAuthInfo->auth_data_len = out.cbBuffer;
851 TRACE("sending next auth packet\n");
853 else
855 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
856 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
857 destroy_authinfo(pAuthInfo);
858 *ppAuthInfo = NULL;
859 return FALSE;
863 return TRUE;
866 /***********************************************************************
867 * HTTP_HttpAddRequestHeadersW (internal)
869 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
870 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
872 LPWSTR lpszStart;
873 LPWSTR lpszEnd;
874 LPWSTR buffer;
875 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
877 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
879 if( dwHeaderLength == ~0U )
880 len = strlenW(lpszHeader);
881 else
882 len = dwHeaderLength;
883 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
884 lstrcpynW( buffer, lpszHeader, len + 1);
886 lpszStart = buffer;
890 LPWSTR * pFieldAndValue;
892 lpszEnd = lpszStart;
894 while (*lpszEnd != '\0')
896 if (*lpszEnd == '\r' || *lpszEnd == '\n')
897 break;
898 lpszEnd++;
901 if (*lpszStart == '\0')
902 break;
904 if (*lpszEnd == '\r' || *lpszEnd == '\n')
906 *lpszEnd = '\0';
907 lpszEnd++; /* Jump over newline */
909 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
910 if (*lpszStart == '\0')
912 /* Skip 0-length headers */
913 lpszStart = lpszEnd;
914 res = ERROR_SUCCESS;
915 continue;
917 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
918 if (pFieldAndValue)
920 res = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
921 if (res == ERROR_SUCCESS)
922 res = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
923 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
924 HTTP_FreeTokens(pFieldAndValue);
927 lpszStart = lpszEnd;
928 } while (res == ERROR_SUCCESS);
930 HeapFree(GetProcessHeap(), 0, buffer);
932 return res;
935 /***********************************************************************
936 * HttpAddRequestHeadersW (WININET.@)
938 * Adds one or more HTTP header to the request handler
940 * NOTE
941 * On Windows if dwHeaderLength includes the trailing '\0', then
942 * HttpAddRequestHeadersW() adds it too. However this results in an
943 * invalid Http header which is rejected by some servers so we probably
944 * don't need to match Windows on that point.
946 * RETURNS
947 * TRUE on success
948 * FALSE on failure
951 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
952 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
954 http_request_t *lpwhr;
955 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
957 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
959 if (!lpszHeader)
960 return TRUE;
962 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
963 if (lpwhr && lpwhr->hdr.htype == WH_HHTTPREQ)
964 res = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
965 if( lpwhr )
966 WININET_Release( &lpwhr->hdr );
968 if(res != ERROR_SUCCESS)
969 SetLastError(res);
970 return res == ERROR_SUCCESS;
973 /***********************************************************************
974 * HttpAddRequestHeadersA (WININET.@)
976 * Adds one or more HTTP header to the request handler
978 * RETURNS
979 * TRUE on success
980 * FALSE on failure
983 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
984 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
986 DWORD len;
987 LPWSTR hdr;
988 BOOL r;
990 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
992 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
993 hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
994 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
995 if( dwHeaderLength != ~0U )
996 dwHeaderLength = len;
998 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1000 HeapFree( GetProcessHeap(), 0, hdr );
1002 return r;
1005 /***********************************************************************
1006 * HttpOpenRequestA (WININET.@)
1008 * Open a HTTP request handle
1010 * RETURNS
1011 * HINTERNET a HTTP request handle on success
1012 * NULL on failure
1015 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1016 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1017 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1018 DWORD dwFlags, DWORD_PTR dwContext)
1020 LPWSTR szVerb = NULL, szObjectName = NULL;
1021 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1022 INT acceptTypesCount;
1023 HINTERNET rc = FALSE;
1024 LPCSTR *types;
1026 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1027 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1028 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1029 dwFlags, dwContext);
1031 if (lpszVerb)
1033 szVerb = heap_strdupAtoW(lpszVerb);
1034 if ( !szVerb )
1035 goto end;
1038 if (lpszObjectName)
1040 szObjectName = heap_strdupAtoW(lpszObjectName);
1041 if ( !szObjectName )
1042 goto end;
1045 if (lpszVersion)
1047 szVersion = heap_strdupAtoW(lpszVersion);
1048 if ( !szVersion )
1049 goto end;
1052 if (lpszReferrer)
1054 szReferrer = heap_strdupAtoW(lpszReferrer);
1055 if ( !szReferrer )
1056 goto end;
1059 if (lpszAcceptTypes)
1061 acceptTypesCount = 0;
1062 types = lpszAcceptTypes;
1063 while (*types)
1065 __TRY
1067 /* find out how many there are */
1068 if (*types && **types)
1070 TRACE("accept type: %s\n", debugstr_a(*types));
1071 acceptTypesCount++;
1074 __EXCEPT_PAGE_FAULT
1076 WARN("invalid accept type pointer\n");
1078 __ENDTRY;
1079 types++;
1081 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1082 if (!szAcceptTypes) goto end;
1084 acceptTypesCount = 0;
1085 types = lpszAcceptTypes;
1086 while (*types)
1088 __TRY
1090 if (*types && **types)
1091 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1093 __EXCEPT_PAGE_FAULT
1095 /* ignore invalid pointer */
1097 __ENDTRY;
1098 types++;
1100 szAcceptTypes[acceptTypesCount] = NULL;
1103 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1104 szVersion, szReferrer,
1105 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1107 end:
1108 if (szAcceptTypes)
1110 acceptTypesCount = 0;
1111 while (szAcceptTypes[acceptTypesCount])
1113 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1114 acceptTypesCount++;
1116 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1118 HeapFree(GetProcessHeap(), 0, szReferrer);
1119 HeapFree(GetProcessHeap(), 0, szVersion);
1120 HeapFree(GetProcessHeap(), 0, szObjectName);
1121 HeapFree(GetProcessHeap(), 0, szVerb);
1123 return rc;
1126 /***********************************************************************
1127 * HTTP_EncodeBase64
1129 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1131 UINT n = 0, x;
1132 static const CHAR HTTP_Base64Enc[] =
1133 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1135 while( len > 0 )
1137 /* first 6 bits, all from bin[0] */
1138 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1139 x = (bin[0] & 3) << 4;
1141 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1142 if( len == 1 )
1144 base64[n++] = HTTP_Base64Enc[x];
1145 base64[n++] = '=';
1146 base64[n++] = '=';
1147 break;
1149 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1150 x = ( bin[1] & 0x0f ) << 2;
1152 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1153 if( len == 2 )
1155 base64[n++] = HTTP_Base64Enc[x];
1156 base64[n++] = '=';
1157 break;
1159 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1161 /* last 6 bits, all from bin [2] */
1162 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1163 bin += 3;
1164 len -= 3;
1166 base64[n] = 0;
1167 return n;
1170 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1171 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1172 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1173 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1174 static const signed char HTTP_Base64Dec[256] =
1176 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1177 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1178 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1179 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1180 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1181 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1182 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1183 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1184 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1185 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1186 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1187 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1188 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1189 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1190 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1191 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1192 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1193 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1194 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1195 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1196 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1197 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1198 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1199 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1200 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1201 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1203 #undef CH
1205 /***********************************************************************
1206 * HTTP_DecodeBase64
1208 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1210 unsigned int n = 0;
1212 while(*base64)
1214 signed char in[4];
1216 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1217 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1218 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1219 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1221 WARN("invalid base64: %s\n", debugstr_w(base64));
1222 return 0;
1224 if (bin)
1225 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1226 n++;
1228 if ((base64[2] == '=') && (base64[3] == '='))
1229 break;
1230 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1231 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1233 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1234 return 0;
1236 if (bin)
1237 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1238 n++;
1240 if (base64[3] == '=')
1241 break;
1242 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1243 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1245 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1246 return 0;
1248 if (bin)
1249 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1250 n++;
1252 base64 += 4;
1255 return n;
1258 /***********************************************************************
1259 * HTTP_InsertAuthorization
1261 * Insert or delete the authorization field in the request header.
1263 static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1265 if (pAuthInfo)
1267 static const WCHAR wszSpace[] = {' ',0};
1268 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1269 unsigned int len;
1270 WCHAR *authorization = NULL;
1272 if (pAuthInfo->auth_data_len)
1274 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1275 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1276 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1277 if (!authorization)
1278 return FALSE;
1280 strcpyW(authorization, pAuthInfo->scheme);
1281 strcatW(authorization, wszSpace);
1282 HTTP_EncodeBase64(pAuthInfo->auth_data,
1283 pAuthInfo->auth_data_len,
1284 authorization+strlenW(authorization));
1286 /* clear the data as it isn't valid now that it has been sent to the
1287 * server, unless it's Basic authentication which doesn't do
1288 * connection tracking */
1289 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1291 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1292 pAuthInfo->auth_data = NULL;
1293 pAuthInfo->auth_data_len = 0;
1297 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1299 HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1301 HeapFree(GetProcessHeap(), 0, authorization);
1303 return TRUE;
1306 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1308 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1309 DWORD size;
1311 size = sizeof(new_location);
1312 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1314 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1315 strcpyW( url, new_location );
1317 else
1319 static const WCHAR slash[] = { '/',0 };
1320 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1321 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1322 http_session_t *session = req->lpHttpSession;
1324 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1325 size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
1327 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1329 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1330 sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
1331 else
1332 sprintfW( url, format, session->lpszHostName, session->nHostPort );
1333 if (req->lpszPath[0] != '/') strcatW( url, slash );
1334 strcatW( url, req->lpszPath );
1336 TRACE("url=%s\n", debugstr_w(url));
1337 return url;
1340 /***********************************************************************
1341 * HTTP_DealWithProxy
1343 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
1345 WCHAR buf[MAXHOSTNAME];
1346 WCHAR protoProxy[MAXHOSTNAME + 15];
1347 DWORD protoProxyLen = sizeof(protoProxy) / sizeof(protoProxy[0]);
1348 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1349 static WCHAR szNul[] = { 0 };
1350 URL_COMPONENTSW UrlComponents;
1351 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1352 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1353 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1355 memset( &UrlComponents, 0, sizeof UrlComponents );
1356 UrlComponents.dwStructSize = sizeof UrlComponents;
1357 UrlComponents.lpszHostName = buf;
1358 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1360 if (!INTERNET_FindProxyForProtocol(hIC->lpszProxy, protoHttp, protoProxy, &protoProxyLen))
1361 return FALSE;
1362 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1363 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1364 sprintfW(proxy, szFormat, protoProxy);
1365 else
1366 strcpyW(proxy, protoProxy);
1367 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1368 return FALSE;
1369 if( UrlComponents.dwHostNameLength == 0 )
1370 return FALSE;
1372 if( !lpwhr->lpszPath )
1373 lpwhr->lpszPath = szNul;
1375 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1376 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1378 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
1379 lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
1380 lpwhs->nServerPort = UrlComponents.nPort;
1382 TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
1383 return TRUE;
1386 #ifndef INET6_ADDRSTRLEN
1387 #define INET6_ADDRSTRLEN 46
1388 #endif
1390 static DWORD HTTP_ResolveName(http_request_t *lpwhr)
1392 char szaddr[INET6_ADDRSTRLEN];
1393 http_session_t *lpwhs = lpwhr->lpHttpSession;
1394 const void *addr;
1396 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1397 INTERNET_STATUS_RESOLVING_NAME,
1398 lpwhs->lpszServerName,
1399 (strlenW(lpwhs->lpszServerName)+1) * sizeof(WCHAR));
1401 lpwhs->sa_len = sizeof(lpwhs->socketAddress);
1402 if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
1403 (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
1404 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1406 switch (lpwhs->socketAddress.ss_family)
1408 case AF_INET:
1409 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
1410 break;
1411 case AF_INET6:
1412 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
1413 break;
1414 default:
1415 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
1416 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1418 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1419 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1420 INTERNET_STATUS_NAME_RESOLVED,
1421 szaddr, strlen(szaddr)+1);
1423 TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
1424 return ERROR_SUCCESS;
1428 /***********************************************************************
1429 * HTTPREQ_Destroy (internal)
1431 * Deallocate request handle
1434 static void HTTPREQ_Destroy(object_header_t *hdr)
1436 http_request_t *lpwhr = (http_request_t*) hdr;
1437 DWORD i;
1439 TRACE("\n");
1441 if(lpwhr->hCacheFile)
1442 CloseHandle(lpwhr->hCacheFile);
1444 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
1446 DeleteCriticalSection( &lpwhr->read_section );
1447 WININET_Release(&lpwhr->lpHttpSession->hdr);
1449 destroy_authinfo(lpwhr->pAuthInfo);
1450 destroy_authinfo(lpwhr->pProxyAuthInfo);
1452 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
1453 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
1454 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
1455 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
1456 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
1458 for (i = 0; i < lpwhr->nCustHeaders; i++)
1460 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
1461 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
1464 #ifdef HAVE_ZLIB
1465 if(lpwhr->gzip_stream) {
1466 if(!lpwhr->gzip_stream->end_of_data)
1467 inflateEnd(&lpwhr->gzip_stream->zstream);
1468 HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
1470 #endif
1472 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
1473 HeapFree(GetProcessHeap(), 0, lpwhr);
1476 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1478 http_request_t *lpwhr = (http_request_t*) hdr;
1480 TRACE("%p\n",lpwhr);
1482 if (!NETCON_connected(&lpwhr->netConnection))
1483 return;
1485 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1486 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1488 NETCON_close(&lpwhr->netConnection);
1490 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
1491 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1494 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1496 LPHTTPHEADERW host_header;
1498 static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
1500 host_header = HTTP_GetHeader(req, hostW);
1501 if(!host_header)
1502 return FALSE;
1504 sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
1505 return TRUE;
1508 static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
1510 WCHAR szVersion[10];
1511 WCHAR szConnectionResponse[20];
1512 DWORD dwBufferSize = sizeof(szVersion);
1513 BOOL keepalive = FALSE;
1515 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1516 * the connection is keep-alive by default */
1517 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1518 && !strcmpiW(szVersion, g_szHttp1_1))
1520 keepalive = TRUE;
1523 dwBufferSize = sizeof(szConnectionResponse);
1524 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1525 || HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1527 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1530 return keepalive;
1533 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1535 http_request_t *req = (http_request_t*)hdr;
1537 switch(option) {
1538 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1540 http_session_t *lpwhs = req->lpHttpSession;
1541 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1543 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1545 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1546 return ERROR_INSUFFICIENT_BUFFER;
1547 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1548 /* FIXME: can't get a SOCKET from our connection since we don't use
1549 * winsock
1551 info->Socket = 0;
1552 /* FIXME: get source port from req->netConnection */
1553 info->SourcePort = 0;
1554 info->DestPort = lpwhs->nHostPort;
1555 info->Flags = 0;
1556 if (HTTP_KeepAlive(req))
1557 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1558 if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
1559 info->Flags |= IDSI_FLAG_PROXY;
1560 if (req->netConnection.useSSL)
1561 info->Flags |= IDSI_FLAG_SECURE;
1563 return ERROR_SUCCESS;
1566 case INTERNET_OPTION_SECURITY_FLAGS:
1568 http_session_t *lpwhs;
1569 lpwhs = req->lpHttpSession;
1571 if (*size < sizeof(ULONG))
1572 return ERROR_INSUFFICIENT_BUFFER;
1574 *size = sizeof(DWORD);
1575 if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
1576 *(DWORD*)buffer = SECURITY_FLAG_SECURE;
1577 else
1578 *(DWORD*)buffer = 0;
1579 FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
1580 return ERROR_SUCCESS;
1583 case INTERNET_OPTION_HANDLE_TYPE:
1584 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1586 if (*size < sizeof(ULONG))
1587 return ERROR_INSUFFICIENT_BUFFER;
1589 *size = sizeof(DWORD);
1590 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1591 return ERROR_SUCCESS;
1593 case INTERNET_OPTION_URL: {
1594 WCHAR url[INTERNET_MAX_URL_LENGTH];
1595 HTTPHEADERW *host;
1596 DWORD len;
1597 WCHAR *pch;
1599 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1601 TRACE("INTERNET_OPTION_URL\n");
1603 host = HTTP_GetHeader(req, hostW);
1604 strcpyW(url, httpW);
1605 strcatW(url, host->lpszValue);
1606 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1607 *pch = 0;
1608 strcatW(url, req->lpszPath);
1610 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1612 if(unicode) {
1613 len = (strlenW(url)+1) * sizeof(WCHAR);
1614 if(*size < len)
1615 return ERROR_INSUFFICIENT_BUFFER;
1617 *size = len;
1618 strcpyW(buffer, url);
1619 return ERROR_SUCCESS;
1620 }else {
1621 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1622 if(len > *size)
1623 return ERROR_INSUFFICIENT_BUFFER;
1625 *size = len;
1626 return ERROR_SUCCESS;
1630 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1631 INTERNET_CACHE_ENTRY_INFOW *info;
1632 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1633 WCHAR url[INTERNET_MAX_URL_LENGTH];
1634 DWORD nbytes, error;
1635 BOOL ret;
1637 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1639 if (*size < sizeof(*ts))
1641 *size = sizeof(*ts);
1642 return ERROR_INSUFFICIENT_BUFFER;
1644 nbytes = 0;
1645 HTTP_GetRequestURL(req, url);
1646 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1647 error = GetLastError();
1648 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1650 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1651 return ERROR_OUTOFMEMORY;
1653 GetUrlCacheEntryInfoW(url, info, &nbytes);
1655 ts->ftExpires = info->ExpireTime;
1656 ts->ftLastModified = info->LastModifiedTime;
1658 HeapFree(GetProcessHeap(), 0, info);
1659 *size = sizeof(*ts);
1660 return ERROR_SUCCESS;
1662 return error;
1665 case INTERNET_OPTION_DATAFILE_NAME: {
1666 DWORD req_size;
1668 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1670 if(!req->lpszCacheFile) {
1671 *size = 0;
1672 return ERROR_INTERNET_ITEM_NOT_FOUND;
1675 if(unicode) {
1676 req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
1677 if(*size < req_size)
1678 return ERROR_INSUFFICIENT_BUFFER;
1680 *size = req_size;
1681 memcpy(buffer, req->lpszCacheFile, *size);
1682 return ERROR_SUCCESS;
1683 }else {
1684 req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
1685 if (req_size > *size)
1686 return ERROR_INSUFFICIENT_BUFFER;
1688 *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
1689 -1, buffer, *size, NULL, NULL);
1690 return ERROR_SUCCESS;
1694 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1695 PCCERT_CONTEXT context;
1697 if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
1698 *size = sizeof(INTERNET_CERTIFICATE_INFOW);
1699 return ERROR_INSUFFICIENT_BUFFER;
1702 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1703 if(context) {
1704 INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
1705 DWORD len;
1707 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1708 info->ftExpiry = context->pCertInfo->NotAfter;
1709 info->ftStart = context->pCertInfo->NotBefore;
1710 if(unicode) {
1711 len = CertNameToStrW(context->dwCertEncodingType,
1712 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1713 info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
1714 if(info->lpszSubjectInfo)
1715 CertNameToStrW(context->dwCertEncodingType,
1716 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1717 info->lpszSubjectInfo, len);
1718 len = CertNameToStrW(context->dwCertEncodingType,
1719 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1720 info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
1721 if (info->lpszIssuerInfo)
1722 CertNameToStrW(context->dwCertEncodingType,
1723 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1724 info->lpszIssuerInfo, len);
1725 }else {
1726 INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
1728 len = CertNameToStrA(context->dwCertEncodingType,
1729 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1730 infoA->lpszSubjectInfo = LocalAlloc(0, len);
1731 if(infoA->lpszSubjectInfo)
1732 CertNameToStrA(context->dwCertEncodingType,
1733 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1734 infoA->lpszSubjectInfo, len);
1735 len = CertNameToStrA(context->dwCertEncodingType,
1736 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1737 infoA->lpszIssuerInfo = LocalAlloc(0, len);
1738 if(infoA->lpszIssuerInfo)
1739 CertNameToStrA(context->dwCertEncodingType,
1740 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1741 infoA->lpszIssuerInfo, len);
1745 * Contrary to MSDN, these do not appear to be set.
1746 * lpszProtocolName
1747 * lpszSignatureAlgName
1748 * lpszEncryptionAlgName
1749 * dwKeySize
1751 CertFreeCertificateContext(context);
1752 return ERROR_SUCCESS;
1757 return INET_QueryOption(hdr, option, buffer, size, unicode);
1760 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1762 http_request_t *req = (http_request_t*)hdr;
1764 switch(option) {
1765 case INTERNET_OPTION_SEND_TIMEOUT:
1766 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1767 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1769 if (size != sizeof(DWORD))
1770 return ERROR_INVALID_PARAMETER;
1772 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1773 *(DWORD*)buffer);
1775 case INTERNET_OPTION_USERNAME:
1776 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
1777 if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1778 return ERROR_SUCCESS;
1780 case INTERNET_OPTION_PASSWORD:
1781 HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
1782 if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1783 return ERROR_SUCCESS;
1784 case INTERNET_OPTION_HTTP_DECODING:
1785 if(size != sizeof(BOOL))
1786 return ERROR_INVALID_PARAMETER;
1787 req->decoding = *(BOOL*)buffer;
1788 return ERROR_SUCCESS;
1791 return ERROR_INTERNET_INVALID_OPTION;
1794 /* read some more data into the read buffer (the read section must be held) */
1795 static DWORD read_more_data( http_request_t *req, int maxlen )
1797 DWORD res;
1798 int len;
1800 if (req->read_pos)
1802 /* move existing data to the start of the buffer */
1803 if(req->read_size)
1804 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1805 req->read_pos = 0;
1808 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1810 res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1811 maxlen - req->read_size, 0, &len );
1812 if(res == ERROR_SUCCESS)
1813 req->read_size += len;
1815 return res;
1818 /* remove some amount of data from the read buffer (the read section must be held) */
1819 static void remove_data( http_request_t *req, int count )
1821 if (!(req->read_size -= count)) req->read_pos = 0;
1822 else req->read_pos += count;
1825 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1827 int count, bytes_read, pos = 0;
1828 DWORD res;
1830 EnterCriticalSection( &req->read_section );
1831 for (;;)
1833 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1835 if (eol)
1837 count = eol - (req->read_buf + req->read_pos);
1838 bytes_read = count + 1;
1840 else count = bytes_read = req->read_size;
1842 count = min( count, *len - pos );
1843 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1844 pos += count;
1845 remove_data( req, bytes_read );
1846 if (eol) break;
1848 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
1850 *len = 0;
1851 TRACE( "returning empty string\n" );
1852 LeaveCriticalSection( &req->read_section );
1853 INTERNET_SetLastError(res);
1854 return FALSE;
1857 LeaveCriticalSection( &req->read_section );
1859 if (pos < *len)
1861 if (pos && buffer[pos - 1] == '\r') pos--;
1862 *len = pos + 1;
1864 buffer[*len - 1] = 0;
1865 TRACE( "returning %s\n", debugstr_a(buffer));
1866 return TRUE;
1869 /* discard data contents until we reach end of line (the read section must be held) */
1870 static DWORD discard_eol( http_request_t *req )
1872 DWORD res;
1876 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1877 if (eol)
1879 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
1880 break;
1882 req->read_pos = req->read_size = 0; /* discard everything */
1883 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1884 } while (req->read_size);
1885 return ERROR_SUCCESS;
1888 /* read the size of the next chunk (the read section must be held) */
1889 static DWORD start_next_chunk( http_request_t *req )
1891 DWORD chunk_size = 0, res;
1893 if (!req->dwContentLength) return ERROR_SUCCESS;
1894 if (req->dwContentLength == req->dwContentRead)
1896 /* read terminator for the previous chunk */
1897 if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
1898 req->dwContentLength = ~0u;
1899 req->dwContentRead = 0;
1901 for (;;)
1903 while (req->read_size)
1905 char ch = req->read_buf[req->read_pos];
1906 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
1907 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
1908 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
1909 else if (ch == ';' || ch == '\r' || ch == '\n')
1911 TRACE( "reading %u byte chunk\n", chunk_size );
1912 req->dwContentLength = chunk_size;
1913 req->dwContentRead = 0;
1914 return discard_eol( req );
1916 remove_data( req, 1 );
1918 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
1919 if (!req->read_size)
1921 req->dwContentLength = req->dwContentRead = 0;
1922 return ERROR_SUCCESS;
1927 /* check if we have reached the end of the data to read (the read section must be held) */
1928 static BOOL end_of_read_data( http_request_t *req )
1930 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
1931 if (req->read_chunked) return (req->dwContentLength == 0);
1932 if (req->dwContentLength == ~0u) return FALSE;
1933 return (req->dwContentLength == req->dwContentRead);
1936 /* fetch some more data into the read buffer (the read section must be held) */
1937 static DWORD refill_buffer( http_request_t *req )
1939 int len = sizeof(req->read_buf);
1940 DWORD res;
1942 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
1944 if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
1947 if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
1948 if (len <= req->read_size) return ERROR_SUCCESS;
1950 if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
1951 if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
1952 return ERROR_SUCCESS;
1955 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
1957 DWORD ret = ERROR_SUCCESS;
1958 int read = 0;
1960 #ifdef HAVE_ZLIB
1961 z_stream *zstream = &req->gzip_stream->zstream;
1962 DWORD buf_avail;
1963 int zres;
1965 while(read < size && !req->gzip_stream->end_of_data) {
1966 if(!req->read_size) {
1967 if(!sync || refill_buffer(req) != ERROR_SUCCESS)
1968 break;
1971 buf_avail = req->dwContentLength == ~0 ? req->read_size : min(req->read_size, req->dwContentLength-req->dwContentRead);
1973 zstream->next_in = req->read_buf+req->read_pos;
1974 zstream->avail_in = buf_avail;
1975 zstream->next_out = buf+read;
1976 zstream->avail_out = size-read;
1977 zres = inflate(zstream, Z_FULL_FLUSH);
1978 read = size - zstream->avail_out;
1979 req->dwContentRead += buf_avail-zstream->avail_in;
1980 remove_data(req, buf_avail-zstream->avail_in);
1981 if(zres == Z_STREAM_END) {
1982 TRACE("end of data\n");
1983 req->gzip_stream->end_of_data = TRUE;
1984 inflateEnd(&req->gzip_stream->zstream);
1985 }else if(zres != Z_OK) {
1986 WARN("inflate failed %d\n", zres);
1987 if(!read)
1988 ret = ERROR_INTERNET_DECODING_FAILED;
1989 break;
1992 #endif
1994 *read_ret = read;
1995 return ret;
1998 static void refill_gzip_buffer(http_request_t *req)
2000 DWORD res;
2001 int len;
2003 if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2004 return;
2006 if(req->gzip_stream->buf_pos) {
2007 if(req->gzip_stream->buf_size)
2008 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2009 req->gzip_stream->buf_pos = 0;
2012 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2013 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2014 if(res == ERROR_SUCCESS)
2015 req->gzip_stream->buf_size += len;
2018 /* return the size of data available to be read immediately (the read section must be held) */
2019 static DWORD get_avail_data( http_request_t *req )
2021 if (req->gzip_stream) {
2022 refill_gzip_buffer(req);
2023 return req->gzip_stream->buf_size;
2025 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2026 return 0;
2027 return min( req->read_size, req->dwContentLength - req->dwContentRead );
2030 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2032 INTERNET_ASYNC_RESULT iar;
2033 DWORD res;
2035 TRACE("%p\n", req);
2037 EnterCriticalSection( &req->read_section );
2038 if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
2039 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2040 iar.dwError = first_notif ? 0 : get_avail_data(req);
2041 }else {
2042 iar.dwResult = 0;
2043 iar.dwError = res;
2045 LeaveCriticalSection( &req->read_section );
2047 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2048 sizeof(INTERNET_ASYNC_RESULT));
2051 /* read data from the http connection (the read section must be held) */
2052 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2054 BOOL finished_reading = FALSE;
2055 int len, bytes_read = 0;
2056 DWORD ret = ERROR_SUCCESS;
2058 EnterCriticalSection( &req->read_section );
2060 if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
2062 if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
2065 if(req->gzip_stream) {
2066 if(req->gzip_stream->buf_size) {
2067 bytes_read = min(req->gzip_stream->buf_size, size);
2068 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2069 req->gzip_stream->buf_pos += bytes_read;
2070 req->gzip_stream->buf_size -= bytes_read;
2071 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2072 refill_buffer(req);
2075 if(size > bytes_read) {
2076 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2077 if(ret == ERROR_SUCCESS)
2078 bytes_read += len;
2081 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2082 }else {
2083 if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
2085 if (req->read_size) {
2086 bytes_read = min( req->read_size, size );
2087 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2088 remove_data( req, bytes_read );
2091 if (size > bytes_read && (!bytes_read || sync)) {
2092 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2093 sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2094 bytes_read += len;
2095 /* always return success, even if the network layer returns an error */
2098 finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
2099 req->dwContentRead += bytes_read;
2101 done:
2102 *read = bytes_read;
2104 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
2105 LeaveCriticalSection( &req->read_section );
2107 if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
2108 BOOL res;
2109 DWORD dwBytesWritten;
2111 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2112 if(!res)
2113 WARN("WriteFile failed: %u\n", GetLastError());
2116 if(finished_reading)
2117 HTTP_FinishedReading(req);
2119 return ret;
2123 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2125 http_request_t *req = (http_request_t*)hdr;
2126 return HTTPREQ_Read(req, buffer, size, read, TRUE);
2129 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2131 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2132 http_request_t *req = (http_request_t*)workRequest->hdr;
2133 INTERNET_ASYNC_RESULT iar;
2134 DWORD res;
2136 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2138 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2139 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2141 iar.dwResult = res == ERROR_SUCCESS;
2142 iar.dwError = res;
2144 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2145 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2146 sizeof(INTERNET_ASYNC_RESULT));
2149 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2150 DWORD flags, DWORD_PTR context)
2152 http_request_t *req = (http_request_t*)hdr;
2153 DWORD res;
2155 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2156 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2158 if (buffers->dwStructSize != sizeof(*buffers))
2159 return ERROR_INVALID_PARAMETER;
2161 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2163 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2165 WORKREQUEST workRequest;
2167 if (TryEnterCriticalSection( &req->read_section ))
2169 if (get_avail_data(req))
2171 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2172 &buffers->dwBufferLength, FALSE);
2173 LeaveCriticalSection( &req->read_section );
2174 goto done;
2176 LeaveCriticalSection( &req->read_section );
2179 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2180 workRequest.hdr = WININET_AddRef(&req->hdr);
2181 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2183 INTERNET_AsyncCall(&workRequest);
2185 return ERROR_IO_PENDING;
2188 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2189 !(flags & IRF_NO_WAIT));
2191 done:
2192 if (res == ERROR_SUCCESS) {
2193 DWORD size = buffers->dwBufferLength;
2194 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2195 &size, sizeof(size));
2198 return res;
2201 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2203 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2204 http_request_t *req = (http_request_t*)workRequest->hdr;
2205 INTERNET_ASYNC_RESULT iar;
2206 DWORD res;
2208 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2210 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2211 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2213 iar.dwResult = res == ERROR_SUCCESS;
2214 iar.dwError = res;
2216 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2217 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2218 sizeof(INTERNET_ASYNC_RESULT));
2221 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2222 DWORD flags, DWORD_PTR context)
2225 http_request_t *req = (http_request_t*)hdr;
2226 DWORD res;
2228 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2229 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2231 if (buffers->dwStructSize != sizeof(*buffers))
2232 return ERROR_INVALID_PARAMETER;
2234 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2236 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2238 WORKREQUEST workRequest;
2240 if (TryEnterCriticalSection( &req->read_section ))
2242 if (get_avail_data(req))
2244 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2245 &buffers->dwBufferLength, FALSE);
2246 LeaveCriticalSection( &req->read_section );
2247 goto done;
2249 LeaveCriticalSection( &req->read_section );
2252 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2253 workRequest.hdr = WININET_AddRef(&req->hdr);
2254 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2256 INTERNET_AsyncCall(&workRequest);
2258 return ERROR_IO_PENDING;
2261 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
2262 !(flags & IRF_NO_WAIT));
2264 done:
2265 if (res == ERROR_SUCCESS) {
2266 DWORD size = buffers->dwBufferLength;
2267 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2268 &size, sizeof(size));
2271 return res;
2274 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2276 DWORD res;
2277 http_request_t *lpwhr = (http_request_t*)hdr;
2279 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2281 *written = 0;
2282 res = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written);
2283 if (res == ERROR_SUCCESS)
2284 lpwhr->dwBytesWritten += *written;
2286 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2287 return res;
2290 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2292 http_request_t *req = (http_request_t*)workRequest->hdr;
2294 HTTP_ReceiveRequestData(req, FALSE);
2297 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2299 http_request_t *req = (http_request_t*)hdr;
2301 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2303 if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2305 WORKREQUEST workRequest;
2307 /* never wait, if we can't enter the section we queue an async request right away */
2308 if (TryEnterCriticalSection( &req->read_section ))
2310 if ((*available = get_avail_data( req ))) goto done;
2311 if (end_of_read_data( req )) goto done;
2312 LeaveCriticalSection( &req->read_section );
2315 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2316 workRequest.hdr = WININET_AddRef( &req->hdr );
2318 INTERNET_AsyncCall(&workRequest);
2320 return ERROR_IO_PENDING;
2323 EnterCriticalSection( &req->read_section );
2325 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2327 refill_buffer( req );
2328 *available = get_avail_data( req );
2331 done:
2332 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2334 DWORD extra;
2335 if (NETCON_query_data_available(&req->netConnection, &extra))
2336 *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
2338 LeaveCriticalSection( &req->read_section );
2340 TRACE( "returning %u\n", *available );
2341 return ERROR_SUCCESS;
2344 static const object_vtbl_t HTTPREQVtbl = {
2345 HTTPREQ_Destroy,
2346 HTTPREQ_CloseConnection,
2347 HTTPREQ_QueryOption,
2348 HTTPREQ_SetOption,
2349 HTTPREQ_ReadFile,
2350 HTTPREQ_ReadFileExA,
2351 HTTPREQ_ReadFileExW,
2352 HTTPREQ_WriteFile,
2353 HTTPREQ_QueryDataAvailable,
2354 NULL
2357 /***********************************************************************
2358 * HTTP_HttpOpenRequestW (internal)
2360 * Open a HTTP request handle
2362 * RETURNS
2363 * HINTERNET a HTTP request handle on success
2364 * NULL on failure
2367 static DWORD HTTP_HttpOpenRequestW(http_session_t *lpwhs,
2368 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2369 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2370 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2372 appinfo_t *hIC = NULL;
2373 http_request_t *lpwhr;
2374 LPWSTR lpszHostName = NULL;
2375 HINTERNET handle = NULL;
2376 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2377 DWORD len, res;
2379 TRACE("-->\n");
2381 assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
2382 hIC = lpwhs->lpAppInfo;
2384 lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
2385 if (NULL == lpwhr)
2387 res = ERROR_OUTOFMEMORY;
2388 goto lend;
2390 lpwhr->hdr.htype = WH_HHTTPREQ;
2391 lpwhr->hdr.vtbl = &HTTPREQVtbl;
2392 lpwhr->hdr.dwFlags = dwFlags;
2393 lpwhr->hdr.dwContext = dwContext;
2394 lpwhr->hdr.refs = 1;
2395 lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
2396 lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
2397 lpwhr->dwContentLength = ~0u;
2398 InitializeCriticalSection( &lpwhr->read_section );
2400 WININET_AddRef( &lpwhs->hdr );
2401 lpwhr->lpHttpSession = lpwhs;
2402 list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
2404 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2405 (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
2406 if (NULL == lpszHostName)
2408 res = ERROR_OUTOFMEMORY;
2409 goto lend;
2412 handle = WININET_AllocHandle( &lpwhr->hdr );
2413 if (NULL == handle)
2415 res = ERROR_OUTOFMEMORY;
2416 goto lend;
2419 if ((res = NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2421 InternetCloseHandle( handle );
2422 handle = NULL;
2423 goto lend;
2426 if (lpszObjectName && *lpszObjectName) {
2427 HRESULT rc;
2429 len = 0;
2430 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2431 if (rc != E_POINTER)
2432 len = strlenW(lpszObjectName)+1;
2433 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2434 rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
2435 URL_ESCAPE_SPACES_ONLY);
2436 if (rc != S_OK)
2438 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2439 strcpyW(lpwhr->lpszPath,lpszObjectName);
2441 }else {
2442 static const WCHAR slashW[] = {'/',0};
2444 lpwhr->lpszPath = heap_strdupW(slashW);
2447 if (lpszReferrer && *lpszReferrer)
2448 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2450 if (lpszAcceptTypes)
2452 int i;
2453 for (i = 0; lpszAcceptTypes[i]; i++)
2455 if (!*lpszAcceptTypes[i]) continue;
2456 HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
2457 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2458 HTTP_ADDHDR_FLAG_REQ |
2459 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2463 lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2464 lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2466 if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
2467 lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
2468 lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
2470 sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
2471 HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
2472 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2474 else
2475 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
2476 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2478 if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
2479 lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
2480 INTERNET_DEFAULT_HTTPS_PORT :
2481 INTERNET_DEFAULT_HTTP_PORT);
2483 if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
2484 lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2485 INTERNET_DEFAULT_HTTPS_PORT :
2486 INTERNET_DEFAULT_HTTP_PORT);
2488 if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
2489 HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
2491 INTERNET_SendCallback(&lpwhs->hdr, dwContext,
2492 INTERNET_STATUS_HANDLE_CREATED, &handle,
2493 sizeof(handle));
2495 lend:
2496 HeapFree(GetProcessHeap(), 0, lpszHostName);
2497 if( lpwhr )
2498 WININET_Release( &lpwhr->hdr );
2500 TRACE("<-- %p (%p)\n", handle, lpwhr);
2501 *ret = handle;
2502 return res;
2505 /***********************************************************************
2506 * HttpOpenRequestW (WININET.@)
2508 * Open a HTTP request handle
2510 * RETURNS
2511 * HINTERNET a HTTP request handle on success
2512 * NULL on failure
2515 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2516 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2517 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2518 DWORD dwFlags, DWORD_PTR dwContext)
2520 http_session_t *lpwhs;
2521 HINTERNET handle = NULL;
2522 DWORD res;
2524 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2525 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2526 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2527 dwFlags, dwContext);
2528 if(lpszAcceptTypes!=NULL)
2530 int i;
2531 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2532 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2535 lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
2536 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
2538 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2539 goto lend;
2543 * My tests seem to show that the windows version does not
2544 * become asynchronous until after this point. And anyhow
2545 * if this call was asynchronous then how would you get the
2546 * necessary HINTERNET pointer returned by this function.
2549 res = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
2550 lpszVersion, lpszReferrer, lpszAcceptTypes,
2551 dwFlags, dwContext, &handle);
2552 lend:
2553 if( lpwhs )
2554 WININET_Release( &lpwhs->hdr );
2555 TRACE("returning %p\n", handle);
2556 if(res != ERROR_SUCCESS)
2557 SetLastError(res);
2558 return handle;
2561 /* read any content returned by the server so that the connection can be
2562 * reused */
2563 static void HTTP_DrainContent(http_request_t *req)
2565 DWORD bytes_read;
2567 if (!NETCON_connected(&req->netConnection)) return;
2569 if (req->dwContentLength == -1)
2571 NETCON_close(&req->netConnection);
2572 return;
2574 if (!strcmpW(req->lpszVerb, szHEAD)) return;
2578 char buffer[2048];
2579 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2580 return;
2581 } while (bytes_read);
2584 static const LPCWSTR header_lookup[] = {
2585 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2586 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2587 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2588 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2589 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2590 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2591 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2592 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2593 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2594 szDate, /* HTTP_QUERY_DATE = 9 */
2595 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2596 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2597 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2598 szURI, /* HTTP_QUERY_URI = 13 */
2599 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2600 NULL, /* HTTP_QUERY_COST = 15 */
2601 NULL, /* HTTP_QUERY_LINK = 16 */
2602 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2603 NULL, /* HTTP_QUERY_VERSION = 18 */
2604 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2605 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2606 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2607 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2608 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2609 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2610 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2611 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2612 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2613 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2614 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2615 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2616 NULL, /* HTTP_QUERY_FROM = 31 */
2617 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2618 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2619 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2620 szReferer, /* HTTP_QUERY_REFERER = 35 */
2621 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2622 szServer, /* HTTP_QUERY_SERVER = 37 */
2623 NULL, /* HTTP_TITLE = 38 */
2624 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2625 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2626 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2627 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2628 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2629 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2630 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2631 NULL, /* HTTP_QUERY_REFRESH = 46 */
2632 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2633 szAge, /* HTTP_QUERY_AGE = 48 */
2634 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2635 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2636 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2637 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2638 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2639 szETag, /* HTTP_QUERY_ETAG = 54 */
2640 hostW, /* HTTP_QUERY_HOST = 55 */
2641 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2642 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2643 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2644 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2645 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2646 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2647 szRange, /* HTTP_QUERY_RANGE = 62 */
2648 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2649 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2650 szVary, /* HTTP_QUERY_VARY = 65 */
2651 szVia, /* HTTP_QUERY_VIA = 66 */
2652 szWarning, /* HTTP_QUERY_WARNING = 67 */
2653 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2654 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2655 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2658 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2660 /***********************************************************************
2661 * HTTP_HttpQueryInfoW (internal)
2663 static DWORD HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
2664 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2666 LPHTTPHEADERW lphttpHdr = NULL;
2667 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2668 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2669 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2670 INT index = -1;
2672 /* Find requested header structure */
2673 switch (level)
2675 case HTTP_QUERY_CUSTOM:
2676 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
2677 index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
2678 break;
2679 case HTTP_QUERY_RAW_HEADERS_CRLF:
2681 LPWSTR headers;
2682 DWORD len = 0;
2683 DWORD res = ERROR_INVALID_PARAMETER;
2685 if (request_only)
2686 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
2687 else
2688 headers = lpwhr->lpszRawHeaders;
2690 if (headers)
2691 len = strlenW(headers) * sizeof(WCHAR);
2693 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2695 len += sizeof(WCHAR);
2696 res = ERROR_INSUFFICIENT_BUFFER;
2698 else if (lpBuffer)
2700 if (headers)
2701 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2702 else
2704 len = strlenW(szCrLf) * sizeof(WCHAR);
2705 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2707 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2708 res = ERROR_SUCCESS;
2710 *lpdwBufferLength = len;
2712 if (request_only)
2713 HeapFree(GetProcessHeap(), 0, headers);
2714 return res;
2716 case HTTP_QUERY_RAW_HEADERS:
2718 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
2719 DWORD i, size = 0;
2720 LPWSTR pszString = lpBuffer;
2722 for (i = 0; ppszRawHeaderLines[i]; i++)
2723 size += strlenW(ppszRawHeaderLines[i]) + 1;
2725 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2727 HTTP_FreeTokens(ppszRawHeaderLines);
2728 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2729 return ERROR_INSUFFICIENT_BUFFER;
2731 if (pszString)
2733 for (i = 0; ppszRawHeaderLines[i]; i++)
2735 DWORD len = strlenW(ppszRawHeaderLines[i]);
2736 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2737 pszString += len+1;
2739 *pszString = '\0';
2740 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2742 *lpdwBufferLength = size * sizeof(WCHAR);
2743 HTTP_FreeTokens(ppszRawHeaderLines);
2745 return ERROR_SUCCESS;
2747 case HTTP_QUERY_STATUS_TEXT:
2748 if (lpwhr->lpszStatusText)
2750 DWORD len = strlenW(lpwhr->lpszStatusText);
2751 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2753 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2754 return ERROR_INSUFFICIENT_BUFFER;
2756 if (lpBuffer)
2758 memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
2759 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2761 *lpdwBufferLength = len * sizeof(WCHAR);
2762 return ERROR_SUCCESS;
2764 break;
2765 case HTTP_QUERY_VERSION:
2766 if (lpwhr->lpszVersion)
2768 DWORD len = strlenW(lpwhr->lpszVersion);
2769 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2771 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2772 return ERROR_INSUFFICIENT_BUFFER;
2774 if (lpBuffer)
2776 memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
2777 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2779 *lpdwBufferLength = len * sizeof(WCHAR);
2780 return ERROR_SUCCESS;
2782 break;
2783 case HTTP_QUERY_CONTENT_ENCODING:
2784 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2785 requested_index,request_only);
2786 break;
2787 default:
2788 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2790 if (level < LAST_TABLE_HEADER && header_lookup[level])
2791 index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
2792 requested_index,request_only);
2795 if (index >= 0)
2796 lphttpHdr = &lpwhr->pCustHeaders[index];
2798 /* Ensure header satisfies requested attributes */
2799 if (!lphttpHdr ||
2800 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
2801 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
2803 return ERROR_HTTP_HEADER_NOT_FOUND;
2806 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
2808 /* coalesce value to requested type */
2809 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
2811 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
2812 TRACE(" returning number: %d\n", *(int *)lpBuffer);
2814 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
2816 time_t tmpTime;
2817 struct tm tmpTM;
2818 SYSTEMTIME *STHook;
2820 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
2822 tmpTM = *gmtime(&tmpTime);
2823 STHook = (SYSTEMTIME *)lpBuffer;
2824 STHook->wDay = tmpTM.tm_mday;
2825 STHook->wHour = tmpTM.tm_hour;
2826 STHook->wMilliseconds = 0;
2827 STHook->wMinute = tmpTM.tm_min;
2828 STHook->wDayOfWeek = tmpTM.tm_wday;
2829 STHook->wMonth = tmpTM.tm_mon + 1;
2830 STHook->wSecond = tmpTM.tm_sec;
2831 STHook->wYear = tmpTM.tm_year;
2833 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
2834 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
2835 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
2837 else if (lphttpHdr->lpszValue)
2839 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
2841 if (len > *lpdwBufferLength)
2843 *lpdwBufferLength = len;
2844 return ERROR_INSUFFICIENT_BUFFER;
2846 if (lpBuffer)
2848 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
2849 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
2851 *lpdwBufferLength = len - sizeof(WCHAR);
2853 return ERROR_SUCCESS;
2856 /***********************************************************************
2857 * HttpQueryInfoW (WININET.@)
2859 * Queries for information about an HTTP request
2861 * RETURNS
2862 * TRUE on success
2863 * FALSE on failure
2866 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
2867 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2869 http_request_t *lpwhr;
2870 DWORD res;
2872 if (TRACE_ON(wininet)) {
2873 #define FE(x) { x, #x }
2874 static const wininet_flag_info query_flags[] = {
2875 FE(HTTP_QUERY_MIME_VERSION),
2876 FE(HTTP_QUERY_CONTENT_TYPE),
2877 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
2878 FE(HTTP_QUERY_CONTENT_ID),
2879 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
2880 FE(HTTP_QUERY_CONTENT_LENGTH),
2881 FE(HTTP_QUERY_CONTENT_LANGUAGE),
2882 FE(HTTP_QUERY_ALLOW),
2883 FE(HTTP_QUERY_PUBLIC),
2884 FE(HTTP_QUERY_DATE),
2885 FE(HTTP_QUERY_EXPIRES),
2886 FE(HTTP_QUERY_LAST_MODIFIED),
2887 FE(HTTP_QUERY_MESSAGE_ID),
2888 FE(HTTP_QUERY_URI),
2889 FE(HTTP_QUERY_DERIVED_FROM),
2890 FE(HTTP_QUERY_COST),
2891 FE(HTTP_QUERY_LINK),
2892 FE(HTTP_QUERY_PRAGMA),
2893 FE(HTTP_QUERY_VERSION),
2894 FE(HTTP_QUERY_STATUS_CODE),
2895 FE(HTTP_QUERY_STATUS_TEXT),
2896 FE(HTTP_QUERY_RAW_HEADERS),
2897 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
2898 FE(HTTP_QUERY_CONNECTION),
2899 FE(HTTP_QUERY_ACCEPT),
2900 FE(HTTP_QUERY_ACCEPT_CHARSET),
2901 FE(HTTP_QUERY_ACCEPT_ENCODING),
2902 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
2903 FE(HTTP_QUERY_AUTHORIZATION),
2904 FE(HTTP_QUERY_CONTENT_ENCODING),
2905 FE(HTTP_QUERY_FORWARDED),
2906 FE(HTTP_QUERY_FROM),
2907 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
2908 FE(HTTP_QUERY_LOCATION),
2909 FE(HTTP_QUERY_ORIG_URI),
2910 FE(HTTP_QUERY_REFERER),
2911 FE(HTTP_QUERY_RETRY_AFTER),
2912 FE(HTTP_QUERY_SERVER),
2913 FE(HTTP_QUERY_TITLE),
2914 FE(HTTP_QUERY_USER_AGENT),
2915 FE(HTTP_QUERY_WWW_AUTHENTICATE),
2916 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
2917 FE(HTTP_QUERY_ACCEPT_RANGES),
2918 FE(HTTP_QUERY_SET_COOKIE),
2919 FE(HTTP_QUERY_COOKIE),
2920 FE(HTTP_QUERY_REQUEST_METHOD),
2921 FE(HTTP_QUERY_REFRESH),
2922 FE(HTTP_QUERY_CONTENT_DISPOSITION),
2923 FE(HTTP_QUERY_AGE),
2924 FE(HTTP_QUERY_CACHE_CONTROL),
2925 FE(HTTP_QUERY_CONTENT_BASE),
2926 FE(HTTP_QUERY_CONTENT_LOCATION),
2927 FE(HTTP_QUERY_CONTENT_MD5),
2928 FE(HTTP_QUERY_CONTENT_RANGE),
2929 FE(HTTP_QUERY_ETAG),
2930 FE(HTTP_QUERY_HOST),
2931 FE(HTTP_QUERY_IF_MATCH),
2932 FE(HTTP_QUERY_IF_NONE_MATCH),
2933 FE(HTTP_QUERY_IF_RANGE),
2934 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
2935 FE(HTTP_QUERY_MAX_FORWARDS),
2936 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
2937 FE(HTTP_QUERY_RANGE),
2938 FE(HTTP_QUERY_TRANSFER_ENCODING),
2939 FE(HTTP_QUERY_UPGRADE),
2940 FE(HTTP_QUERY_VARY),
2941 FE(HTTP_QUERY_VIA),
2942 FE(HTTP_QUERY_WARNING),
2943 FE(HTTP_QUERY_CUSTOM)
2945 static const wininet_flag_info modifier_flags[] = {
2946 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
2947 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
2948 FE(HTTP_QUERY_FLAG_NUMBER),
2949 FE(HTTP_QUERY_FLAG_COALESCE)
2951 #undef FE
2952 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
2953 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
2954 DWORD i;
2956 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
2957 TRACE(" Attribute:");
2958 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
2959 if (query_flags[i].val == info) {
2960 TRACE(" %s", query_flags[i].name);
2961 break;
2964 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
2965 TRACE(" Unknown (%08x)", info);
2968 TRACE(" Modifier:");
2969 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
2970 if (modifier_flags[i].val & info_mod) {
2971 TRACE(" %s", modifier_flags[i].name);
2972 info_mod &= ~ modifier_flags[i].val;
2976 if (info_mod) {
2977 TRACE(" Unknown (%08x)", info_mod);
2979 TRACE("\n");
2982 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
2983 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
2985 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2986 goto lend;
2989 if (lpBuffer == NULL)
2990 *lpdwBufferLength = 0;
2991 res = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
2992 lpBuffer, lpdwBufferLength, lpdwIndex);
2994 lend:
2995 if( lpwhr )
2996 WININET_Release( &lpwhr->hdr );
2998 TRACE("%u <--\n", res);
2999 if(res != ERROR_SUCCESS)
3000 SetLastError(res);
3001 return res == ERROR_SUCCESS;
3004 /***********************************************************************
3005 * HttpQueryInfoA (WININET.@)
3007 * Queries for information about an HTTP request
3009 * RETURNS
3010 * TRUE on success
3011 * FALSE on failure
3014 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3015 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3017 BOOL result;
3018 DWORD len;
3019 WCHAR* bufferW;
3021 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3022 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3024 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3025 lpdwBufferLength, lpdwIndex );
3028 if (lpBuffer)
3030 DWORD alloclen;
3031 len = (*lpdwBufferLength)*sizeof(WCHAR);
3032 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3034 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3035 if (alloclen < len)
3036 alloclen = len;
3038 else
3039 alloclen = len;
3040 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3041 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3042 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3043 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3044 } else
3046 bufferW = NULL;
3047 len = 0;
3050 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3051 &len, lpdwIndex );
3052 if( result )
3054 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3055 lpBuffer, *lpdwBufferLength, NULL, NULL );
3056 *lpdwBufferLength = len - 1;
3058 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3060 else
3061 /* since the strings being returned from HttpQueryInfoW should be
3062 * only ASCII characters, it is reasonable to assume that all of
3063 * the Unicode characters can be reduced to a single byte */
3064 *lpdwBufferLength = len / sizeof(WCHAR);
3066 HeapFree(GetProcessHeap(), 0, bufferW );
3068 return result;
3071 /***********************************************************************
3072 * HTTP_GetRedirectURL (internal)
3074 static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
3076 static WCHAR szHttp[] = {'h','t','t','p',0};
3077 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3078 http_session_t *lpwhs = lpwhr->lpHttpSession;
3079 URL_COMPONENTSW urlComponents;
3080 DWORD url_length = 0;
3081 LPWSTR orig_url;
3082 LPWSTR combined_url;
3084 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3085 urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3086 urlComponents.dwSchemeLength = 0;
3087 urlComponents.lpszHostName = lpwhs->lpszHostName;
3088 urlComponents.dwHostNameLength = 0;
3089 urlComponents.nPort = lpwhs->nHostPort;
3090 urlComponents.lpszUserName = lpwhs->lpszUserName;
3091 urlComponents.dwUserNameLength = 0;
3092 urlComponents.lpszPassword = NULL;
3093 urlComponents.dwPasswordLength = 0;
3094 urlComponents.lpszUrlPath = lpwhr->lpszPath;
3095 urlComponents.dwUrlPathLength = 0;
3096 urlComponents.lpszExtraInfo = NULL;
3097 urlComponents.dwExtraInfoLength = 0;
3099 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3100 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3101 return NULL;
3103 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3105 /* convert from bytes to characters */
3106 url_length = url_length / sizeof(WCHAR) - 1;
3107 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3109 HeapFree(GetProcessHeap(), 0, orig_url);
3110 return NULL;
3113 url_length = 0;
3114 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3115 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3117 HeapFree(GetProcessHeap(), 0, orig_url);
3118 return NULL;
3120 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3122 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3124 HeapFree(GetProcessHeap(), 0, orig_url);
3125 HeapFree(GetProcessHeap(), 0, combined_url);
3126 return NULL;
3128 HeapFree(GetProcessHeap(), 0, orig_url);
3129 return combined_url;
3133 /***********************************************************************
3134 * HTTP_HandleRedirect (internal)
3136 static DWORD HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
3138 http_session_t *lpwhs = lpwhr->lpHttpSession;
3139 appinfo_t *hIC = lpwhs->lpAppInfo;
3140 BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
3141 WCHAR path[INTERNET_MAX_URL_LENGTH];
3142 int index;
3144 if(lpszUrl[0]=='/')
3146 /* if it's an absolute path, keep the same session info */
3147 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3149 else
3151 URL_COMPONENTSW urlComponents;
3152 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3153 static WCHAR szHttp[] = {'h','t','t','p',0};
3154 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3156 userName[0] = 0;
3157 hostName[0] = 0;
3158 protocol[0] = 0;
3160 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3161 urlComponents.lpszScheme = protocol;
3162 urlComponents.dwSchemeLength = 32;
3163 urlComponents.lpszHostName = hostName;
3164 urlComponents.dwHostNameLength = MAXHOSTNAME;
3165 urlComponents.lpszUserName = userName;
3166 urlComponents.dwUserNameLength = 1024;
3167 urlComponents.lpszPassword = NULL;
3168 urlComponents.dwPasswordLength = 0;
3169 urlComponents.lpszUrlPath = path;
3170 urlComponents.dwUrlPathLength = 2048;
3171 urlComponents.lpszExtraInfo = NULL;
3172 urlComponents.dwExtraInfoLength = 0;
3173 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3174 return INTERNET_GetLastError();
3176 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3177 (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3179 TRACE("redirect from secure page to non-secure page\n");
3180 /* FIXME: warn about from secure redirect to non-secure page */
3181 lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3183 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3184 !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
3186 TRACE("redirect from non-secure page to secure page\n");
3187 /* FIXME: notify about redirect to secure page */
3188 lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3191 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3193 if (lstrlenW(protocol)>4) /*https*/
3194 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3195 else /*http*/
3196 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3199 #if 0
3201 * This upsets redirects to binary files on sourceforge.net
3202 * and gives an html page instead of the target file
3203 * Examination of the HTTP request sent by native wininet.dll
3204 * reveals that it doesn't send a referrer in that case.
3205 * Maybe there's a flag that enables this, or maybe a referrer
3206 * shouldn't be added in case of a redirect.
3209 /* consider the current host as the referrer */
3210 if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
3211 HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
3212 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3213 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3214 #endif
3216 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
3217 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3218 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3220 int len;
3221 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3222 len = lstrlenW(hostName);
3223 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3224 lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3225 sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
3227 else
3228 lpwhs->lpszHostName = heap_strdupW(hostName);
3230 HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3232 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
3233 lpwhs->lpszUserName = NULL;
3234 if (userName[0])
3235 lpwhs->lpszUserName = heap_strdupW(userName);
3237 if (!using_proxy)
3239 if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
3241 DWORD res;
3243 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
3244 lpwhs->lpszServerName = heap_strdupW(hostName);
3245 lpwhs->nServerPort = urlComponents.nPort;
3247 NETCON_close(&lpwhr->netConnection);
3248 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS)
3249 return res;
3251 res = NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE);
3252 if (res != ERROR_SUCCESS)
3253 return res;
3255 lpwhr->read_pos = lpwhr->read_size = 0;
3256 lpwhr->read_chunked = FALSE;
3259 else
3260 TRACE("Redirect through proxy\n");
3263 HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
3264 lpwhr->lpszPath=NULL;
3265 if (*path)
3267 DWORD needed = 0;
3268 HRESULT rc;
3270 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3271 if (rc != E_POINTER)
3272 needed = strlenW(path)+1;
3273 lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3274 rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
3275 URL_ESCAPE_SPACES_ONLY);
3276 if (rc != S_OK)
3278 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3279 strcpyW(lpwhr->lpszPath,path);
3283 /* Remove custom content-type/length headers on redirects. */
3284 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
3285 if (0 <= index)
3286 HTTP_DeleteCustomHeader(lpwhr, index);
3287 index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
3288 if (0 <= index)
3289 HTTP_DeleteCustomHeader(lpwhr, index);
3291 return ERROR_SUCCESS;
3294 /***********************************************************************
3295 * HTTP_build_req (internal)
3297 * concatenate all the strings in the request together
3299 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3301 LPCWSTR *t;
3302 LPWSTR str;
3304 for( t = list; *t ; t++ )
3305 len += strlenW( *t );
3306 len++;
3308 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3309 *str = 0;
3311 for( t = list; *t ; t++ )
3312 strcatW( str, *t );
3314 return str;
3317 static DWORD HTTP_SecureProxyConnect(http_request_t *lpwhr)
3319 LPWSTR lpszPath;
3320 LPWSTR requestString;
3321 INT len;
3322 INT cnt;
3323 INT responseLen;
3324 char *ascii_req;
3325 DWORD res;
3326 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3327 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3328 http_session_t *lpwhs = lpwhr->lpHttpSession;
3330 TRACE("\n");
3332 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
3333 sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
3334 requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
3335 HeapFree( GetProcessHeap(), 0, lpszPath );
3337 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3338 NULL, 0, NULL, NULL );
3339 len--; /* the nul terminator isn't needed */
3340 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3341 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3342 ascii_req, len, NULL, NULL );
3343 HeapFree( GetProcessHeap(), 0, requestString );
3345 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3347 res = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
3348 HeapFree( GetProcessHeap(), 0, ascii_req );
3349 if (res != ERROR_SUCCESS)
3350 return res;
3352 responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
3353 if (!responseLen)
3354 return ERROR_HTTP_INVALID_HEADER;
3356 return ERROR_SUCCESS;
3359 static void HTTP_InsertCookies(http_request_t *lpwhr)
3361 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3362 LPWSTR lpszCookies, lpszUrl = NULL;
3363 DWORD nCookieSize, size;
3364 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3366 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
3367 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3368 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
3370 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3372 int cnt = 0;
3373 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3375 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3376 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3378 cnt += sprintfW(lpszCookies, szCookie);
3379 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3380 strcatW(lpszCookies, szCrLf);
3382 HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3383 HeapFree(GetProcessHeap(), 0, lpszCookies);
3386 HeapFree(GetProcessHeap(), 0, lpszUrl);
3389 /***********************************************************************
3390 * HTTP_HttpSendRequestW (internal)
3392 * Sends the specified request to the HTTP server
3394 * RETURNS
3395 * TRUE on success
3396 * FALSE on failure
3399 static DWORD HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
3400 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
3401 DWORD dwContentLength, BOOL bEndRequest)
3403 INT cnt;
3404 BOOL redirected = FALSE;
3405 LPWSTR requestString = NULL;
3406 INT responseLen;
3407 BOOL loop_next;
3408 INTERNET_ASYNC_RESULT iar;
3409 static const WCHAR szPost[] = { 'P','O','S','T',0 };
3410 static const WCHAR szContentLength[] =
3411 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
3412 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
3413 DWORD res;
3415 TRACE("--> %p\n", lpwhr);
3417 assert(lpwhr->hdr.htype == WH_HHTTPREQ);
3419 /* if the verb is NULL default to GET */
3420 if (!lpwhr->lpszVerb)
3421 lpwhr->lpszVerb = heap_strdupW(szGET);
3423 if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
3425 sprintfW(contentLengthStr, szContentLength, dwContentLength);
3426 HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
3427 lpwhr->dwBytesToWrite = dwContentLength;
3429 if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
3431 WCHAR *agent_header;
3432 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
3433 int len;
3435 len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
3436 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3437 sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
3439 HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3440 HeapFree(GetProcessHeap(), 0, agent_header);
3442 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
3444 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
3445 HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3447 if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
3449 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
3450 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
3451 HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
3456 DWORD len;
3457 BOOL reusing_connection;
3458 char *ascii_req;
3460 loop_next = FALSE;
3462 /* like native, just in case the caller forgot to call InternetReadFile
3463 * for all the data */
3464 HTTP_DrainContent(lpwhr);
3465 lpwhr->dwContentRead = 0;
3466 if(redirected) {
3467 lpwhr->dwContentLength = ~0u;
3468 lpwhr->dwBytesToWrite = 0;
3471 if (TRACE_ON(wininet))
3473 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3474 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
3477 HTTP_FixURL(lpwhr);
3478 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
3480 HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
3482 HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
3483 HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
3485 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
3486 HTTP_InsertCookies(lpwhr);
3488 /* add the headers the caller supplied */
3489 if( lpszHeaders && dwHeaderLength )
3491 HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
3492 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
3495 if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
3497 WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
3498 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
3499 HeapFree(GetProcessHeap(), 0, url);
3501 else
3502 requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
3505 TRACE("Request header -> %s\n", debugstr_w(requestString) );
3507 /* Send the request and store the results */
3508 if(NETCON_connected(&lpwhr->netConnection))
3509 reusing_connection = TRUE;
3510 else
3511 reusing_connection = FALSE;
3513 if ((res = HTTP_OpenConnection(lpwhr)) != ERROR_SUCCESS)
3514 goto lend;
3516 /* send the request as ASCII, tack on the optional data */
3517 if (!lpOptional || redirected)
3518 dwOptionalLength = 0;
3519 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3520 NULL, 0, NULL, NULL );
3521 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
3522 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3523 ascii_req, len, NULL, NULL );
3524 if( lpOptional )
3525 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
3526 len = (len + dwOptionalLength - 1);
3527 ascii_req[len] = 0;
3528 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
3530 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3531 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
3533 res = NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
3534 HeapFree( GetProcessHeap(), 0, ascii_req );
3536 lpwhr->dwBytesWritten = dwOptionalLength;
3538 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3539 INTERNET_STATUS_REQUEST_SENT,
3540 &len, sizeof(DWORD));
3542 if (bEndRequest)
3544 DWORD dwBufferSize;
3545 DWORD dwStatusCode;
3547 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3548 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3550 if (res != ERROR_SUCCESS)
3551 goto lend;
3553 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3554 /* FIXME: We should know that connection is closed before sending
3555 * headers. Otherwise wrong callbacks are executed */
3556 if(!responseLen && reusing_connection) {
3557 TRACE("Connection closed by server, reconnecting\n");
3558 loop_next = TRUE;
3559 continue;
3562 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3563 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
3564 sizeof(DWORD));
3566 HTTP_ProcessCookies(lpwhr);
3568 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3570 dwBufferSize = sizeof(dwStatusCode);
3571 if (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
3572 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
3573 dwStatusCode = 0;
3575 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
3577 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3578 dwBufferSize=sizeof(szNewLocation);
3579 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
3580 HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
3582 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3584 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3585 lpwhr->lpszVerb = heap_strdupW(szGET);
3587 HTTP_DrainContent(lpwhr);
3588 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3590 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3591 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3592 res = HTTP_HandleRedirect(lpwhr, new_url);
3593 if (res == ERROR_SUCCESS)
3595 HeapFree(GetProcessHeap(), 0, requestString);
3596 loop_next = TRUE;
3598 HeapFree( GetProcessHeap(), 0, new_url );
3600 redirected = TRUE;
3603 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
3605 WCHAR szAuthValue[2048];
3606 dwBufferSize=2048;
3607 if (dwStatusCode == HTTP_STATUS_DENIED)
3609 LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
3610 DWORD dwIndex = 0;
3611 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3613 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3614 &lpwhr->pAuthInfo,
3615 lpwhr->lpHttpSession->lpszUserName,
3616 lpwhr->lpHttpSession->lpszPassword,
3617 Host->lpszValue))
3619 HeapFree(GetProcessHeap(), 0, requestString);
3620 loop_next = TRUE;
3621 break;
3625 if(!loop_next) {
3626 TRACE("Cleaning wrong authorization data\n");
3627 destroy_authinfo(lpwhr->pAuthInfo);
3628 lpwhr->pAuthInfo = NULL;
3631 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
3633 DWORD dwIndex = 0;
3634 while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
3636 if (HTTP_DoAuthorization(lpwhr, szAuthValue,
3637 &lpwhr->pProxyAuthInfo,
3638 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
3639 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
3640 NULL))
3642 loop_next = TRUE;
3643 break;
3647 if(!loop_next) {
3648 TRACE("Cleaning wrong proxy authorization data\n");
3649 destroy_authinfo(lpwhr->pProxyAuthInfo);
3650 lpwhr->pProxyAuthInfo = NULL;
3655 else
3656 res = ERROR_SUCCESS;
3658 while (loop_next);
3660 if(res == ERROR_SUCCESS) {
3661 WCHAR url[INTERNET_MAX_URL_LENGTH];
3662 WCHAR cacheFileName[MAX_PATH+1];
3663 BOOL b;
3665 b = HTTP_GetRequestURL(lpwhr, url);
3666 if(!b) {
3667 WARN("Could not get URL\n");
3668 goto lend;
3671 b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
3672 if(b) {
3673 HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
3674 CloseHandle(lpwhr->hCacheFile);
3676 lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
3677 lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3678 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3679 if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
3680 WARN("Could not create file: %u\n", GetLastError());
3681 lpwhr->hCacheFile = NULL;
3683 }else {
3684 WARN("Could not create cache entry: %08x\n", GetLastError());
3688 lend:
3690 HeapFree(GetProcessHeap(), 0, requestString);
3692 /* TODO: send notification for P3P header */
3694 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3696 if (res == ERROR_SUCCESS && lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite)
3697 HTTP_ReceiveRequestData(lpwhr, TRUE);
3698 else
3700 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)lpwhr->hdr.hInternet : 0);
3701 iar.dwError = res;
3703 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3704 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3705 sizeof(INTERNET_ASYNC_RESULT));
3709 TRACE("<--\n");
3710 return res;
3713 /***********************************************************************
3715 * Helper functions for the HttpSendRequest(Ex) functions
3718 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
3720 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
3721 http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
3723 TRACE("%p\n", lpwhr);
3725 HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
3726 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
3727 req->dwContentLength, req->bEndRequest);
3729 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
3733 static DWORD HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
3735 INT responseLen;
3736 DWORD dwBufferSize;
3737 INTERNET_ASYNC_RESULT iar;
3738 DWORD res = ERROR_SUCCESS;
3740 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3741 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
3743 responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
3744 if (!responseLen)
3745 res = ERROR_HTTP_HEADER_NOT_FOUND;
3747 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3748 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
3750 /* process cookies here. Is this right? */
3751 HTTP_ProcessCookies(lpwhr);
3753 if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
3755 if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
3757 DWORD dwCode,dwCodeLength = sizeof(DWORD);
3758 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
3759 && (dwCode == 302 || dwCode == 301 || dwCode == 303))
3761 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
3762 dwBufferSize=sizeof(szNewLocation);
3763 if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
3765 if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
3767 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
3768 lpwhr->lpszVerb = heap_strdupW(szGET);
3770 HTTP_DrainContent(lpwhr);
3771 if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
3773 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
3774 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
3775 res = HTTP_HandleRedirect(lpwhr, new_url);
3776 if (res == ERROR_SUCCESS)
3777 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
3778 HeapFree( GetProcessHeap(), 0, new_url );
3784 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)lpwhr->hdr.hInternet : 0);
3785 iar.dwError = res;
3787 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
3788 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3789 sizeof(INTERNET_ASYNC_RESULT));
3790 return res;
3793 /***********************************************************************
3794 * HttpEndRequestA (WININET.@)
3796 * Ends an HTTP request that was started by HttpSendRequestEx
3798 * RETURNS
3799 * TRUE if successful
3800 * FALSE on failure
3803 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
3804 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3806 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
3808 if (lpBuffersOut)
3810 SetLastError(ERROR_INVALID_PARAMETER);
3811 return FALSE;
3814 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
3817 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
3819 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
3820 http_request_t *lpwhr = (http_request_t*)work->hdr;
3822 TRACE("%p\n", lpwhr);
3824 HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
3827 /***********************************************************************
3828 * HttpEndRequestW (WININET.@)
3830 * Ends an HTTP request that was started by HttpSendRequestEx
3832 * RETURNS
3833 * TRUE if successful
3834 * FALSE on failure
3837 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
3838 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
3840 http_request_t *lpwhr;
3841 DWORD res;
3843 TRACE("-->\n");
3845 if (lpBuffersOut)
3847 SetLastError(ERROR_INVALID_PARAMETER);
3848 return FALSE;
3851 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3853 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3855 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3856 if (lpwhr)
3857 WININET_Release( &lpwhr->hdr );
3858 return FALSE;
3860 lpwhr->hdr.dwFlags |= dwFlags;
3862 if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3864 WORKREQUEST work;
3865 struct WORKREQ_HTTPENDREQUESTW *request;
3867 work.asyncproc = AsyncHttpEndRequestProc;
3868 work.hdr = WININET_AddRef( &lpwhr->hdr );
3870 request = &work.u.HttpEndRequestW;
3871 request->dwFlags = dwFlags;
3872 request->dwContext = dwContext;
3874 INTERNET_AsyncCall(&work);
3875 res = ERROR_IO_PENDING;
3877 else
3878 res = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
3880 WININET_Release( &lpwhr->hdr );
3881 TRACE("%u <--\n", res);
3882 if(res != ERROR_SUCCESS)
3883 SetLastError(res);
3884 return res == ERROR_SUCCESS;
3887 /***********************************************************************
3888 * HttpSendRequestExA (WININET.@)
3890 * Sends the specified request to the HTTP server and allows chunked
3891 * transfers.
3893 * RETURNS
3894 * Success: TRUE
3895 * Failure: FALSE, call GetLastError() for more information.
3897 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
3898 LPINTERNET_BUFFERSA lpBuffersIn,
3899 LPINTERNET_BUFFERSA lpBuffersOut,
3900 DWORD dwFlags, DWORD_PTR dwContext)
3902 INTERNET_BUFFERSW BuffersInW;
3903 BOOL rc = FALSE;
3904 DWORD headerlen;
3905 LPWSTR header = NULL;
3907 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3908 lpBuffersOut, dwFlags, dwContext);
3910 if (lpBuffersIn)
3912 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
3913 if (lpBuffersIn->lpcszHeader)
3915 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
3916 lpBuffersIn->dwHeadersLength,0,0);
3917 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
3918 if (!(BuffersInW.lpcszHeader = header))
3920 SetLastError(ERROR_OUTOFMEMORY);
3921 return FALSE;
3923 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
3924 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
3925 header, headerlen);
3927 else
3928 BuffersInW.lpcszHeader = NULL;
3929 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
3930 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
3931 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
3932 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
3933 BuffersInW.Next = NULL;
3936 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
3938 HeapFree(GetProcessHeap(),0,header);
3940 return rc;
3943 /***********************************************************************
3944 * HttpSendRequestExW (WININET.@)
3946 * Sends the specified request to the HTTP server and allows chunked
3947 * transfers
3949 * RETURNS
3950 * Success: TRUE
3951 * Failure: FALSE, call GetLastError() for more information.
3953 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
3954 LPINTERNET_BUFFERSW lpBuffersIn,
3955 LPINTERNET_BUFFERSW lpBuffersOut,
3956 DWORD dwFlags, DWORD_PTR dwContext)
3958 http_request_t *lpwhr;
3959 http_session_t *lpwhs;
3960 appinfo_t *hIC;
3961 DWORD res;
3963 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
3964 lpBuffersOut, dwFlags, dwContext);
3966 lpwhr = (http_request_t*) WININET_GetObject( hRequest );
3968 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
3970 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3971 goto lend;
3974 lpwhs = lpwhr->lpHttpSession;
3975 assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
3976 hIC = lpwhs->lpAppInfo;
3977 assert(hIC->hdr.htype == WH_HINIT);
3979 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3981 WORKREQUEST workRequest;
3982 struct WORKREQ_HTTPSENDREQUESTW *req;
3984 workRequest.asyncproc = AsyncHttpSendRequestProc;
3985 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
3986 req = &workRequest.u.HttpSendRequestW;
3987 if (lpBuffersIn)
3989 DWORD size = 0;
3991 if (lpBuffersIn->lpcszHeader)
3993 if (lpBuffersIn->dwHeadersLength == ~0u)
3994 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
3995 else
3996 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
3998 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
3999 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
4001 else req->lpszHeader = NULL;
4003 req->dwHeaderLength = size / sizeof(WCHAR);
4004 req->lpOptional = lpBuffersIn->lpvBuffer;
4005 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
4006 req->dwContentLength = lpBuffersIn->dwBufferTotal;
4008 else
4010 req->lpszHeader = NULL;
4011 req->dwHeaderLength = 0;
4012 req->lpOptional = NULL;
4013 req->dwOptionalLength = 0;
4014 req->dwContentLength = 0;
4017 req->bEndRequest = FALSE;
4019 INTERNET_AsyncCall(&workRequest);
4021 * This is from windows.
4023 res = ERROR_IO_PENDING;
4025 else
4027 if (lpBuffersIn)
4028 res = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4029 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
4030 lpBuffersIn->dwBufferTotal, FALSE);
4031 else
4032 res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
4035 lend:
4036 if ( lpwhr )
4037 WININET_Release( &lpwhr->hdr );
4039 TRACE("<---\n");
4040 SetLastError(res);
4041 return res == ERROR_SUCCESS;
4044 /***********************************************************************
4045 * HttpSendRequestW (WININET.@)
4047 * Sends the specified request to the HTTP server
4049 * RETURNS
4050 * TRUE on success
4051 * FALSE on failure
4054 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4055 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4057 http_request_t *lpwhr;
4058 http_session_t *lpwhs = NULL;
4059 appinfo_t *hIC = NULL;
4060 DWORD res = ERROR_SUCCESS;
4062 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4063 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4065 lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
4066 if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
4068 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4069 goto lend;
4072 lpwhs = lpwhr->lpHttpSession;
4073 if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION)
4075 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4076 goto lend;
4079 hIC = lpwhs->lpAppInfo;
4080 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
4082 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4083 goto lend;
4086 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4088 WORKREQUEST workRequest;
4089 struct WORKREQ_HTTPSENDREQUESTW *req;
4091 workRequest.asyncproc = AsyncHttpSendRequestProc;
4092 workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
4093 req = &workRequest.u.HttpSendRequestW;
4094 if (lpszHeaders)
4096 DWORD size;
4098 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4099 else size = dwHeaderLength * sizeof(WCHAR);
4101 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4102 memcpy(req->lpszHeader, lpszHeaders, size);
4104 else
4105 req->lpszHeader = 0;
4106 req->dwHeaderLength = dwHeaderLength;
4107 req->lpOptional = lpOptional;
4108 req->dwOptionalLength = dwOptionalLength;
4109 req->dwContentLength = dwOptionalLength;
4110 req->bEndRequest = TRUE;
4112 INTERNET_AsyncCall(&workRequest);
4114 * This is from windows.
4116 res = ERROR_IO_PENDING;
4118 else
4120 res = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
4121 dwHeaderLength, lpOptional, dwOptionalLength,
4122 dwOptionalLength, TRUE);
4124 lend:
4125 if( lpwhr )
4126 WININET_Release( &lpwhr->hdr );
4128 SetLastError(res);
4129 return res == ERROR_SUCCESS;
4132 /***********************************************************************
4133 * HttpSendRequestA (WININET.@)
4135 * Sends the specified request to the HTTP server
4137 * RETURNS
4138 * TRUE on success
4139 * FALSE on failure
4142 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4143 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4145 BOOL result;
4146 LPWSTR szHeaders=NULL;
4147 DWORD nLen=dwHeaderLength;
4148 if(lpszHeaders!=NULL)
4150 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4151 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4152 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4154 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4155 HeapFree(GetProcessHeap(),0,szHeaders);
4156 return result;
4159 /***********************************************************************
4160 * HTTPSESSION_Destroy (internal)
4162 * Deallocate session handle
4165 static void HTTPSESSION_Destroy(object_header_t *hdr)
4167 http_session_t *lpwhs = (http_session_t*) hdr;
4169 TRACE("%p\n", lpwhs);
4171 WININET_Release(&lpwhs->lpAppInfo->hdr);
4173 HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
4174 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
4175 HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
4176 HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
4177 HeapFree(GetProcessHeap(), 0, lpwhs);
4180 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4182 switch(option) {
4183 case INTERNET_OPTION_HANDLE_TYPE:
4184 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4186 if (*size < sizeof(ULONG))
4187 return ERROR_INSUFFICIENT_BUFFER;
4189 *size = sizeof(DWORD);
4190 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4191 return ERROR_SUCCESS;
4194 return INET_QueryOption(hdr, option, buffer, size, unicode);
4197 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4199 http_session_t *ses = (http_session_t*)hdr;
4201 switch(option) {
4202 case INTERNET_OPTION_USERNAME:
4204 HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
4205 if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4206 return ERROR_SUCCESS;
4208 case INTERNET_OPTION_PASSWORD:
4210 HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
4211 if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4212 return ERROR_SUCCESS;
4214 default: break;
4217 return ERROR_INTERNET_INVALID_OPTION;
4220 static const object_vtbl_t HTTPSESSIONVtbl = {
4221 HTTPSESSION_Destroy,
4222 NULL,
4223 HTTPSESSION_QueryOption,
4224 HTTPSESSION_SetOption,
4225 NULL,
4226 NULL,
4227 NULL,
4228 NULL,
4229 NULL
4233 /***********************************************************************
4234 * HTTP_Connect (internal)
4236 * Create http session handle
4238 * RETURNS
4239 * HINTERNET a session handle on success
4240 * NULL on failure
4243 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4244 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
4245 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4246 DWORD dwInternalFlags, HINTERNET *ret)
4248 http_session_t *lpwhs = NULL;
4249 HINTERNET handle = NULL;
4250 DWORD res = ERROR_SUCCESS;
4252 TRACE("-->\n");
4254 if (!lpszServerName || !lpszServerName[0])
4255 return ERROR_INVALID_PARAMETER;
4257 assert( hIC->hdr.htype == WH_HINIT );
4259 lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
4260 if (!lpwhs)
4261 return ERROR_OUTOFMEMORY;
4264 * According to my tests. The name is not resolved until a request is sent
4267 lpwhs->hdr.htype = WH_HHTTPSESSION;
4268 lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
4269 lpwhs->hdr.dwFlags = dwFlags;
4270 lpwhs->hdr.dwContext = dwContext;
4271 lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
4272 lpwhs->hdr.refs = 1;
4273 lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
4275 WININET_AddRef( &hIC->hdr );
4276 lpwhs->lpAppInfo = hIC;
4277 list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
4279 handle = WININET_AllocHandle( &lpwhs->hdr );
4280 if (NULL == handle)
4282 ERR("Failed to alloc handle\n");
4283 res = ERROR_OUTOFMEMORY;
4284 goto lerror;
4287 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
4288 if(hIC->lpszProxyBypass)
4289 FIXME("Proxy bypass is ignored.\n");
4291 lpwhs->lpszServerName = heap_strdupW(lpszServerName);
4292 lpwhs->lpszHostName = heap_strdupW(lpszServerName);
4293 if (lpszUserName && lpszUserName[0])
4294 lpwhs->lpszUserName = heap_strdupW(lpszUserName);
4295 if (lpszPassword && lpszPassword[0])
4296 lpwhs->lpszPassword = heap_strdupW(lpszPassword);
4297 lpwhs->nServerPort = nServerPort;
4298 lpwhs->nHostPort = nServerPort;
4300 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4301 if (!(lpwhs->hdr.dwInternalFlags & INET_OPENURL))
4303 INTERNET_SendCallback(&hIC->hdr, dwContext,
4304 INTERNET_STATUS_HANDLE_CREATED, &handle,
4305 sizeof(handle));
4308 lerror:
4309 if( lpwhs )
4310 WININET_Release( &lpwhs->hdr );
4313 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4314 * windows
4317 TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
4319 if(res == ERROR_SUCCESS)
4320 *ret = handle;
4321 return res;
4325 /***********************************************************************
4326 * HTTP_OpenConnection (internal)
4328 * Connect to a web server
4330 * RETURNS
4332 * TRUE on success
4333 * FALSE on failure
4335 static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
4337 http_session_t *lpwhs;
4338 appinfo_t *hIC = NULL;
4339 char szaddr[INET6_ADDRSTRLEN];
4340 const void *addr;
4341 DWORD res = ERROR_SUCCESS;
4343 TRACE("-->\n");
4346 if (lpwhr->hdr.htype != WH_HHTTPREQ)
4348 res = ERROR_INVALID_PARAMETER;
4349 goto lend;
4352 if (NETCON_connected(&lpwhr->netConnection))
4353 goto lend;
4354 if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
4356 lpwhs = lpwhr->lpHttpSession;
4358 hIC = lpwhs->lpAppInfo;
4359 switch (lpwhs->socketAddress.ss_family)
4361 case AF_INET:
4362 addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
4363 break;
4364 case AF_INET6:
4365 addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
4366 break;
4367 default:
4368 WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
4369 return ERROR_INTERNET_NAME_NOT_RESOLVED;
4371 inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4372 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4373 INTERNET_STATUS_CONNECTING_TO_SERVER,
4374 szaddr,
4375 strlen(szaddr)+1);
4377 res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
4378 if (res != ERROR_SUCCESS)
4380 WARN("Socket creation failed: %u\n", res);
4381 goto lend;
4384 res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
4385 lpwhs->sa_len);
4386 if(res != ERROR_SUCCESS)
4387 goto lend;
4389 INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
4390 INTERNET_STATUS_CONNECTED_TO_SERVER,
4391 szaddr, strlen(szaddr)+1);
4393 if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
4395 /* Note: we differ from Microsoft's WinINet here. they seem to have
4396 * a bug that causes no status callbacks to be sent when starting
4397 * a tunnel to a proxy server using the CONNECT verb. i believe our
4398 * behaviour to be more correct and to not cause any incompatibilities
4399 * because using a secure connection through a proxy server is a rare
4400 * case that would be hard for anyone to depend on */
4401 if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS) {
4402 HTTPREQ_CloseConnection(&lpwhr->hdr);
4403 goto lend;
4406 res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
4407 if(res != ERROR_SUCCESS)
4409 WARN("Couldn't connect securely to host\n");
4411 if((lpwhr->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4412 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4413 || res == ERROR_INTERNET_INVALID_CA
4414 || res == ERROR_INTERNET_SEC_CERT_NO_REV
4415 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
4416 || res == ERROR_INTERNET_SEC_CERT_REVOKED
4417 || res == ERROR_INTERNET_SEC_INVALID_CERT
4418 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
4419 res = ERROR_INTERNET_SEC_CERT_ERRORS;
4421 HTTPREQ_CloseConnection(&lpwhr->hdr);
4422 goto lend;
4427 lend:
4428 lpwhr->read_pos = lpwhr->read_size = 0;
4429 lpwhr->read_chunked = FALSE;
4431 TRACE("%d <--\n", res);
4432 return res;
4436 /***********************************************************************
4437 * HTTP_clear_response_headers (internal)
4439 * clear out any old response headers
4441 static void HTTP_clear_response_headers( http_request_t *lpwhr )
4443 DWORD i;
4445 for( i=0; i<lpwhr->nCustHeaders; i++)
4447 if( !lpwhr->pCustHeaders[i].lpszField )
4448 continue;
4449 if( !lpwhr->pCustHeaders[i].lpszValue )
4450 continue;
4451 if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
4452 continue;
4453 HTTP_DeleteCustomHeader( lpwhr, i );
4454 i--;
4458 /***********************************************************************
4459 * HTTP_GetResponseHeaders (internal)
4461 * Read server response
4463 * RETURNS
4465 * TRUE on success
4466 * FALSE on error
4468 static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
4470 INT cbreaks = 0;
4471 WCHAR buffer[MAX_REPLY_LEN];
4472 DWORD buflen = MAX_REPLY_LEN;
4473 BOOL bSuccess = FALSE;
4474 INT rc = 0;
4475 char bufferA[MAX_REPLY_LEN];
4476 LPWSTR status_code = NULL, status_text = NULL;
4477 DWORD cchMaxRawHeaders = 1024;
4478 LPWSTR lpszRawHeaders = NULL;
4479 LPWSTR temp;
4480 DWORD cchRawHeaders = 0;
4481 BOOL codeHundred = FALSE;
4483 TRACE("-->\n");
4485 if (!NETCON_connected(&lpwhr->netConnection))
4486 goto lend;
4488 do {
4489 static const WCHAR szHundred[] = {'1','0','0',0};
4491 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
4493 buflen = MAX_REPLY_LEN;
4494 if (!read_line(lpwhr, bufferA, &buflen))
4495 goto lend;
4497 /* clear old response headers (eg. from a redirect response) */
4498 if (clear) {
4499 HTTP_clear_response_headers( lpwhr );
4500 clear = FALSE;
4503 rc += buflen;
4504 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4505 /* check is this a status code line? */
4506 if (!strncmpW(buffer, g_szHttp1_0, 4))
4508 /* split the version from the status code */
4509 status_code = strchrW( buffer, ' ' );
4510 if( !status_code )
4511 goto lend;
4512 *status_code++=0;
4514 /* split the status code from the status text */
4515 status_text = strchrW( status_code, ' ' );
4516 if( !status_text )
4517 goto lend;
4518 *status_text++=0;
4520 TRACE("version [%s] status code [%s] status text [%s]\n",
4521 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
4523 codeHundred = (!strcmpW(status_code, szHundred));
4525 else if (!codeHundred)
4527 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
4529 HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
4530 HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
4532 lpwhr->lpszVersion = heap_strdupW(g_szHttp1_0);
4533 lpwhr->lpszStatusText = heap_strdupW(szOK);
4535 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4536 lpwhr->lpszRawHeaders = heap_strdupW(szDefaultHeader);
4538 bSuccess = TRUE;
4539 goto lend;
4541 } while (codeHundred);
4543 /* Add status code */
4544 HTTP_ProcessHeader(lpwhr, szStatus, status_code,
4545 HTTP_ADDHDR_FLAG_REPLACE);
4547 HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
4548 HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
4550 lpwhr->lpszVersion = heap_strdupW(buffer);
4551 lpwhr->lpszStatusText = heap_strdupW(status_text);
4553 /* Restore the spaces */
4554 *(status_code-1) = ' ';
4555 *(status_text-1) = ' ';
4557 /* regenerate raw headers */
4558 lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4559 if (!lpszRawHeaders) goto lend;
4561 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4562 cchMaxRawHeaders *= 2;
4563 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4564 if (temp == NULL) goto lend;
4565 lpszRawHeaders = temp;
4566 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4567 cchRawHeaders += (buflen-1);
4568 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4569 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4570 lpszRawHeaders[cchRawHeaders] = '\0';
4572 /* Parse each response line */
4575 buflen = MAX_REPLY_LEN;
4576 if (read_line(lpwhr, bufferA, &buflen))
4578 LPWSTR * pFieldAndValue;
4580 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
4582 if (!bufferA[0]) break;
4583 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
4585 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
4586 if (pFieldAndValue)
4588 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
4589 cchMaxRawHeaders *= 2;
4590 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
4591 if (temp == NULL) goto lend;
4592 lpszRawHeaders = temp;
4593 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
4594 cchRawHeaders += (buflen-1);
4595 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
4596 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
4597 lpszRawHeaders[cchRawHeaders] = '\0';
4599 HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
4600 HTTP_ADDREQ_FLAG_ADD );
4602 HTTP_FreeTokens(pFieldAndValue);
4605 else
4607 cbreaks++;
4608 if (cbreaks >= 2)
4609 break;
4611 }while(1);
4613 /* make sure the response header is terminated with an empty line. Some apps really
4614 truly care about that empty line being there for some reason. Just add it to the
4615 header. */
4616 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
4618 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
4619 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
4620 if (temp == NULL) goto lend;
4621 lpszRawHeaders = temp;
4624 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
4626 HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
4627 lpwhr->lpszRawHeaders = lpszRawHeaders;
4628 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
4629 bSuccess = TRUE;
4631 lend:
4633 TRACE("<--\n");
4634 if (bSuccess)
4635 return rc;
4636 else
4638 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
4639 return 0;
4643 /***********************************************************************
4644 * HTTP_InterpretHttpHeader (internal)
4646 * Parse server response
4648 * RETURNS
4650 * Pointer to array of field, value, NULL on success.
4651 * NULL on error.
4653 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
4655 LPWSTR * pTokenPair;
4656 LPWSTR pszColon;
4657 INT len;
4659 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
4661 pszColon = strchrW(buffer, ':');
4662 /* must have two tokens */
4663 if (!pszColon)
4665 HTTP_FreeTokens(pTokenPair);
4666 if (buffer[0])
4667 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
4668 return NULL;
4671 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
4672 if (!pTokenPair[0])
4674 HTTP_FreeTokens(pTokenPair);
4675 return NULL;
4677 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
4678 pTokenPair[0][pszColon - buffer] = '\0';
4680 /* skip colon */
4681 pszColon++;
4682 len = strlenW(pszColon);
4683 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4684 if (!pTokenPair[1])
4686 HTTP_FreeTokens(pTokenPair);
4687 return NULL;
4689 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
4691 strip_spaces(pTokenPair[0]);
4692 strip_spaces(pTokenPair[1]);
4694 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
4695 return pTokenPair;
4698 /***********************************************************************
4699 * HTTP_ProcessHeader (internal)
4701 * Stuff header into header tables according to <dwModifier>
4705 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4707 static DWORD HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
4709 LPHTTPHEADERW lphttpHdr = NULL;
4710 INT index = -1;
4711 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
4712 DWORD res = ERROR_HTTP_INVALID_HEADER;
4714 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
4716 /* REPLACE wins out over ADD */
4717 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4718 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
4720 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
4721 index = -1;
4722 else
4723 index = HTTP_GetCustomHeaderIndex(lpwhr, field, 0, request_only);
4725 if (index >= 0)
4727 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
4728 return ERROR_HTTP_INVALID_HEADER;
4729 lphttpHdr = &lpwhr->pCustHeaders[index];
4731 else if (value)
4733 HTTPHEADERW hdr;
4735 hdr.lpszField = (LPWSTR)field;
4736 hdr.lpszValue = (LPWSTR)value;
4737 hdr.wFlags = hdr.wCount = 0;
4739 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4740 hdr.wFlags |= HDR_ISREQUEST;
4742 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4744 /* no value to delete */
4745 else return ERROR_SUCCESS;
4747 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4748 lphttpHdr->wFlags |= HDR_ISREQUEST;
4749 else
4750 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
4752 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
4754 HTTP_DeleteCustomHeader( lpwhr, index );
4756 if (value)
4758 HTTPHEADERW hdr;
4760 hdr.lpszField = (LPWSTR)field;
4761 hdr.lpszValue = (LPWSTR)value;
4762 hdr.wFlags = hdr.wCount = 0;
4764 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
4765 hdr.wFlags |= HDR_ISREQUEST;
4767 return HTTP_InsertCustomHeader(lpwhr, &hdr);
4770 return ERROR_SUCCESS;
4772 else if (dwModifier & COALESCEFLAGS)
4774 LPWSTR lpsztmp;
4775 WCHAR ch = 0;
4776 INT len = 0;
4777 INT origlen = strlenW(lphttpHdr->lpszValue);
4778 INT valuelen = strlenW(value);
4780 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
4782 ch = ',';
4783 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4785 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
4787 ch = ';';
4788 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
4791 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
4793 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
4794 if (lpsztmp)
4796 lphttpHdr->lpszValue = lpsztmp;
4797 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
4798 if (ch > 0)
4800 lphttpHdr->lpszValue[origlen] = ch;
4801 origlen++;
4802 lphttpHdr->lpszValue[origlen] = ' ';
4803 origlen++;
4806 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
4807 lphttpHdr->lpszValue[len] = '\0';
4808 res = ERROR_SUCCESS;
4810 else
4812 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
4813 res = ERROR_OUTOFMEMORY;
4816 TRACE("<-- %d\n", res);
4817 return res;
4821 /***********************************************************************
4822 * HTTP_FinishedReading (internal)
4824 * Called when all content from server has been read by client.
4827 static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
4829 BOOL keepalive = HTTP_KeepAlive(lpwhr);
4831 TRACE("\n");
4834 if (!keepalive)
4836 HTTPREQ_CloseConnection(&lpwhr->hdr);
4839 /* FIXME: store data in the URL cache here */
4841 return TRUE;
4845 /***********************************************************************
4846 * HTTP_GetCustomHeaderIndex (internal)
4848 * Return index of custom header from header array
4851 static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
4852 int requested_index, BOOL request_only)
4854 DWORD index;
4856 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
4858 for (index = 0; index < lpwhr->nCustHeaders; index++)
4860 if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
4861 continue;
4863 if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4864 continue;
4866 if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
4867 continue;
4869 if (requested_index == 0)
4870 break;
4871 requested_index --;
4874 if (index >= lpwhr->nCustHeaders)
4875 index = -1;
4877 TRACE("Return: %d\n", index);
4878 return index;
4882 /***********************************************************************
4883 * HTTP_InsertCustomHeader (internal)
4885 * Insert header into array
4888 static DWORD HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
4890 INT count;
4891 LPHTTPHEADERW lph = NULL;
4893 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
4894 count = lpwhr->nCustHeaders + 1;
4895 if (count > 1)
4896 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERW) * count);
4897 else
4898 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
4900 if (!lph)
4901 return ERROR_OUTOFMEMORY;
4903 lpwhr->pCustHeaders = lph;
4904 lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
4905 lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
4906 lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
4907 lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
4908 lpwhr->nCustHeaders++;
4910 return ERROR_SUCCESS;
4914 /***********************************************************************
4915 * HTTP_DeleteCustomHeader (internal)
4917 * Delete header from array
4918 * If this function is called, the indexs may change.
4920 static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
4922 if( lpwhr->nCustHeaders <= 0 )
4923 return FALSE;
4924 if( index >= lpwhr->nCustHeaders )
4925 return FALSE;
4926 lpwhr->nCustHeaders--;
4928 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
4929 HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
4931 memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
4932 (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
4933 memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
4935 return TRUE;
4939 /***********************************************************************
4940 * HTTP_VerifyValidHeader (internal)
4942 * Verify the given header is not invalid for the given http request
4945 static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
4947 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
4948 if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
4949 return ERROR_HTTP_INVALID_HEADER;
4951 return ERROR_SUCCESS;
4954 /***********************************************************************
4955 * IsHostInProxyBypassList (@)
4957 * Undocumented
4960 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
4962 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
4963 return FALSE;