wininet: Ensure that we have data to report before sending INTERNET_STATUS_REQUEST_CO...
[wine.git] / dlls / wininet / http.c
blob9444f088c66e2a27d739deaedefab871bcf2e00b
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 #include "winternl.h"
60 #define NO_SHLWAPI_STREAM
61 #define NO_SHLWAPI_REG
62 #define NO_SHLWAPI_STRFCNS
63 #define NO_SHLWAPI_GDI
64 #include "shlwapi.h"
65 #include "sspi.h"
66 #include "wincrypt.h"
68 #include "internet.h"
69 #include "wine/debug.h"
70 #include "wine/exception.h"
71 #include "wine/unicode.h"
73 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
75 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
76 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
77 static const WCHAR szOK[] = {'O','K',0};
78 static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
79 static const WCHAR hostW[] = { 'H','o','s','t',0 };
80 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
81 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
82 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
83 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
84 static const WCHAR szGET[] = { 'G','E','T', 0 };
85 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
86 static const WCHAR szCrLf[] = {'\r','\n', 0};
88 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
89 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
90 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
91 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
92 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
93 static const WCHAR szAge[] = { 'A','g','e',0 };
94 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
95 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
96 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
97 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
98 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
99 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
100 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
101 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
102 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
103 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
104 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
105 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 };
106 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
107 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
108 static const WCHAR szDate[] = { 'D','a','t','e',0 };
109 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
110 static const WCHAR szETag[] = { 'E','T','a','g',0 };
111 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
112 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
113 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
114 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
115 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
116 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
117 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
118 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
119 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
120 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
121 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
122 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
123 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
124 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
125 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
126 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
127 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
128 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
129 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
130 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
131 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
132 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 };
133 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
134 static const WCHAR szURI[] = { 'U','R','I',0 };
135 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
136 static const WCHAR szVary[] = { 'V','a','r','y',0 };
137 static const WCHAR szVia[] = { 'V','i','a',0 };
138 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
139 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
141 #define MAXHOSTNAME 100
142 #define MAX_FIELD_VALUE_LEN 256
143 #define MAX_FIELD_LEN 256
145 #define HTTP_REFERER szReferer
146 #define HTTP_ACCEPT szAccept
147 #define HTTP_USERAGENT szUser_Agent
149 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
150 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
151 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
152 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
153 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
154 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
155 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
157 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
159 struct HttpAuthInfo
161 LPWSTR scheme;
162 CredHandle cred;
163 CtxtHandle ctx;
164 TimeStamp exp;
165 ULONG attr;
166 ULONG max_token;
167 void *auth_data;
168 unsigned int auth_data_len;
169 BOOL finished; /* finished authenticating */
173 struct gzip_stream_t {
174 #ifdef HAVE_ZLIB
175 z_stream zstream;
176 #endif
177 BYTE buf[8192];
178 DWORD buf_size;
179 DWORD buf_pos;
180 BOOL end_of_data;
183 typedef struct _basicAuthorizationData
185 struct list entry;
187 LPWSTR host;
188 LPWSTR realm;
189 LPSTR authorization;
190 UINT authorizationLen;
191 } basicAuthorizationData;
193 typedef struct _authorizationData
195 struct list entry;
197 LPWSTR host;
198 LPWSTR scheme;
199 LPWSTR domain;
200 UINT domain_len;
201 LPWSTR user;
202 UINT user_len;
203 LPWSTR password;
204 UINT password_len;
205 } authorizationData;
207 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
208 static struct list authorizationCache = LIST_INIT(authorizationCache);
210 static CRITICAL_SECTION authcache_cs;
211 static CRITICAL_SECTION_DEBUG critsect_debug =
213 0, 0, &authcache_cs,
214 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
215 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
217 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
219 static DWORD HTTP_OpenConnection(http_request_t *req);
220 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
221 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
222 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
223 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
224 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
225 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
226 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
227 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
228 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
229 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
230 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
231 static void HTTP_DrainContent(http_request_t *req);
232 static BOOL HTTP_FinishedReading(http_request_t *req);
234 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
236 int HeaderIndex = 0;
237 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
238 if (HeaderIndex == -1)
239 return NULL;
240 else
241 return &req->custHeaders[HeaderIndex];
244 #ifdef HAVE_ZLIB
246 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
248 return HeapAlloc(GetProcessHeap(), 0, items*size);
251 static void wininet_zfree(voidpf opaque, voidpf address)
253 HeapFree(GetProcessHeap(), 0, address);
256 static void init_gzip_stream(http_request_t *req)
258 gzip_stream_t *gzip_stream;
259 int index, zres;
261 gzip_stream = heap_alloc_zero(sizeof(gzip_stream_t));
262 gzip_stream->zstream.zalloc = wininet_zalloc;
263 gzip_stream->zstream.zfree = wininet_zfree;
265 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
266 if(zres != Z_OK) {
267 ERR("inflateInit failed: %d\n", zres);
268 HeapFree(GetProcessHeap(), 0, gzip_stream);
269 return;
272 req->gzip_stream = gzip_stream;
274 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
275 if(index != -1)
276 HTTP_DeleteCustomHeader(req, index);
279 static void release_gzip_stream(http_request_t *req)
281 if(!req->gzip_stream->end_of_data)
282 inflateEnd(&req->gzip_stream->zstream);
283 heap_free(req->gzip_stream);
284 req->gzip_stream = NULL;
287 #else
289 static void init_gzip_stream(http_request_t *req)
291 ERR("gzip stream not supported, missing zlib.\n");
294 static void release_gzip_stream(http_request_t *req)
298 #endif
300 /* set the request content length based on the headers */
301 static DWORD set_content_length( http_request_t *request )
303 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
304 WCHAR encoding[20];
305 DWORD size;
307 size = sizeof(request->contentLength);
308 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
309 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
310 request->contentLength = ~0u;
312 size = sizeof(encoding);
313 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
314 !strcmpiW(encoding, szChunked))
316 request->contentLength = ~0u;
317 request->read_chunked = TRUE;
320 if(request->decoding) {
321 int encoding_idx;
323 static const WCHAR gzipW[] = {'g','z','i','p',0};
325 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
326 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
327 init_gzip_stream(request);
330 return request->contentLength;
333 /***********************************************************************
334 * HTTP_Tokenize (internal)
336 * Tokenize a string, allocating memory for the tokens.
338 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
340 LPWSTR * token_array;
341 int tokens = 0;
342 int i;
343 LPCWSTR next_token;
345 if (string)
347 /* empty string has no tokens */
348 if (*string)
349 tokens++;
350 /* count tokens */
351 for (i = 0; string[i]; i++)
353 if (!strncmpW(string+i, token_string, strlenW(token_string)))
355 DWORD j;
356 tokens++;
357 /* we want to skip over separators, but not the null terminator */
358 for (j = 0; j < strlenW(token_string) - 1; j++)
359 if (!string[i+j])
360 break;
361 i += j;
366 /* add 1 for terminating NULL */
367 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
368 token_array[tokens] = NULL;
369 if (!tokens)
370 return token_array;
371 for (i = 0; i < tokens; i++)
373 int len;
374 next_token = strstrW(string, token_string);
375 if (!next_token) next_token = string+strlenW(string);
376 len = next_token - string;
377 token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
378 memcpy(token_array[i], string, len*sizeof(WCHAR));
379 token_array[i][len] = '\0';
380 string = next_token+strlenW(token_string);
382 return token_array;
385 /***********************************************************************
386 * HTTP_FreeTokens (internal)
388 * Frees memory returned from HTTP_Tokenize.
390 static void HTTP_FreeTokens(LPWSTR * token_array)
392 int i;
393 for (i = 0; token_array[i]; i++)
394 HeapFree(GetProcessHeap(), 0, token_array[i]);
395 HeapFree(GetProcessHeap(), 0, token_array);
398 static void HTTP_FixURL(http_request_t *request)
400 static const WCHAR szSlash[] = { '/',0 };
401 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
403 /* If we don't have a path we set it to root */
404 if (NULL == request->path)
405 request->path = heap_strdupW(szSlash);
406 else /* remove \r and \n*/
408 int nLen = strlenW(request->path);
409 while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n')))
411 nLen--;
412 request->path[nLen]='\0';
414 /* Replace '\' with '/' */
415 while (nLen>0) {
416 nLen--;
417 if (request->path[nLen] == '\\') request->path[nLen]='/';
421 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
422 request->path, strlenW(request->path), szHttp, strlenW(szHttp) )
423 && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */
425 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
426 (strlenW(request->path) + 2)*sizeof(WCHAR));
427 *fixurl = '/';
428 strcpyW(fixurl + 1, request->path);
429 HeapFree( GetProcessHeap(), 0, request->path );
430 request->path = fixurl;
434 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *request, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
436 LPWSTR requestString;
437 DWORD len, n;
438 LPCWSTR *req;
439 UINT i;
440 LPWSTR p;
442 static const WCHAR szSpace[] = { ' ',0 };
443 static const WCHAR szColon[] = { ':',' ',0 };
444 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
446 /* allocate space for an array of all the string pointers to be added */
447 len = (request->nCustHeaders)*4 + 10;
448 req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
450 /* add the verb, path and HTTP version string */
451 n = 0;
452 req[n++] = verb;
453 req[n++] = szSpace;
454 req[n++] = path;
455 req[n++] = szSpace;
456 req[n++] = version;
458 /* Append custom request headers */
459 for (i = 0; i < request->nCustHeaders; i++)
461 if (request->custHeaders[i].wFlags & HDR_ISREQUEST)
463 req[n++] = szCrLf;
464 req[n++] = request->custHeaders[i].lpszField;
465 req[n++] = szColon;
466 req[n++] = request->custHeaders[i].lpszValue;
468 TRACE("Adding custom header %s (%s)\n",
469 debugstr_w(request->custHeaders[i].lpszField),
470 debugstr_w(request->custHeaders[i].lpszValue));
474 if( n >= len )
475 ERR("oops. buffer overrun\n");
477 req[n] = NULL;
478 requestString = HTTP_build_req( req, 4 );
479 HeapFree( GetProcessHeap(), 0, req );
482 * Set (header) termination string for request
483 * Make sure there's exactly two new lines at the end of the request
485 p = &requestString[strlenW(requestString)-1];
486 while ( (*p == '\n') || (*p == '\r') )
487 p--;
488 strcpyW( p+1, sztwocrlf );
490 return requestString;
493 static void HTTP_ProcessCookies( http_request_t *request )
495 int HeaderIndex;
496 int numCookies = 0;
497 LPHTTPHEADERW setCookieHeader;
499 while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, szSet_Cookie, numCookies, FALSE)) != -1)
501 setCookieHeader = &request->custHeaders[HeaderIndex];
503 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
505 int len;
506 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
507 LPWSTR buf_url;
508 LPHTTPHEADERW Host;
510 Host = HTTP_GetHeader(request, hostW);
511 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(request->path);
512 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
513 sprintfW(buf_url, szFmt, Host->lpszValue, request->path);
514 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
516 HeapFree(GetProcessHeap(), 0, buf_url);
518 numCookies++;
522 static void strip_spaces(LPWSTR start)
524 LPWSTR str = start;
525 LPWSTR end;
527 while (*str == ' ' && *str != '\0')
528 str++;
530 if (str != start)
531 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
533 end = start + strlenW(start) - 1;
534 while (end >= start && *end == ' ')
536 *end = '\0';
537 end--;
541 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
543 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
544 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
545 BOOL is_basic;
546 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
547 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
548 if (is_basic && pszRealm)
550 LPCWSTR token;
551 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
552 LPCWSTR realm;
553 ptr++;
554 *pszRealm=NULL;
555 token = strchrW(ptr,'=');
556 if (!token)
557 return TRUE;
558 realm = ptr;
559 while (*realm == ' ' && *realm != '\0')
560 realm++;
561 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
562 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
564 token++;
565 while (*token == ' ' && *token != '\0')
566 token++;
567 if (*token == '\0')
568 return TRUE;
569 *pszRealm = heap_strdupW(token);
570 strip_spaces(*pszRealm);
574 return is_basic;
577 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
579 if (!authinfo) return;
581 if (SecIsValidHandle(&authinfo->ctx))
582 DeleteSecurityContext(&authinfo->ctx);
583 if (SecIsValidHandle(&authinfo->cred))
584 FreeCredentialsHandle(&authinfo->cred);
586 HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
587 HeapFree(GetProcessHeap(), 0, authinfo->scheme);
588 HeapFree(GetProcessHeap(), 0, authinfo);
591 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
593 basicAuthorizationData *ad;
594 UINT rc = 0;
596 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
598 EnterCriticalSection(&authcache_cs);
599 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
601 if (!strcmpiW(host,ad->host) && !strcmpW(realm,ad->realm))
603 TRACE("Authorization found in cache\n");
604 *auth_data = HeapAlloc(GetProcessHeap(),0,ad->authorizationLen);
605 memcpy(*auth_data,ad->authorization,ad->authorizationLen);
606 rc = ad->authorizationLen;
607 break;
610 LeaveCriticalSection(&authcache_cs);
611 return rc;
614 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
616 struct list *cursor;
617 basicAuthorizationData* ad = NULL;
619 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
621 EnterCriticalSection(&authcache_cs);
622 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
624 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
625 if (!strcmpiW(host,check->host) && !strcmpW(realm,check->realm))
627 ad = check;
628 break;
632 if (ad)
634 TRACE("Found match in cache, replacing\n");
635 HeapFree(GetProcessHeap(),0,ad->authorization);
636 ad->authorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
637 memcpy(ad->authorization, auth_data, auth_data_len);
638 ad->authorizationLen = auth_data_len;
640 else
642 ad = HeapAlloc(GetProcessHeap(),0,sizeof(basicAuthorizationData));
643 ad->host = heap_strdupW(host);
644 ad->host = heap_strdupW(realm);
645 ad->authorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
646 memcpy(ad->authorization, auth_data, auth_data_len);
647 ad->authorizationLen = auth_data_len;
648 list_add_head(&basicAuthorizationCache,&ad->entry);
649 TRACE("authorization cached\n");
651 LeaveCriticalSection(&authcache_cs);
654 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
655 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
657 authorizationData *ad;
659 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
661 EnterCriticalSection(&authcache_cs);
662 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
663 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
664 TRACE("Authorization found in cache\n");
666 nt_auth_identity->User = heap_strdupW(ad->user);
667 nt_auth_identity->Password = heap_strdupW(ad->password);
668 nt_auth_identity->Domain = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*ad->domain_len);
669 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
670 (!nt_auth_identity->Domain && ad->domain_len)) {
671 HeapFree(GetProcessHeap(), 0, nt_auth_identity->User);
672 HeapFree(GetProcessHeap(), 0, nt_auth_identity->Password);
673 HeapFree(GetProcessHeap(), 0, nt_auth_identity->Domain);
674 break;
677 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
678 nt_auth_identity->UserLength = ad->user_len;
679 nt_auth_identity->PasswordLength = ad->password_len;
680 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
681 nt_auth_identity->DomainLength = ad->domain_len;
682 LeaveCriticalSection(&authcache_cs);
683 return TRUE;
686 LeaveCriticalSection(&authcache_cs);
688 return FALSE;
691 static void cache_authorization(LPWSTR host, LPWSTR scheme,
692 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
694 authorizationData *ad;
695 BOOL found = FALSE;
697 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
699 EnterCriticalSection(&authcache_cs);
700 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
701 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
702 found = TRUE;
703 break;
706 if(found) {
707 HeapFree(GetProcessHeap(), 0, ad->user);
708 HeapFree(GetProcessHeap(), 0, ad->password);
709 HeapFree(GetProcessHeap(), 0, ad->domain);
710 } else {
711 ad = HeapAlloc(GetProcessHeap(), 0, sizeof(authorizationData));
712 if(!ad) {
713 LeaveCriticalSection(&authcache_cs);
714 return;
717 ad->host = heap_strdupW(host);
718 ad->scheme = heap_strdupW(scheme);
719 list_add_head(&authorizationCache, &ad->entry);
722 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
723 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
724 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
725 ad->user_len = nt_auth_identity->UserLength;
726 ad->password_len = nt_auth_identity->PasswordLength;
727 ad->domain_len = nt_auth_identity->DomainLength;
729 if(!ad->host || !ad->scheme || !ad->user || !ad->password
730 || (nt_auth_identity->Domain && !ad->domain)) {
731 HeapFree(GetProcessHeap(), 0, ad->host);
732 HeapFree(GetProcessHeap(), 0, ad->scheme);
733 HeapFree(GetProcessHeap(), 0, ad->user);
734 HeapFree(GetProcessHeap(), 0, ad->password);
735 HeapFree(GetProcessHeap(), 0, ad->domain);
736 list_remove(&ad->entry);
737 HeapFree(GetProcessHeap(), 0, ad);
740 LeaveCriticalSection(&authcache_cs);
743 static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue,
744 struct HttpAuthInfo **ppAuthInfo,
745 LPWSTR domain_and_username, LPWSTR password,
746 LPWSTR host )
748 SECURITY_STATUS sec_status;
749 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
750 BOOL first = FALSE;
751 LPWSTR szRealm = NULL;
753 TRACE("%s\n", debugstr_w(pszAuthValue));
755 if (!pAuthInfo)
757 TimeStamp exp;
759 first = TRUE;
760 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
761 if (!pAuthInfo)
762 return FALSE;
764 SecInvalidateHandle(&pAuthInfo->cred);
765 SecInvalidateHandle(&pAuthInfo->ctx);
766 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
767 pAuthInfo->attr = 0;
768 pAuthInfo->auth_data = NULL;
769 pAuthInfo->auth_data_len = 0;
770 pAuthInfo->finished = FALSE;
772 if (is_basic_auth_value(pszAuthValue,NULL))
774 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
775 pAuthInfo->scheme = heap_strdupW(szBasic);
776 if (!pAuthInfo->scheme)
778 HeapFree(GetProcessHeap(), 0, pAuthInfo);
779 return FALSE;
782 else
784 PVOID pAuthData;
785 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
787 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
788 if (!pAuthInfo->scheme)
790 HeapFree(GetProcessHeap(), 0, pAuthInfo);
791 return FALSE;
794 if (domain_and_username)
796 WCHAR *user = strchrW(domain_and_username, '\\');
797 WCHAR *domain = domain_and_username;
799 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
801 pAuthData = &nt_auth_identity;
803 if (user) user++;
804 else
806 user = domain_and_username;
807 domain = NULL;
810 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
811 nt_auth_identity.User = user;
812 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
813 nt_auth_identity.Domain = domain;
814 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
815 nt_auth_identity.Password = password;
816 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
818 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
820 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
821 pAuthData = &nt_auth_identity;
822 else
823 /* use default credentials */
824 pAuthData = NULL;
826 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
827 SECPKG_CRED_OUTBOUND, NULL,
828 pAuthData, NULL,
829 NULL, &pAuthInfo->cred,
830 &exp);
832 if(pAuthData && !domain_and_username) {
833 HeapFree(GetProcessHeap(), 0, nt_auth_identity.User);
834 HeapFree(GetProcessHeap(), 0, nt_auth_identity.Domain);
835 HeapFree(GetProcessHeap(), 0, nt_auth_identity.Password);
838 if (sec_status == SEC_E_OK)
840 PSecPkgInfoW sec_pkg_info;
841 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
842 if (sec_status == SEC_E_OK)
844 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
845 FreeContextBuffer(sec_pkg_info);
848 if (sec_status != SEC_E_OK)
850 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
851 debugstr_w(pAuthInfo->scheme), sec_status);
852 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
853 HeapFree(GetProcessHeap(), 0, pAuthInfo);
854 return FALSE;
857 *ppAuthInfo = pAuthInfo;
859 else if (pAuthInfo->finished)
860 return FALSE;
862 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
863 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
865 ERR("authentication scheme changed from %s to %s\n",
866 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
867 return FALSE;
870 if (is_basic_auth_value(pszAuthValue,&szRealm))
872 int userlen;
873 int passlen;
874 char *auth_data = NULL;
875 UINT auth_data_len = 0;
877 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
879 if (!domain_and_username)
881 if (host && szRealm)
882 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
883 if (auth_data_len == 0)
885 HeapFree(GetProcessHeap(),0,szRealm);
886 return FALSE;
889 else
891 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
892 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
894 /* length includes a nul terminator, which will be re-used for the ':' */
895 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
896 if (!auth_data)
898 HeapFree(GetProcessHeap(),0,szRealm);
899 return FALSE;
902 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
903 auth_data[userlen] = ':';
904 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
905 auth_data_len = userlen + 1 + passlen;
906 if (host && szRealm)
907 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
910 pAuthInfo->auth_data = auth_data;
911 pAuthInfo->auth_data_len = auth_data_len;
912 pAuthInfo->finished = TRUE;
913 HeapFree(GetProcessHeap(),0,szRealm);
915 return TRUE;
917 else
919 LPCWSTR pszAuthData;
920 SecBufferDesc out_desc, in_desc;
921 SecBuffer out, in;
922 unsigned char *buffer;
923 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
924 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
926 in.BufferType = SECBUFFER_TOKEN;
927 in.cbBuffer = 0;
928 in.pvBuffer = NULL;
930 in_desc.ulVersion = 0;
931 in_desc.cBuffers = 1;
932 in_desc.pBuffers = &in;
934 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
935 if (*pszAuthData == ' ')
937 pszAuthData++;
938 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
939 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
940 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
943 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
945 out.BufferType = SECBUFFER_TOKEN;
946 out.cbBuffer = pAuthInfo->max_token;
947 out.pvBuffer = buffer;
949 out_desc.ulVersion = 0;
950 out_desc.cBuffers = 1;
951 out_desc.pBuffers = &out;
953 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
954 first ? NULL : &pAuthInfo->ctx,
955 first ? request->session->serverName : NULL,
956 context_req, 0, SECURITY_NETWORK_DREP,
957 in.pvBuffer ? &in_desc : NULL,
958 0, &pAuthInfo->ctx, &out_desc,
959 &pAuthInfo->attr, &pAuthInfo->exp);
960 if (sec_status == SEC_E_OK)
962 pAuthInfo->finished = TRUE;
963 pAuthInfo->auth_data = out.pvBuffer;
964 pAuthInfo->auth_data_len = out.cbBuffer;
965 TRACE("sending last auth packet\n");
967 else if (sec_status == SEC_I_CONTINUE_NEEDED)
969 pAuthInfo->auth_data = out.pvBuffer;
970 pAuthInfo->auth_data_len = out.cbBuffer;
971 TRACE("sending next auth packet\n");
973 else
975 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
976 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
977 destroy_authinfo(pAuthInfo);
978 *ppAuthInfo = NULL;
979 return FALSE;
983 return TRUE;
986 /***********************************************************************
987 * HTTP_HttpAddRequestHeadersW (internal)
989 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *request,
990 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
992 LPWSTR lpszStart;
993 LPWSTR lpszEnd;
994 LPWSTR buffer;
995 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
997 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
999 if( dwHeaderLength == ~0U )
1000 len = strlenW(lpszHeader);
1001 else
1002 len = dwHeaderLength;
1003 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
1004 lstrcpynW( buffer, lpszHeader, len + 1);
1006 lpszStart = buffer;
1010 LPWSTR * pFieldAndValue;
1012 lpszEnd = lpszStart;
1014 while (*lpszEnd != '\0')
1016 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1017 break;
1018 lpszEnd++;
1021 if (*lpszStart == '\0')
1022 break;
1024 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1026 *lpszEnd = '\0';
1027 lpszEnd++; /* Jump over newline */
1029 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1030 if (*lpszStart == '\0')
1032 /* Skip 0-length headers */
1033 lpszStart = lpszEnd;
1034 res = ERROR_SUCCESS;
1035 continue;
1037 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1038 if (pFieldAndValue)
1040 res = HTTP_VerifyValidHeader(request, pFieldAndValue[0]);
1041 if (res == ERROR_SUCCESS)
1042 res = HTTP_ProcessHeader(request, pFieldAndValue[0],
1043 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1044 HTTP_FreeTokens(pFieldAndValue);
1047 lpszStart = lpszEnd;
1048 } while (res == ERROR_SUCCESS);
1050 HeapFree(GetProcessHeap(), 0, buffer);
1052 return res;
1055 /***********************************************************************
1056 * HttpAddRequestHeadersW (WININET.@)
1058 * Adds one or more HTTP header to the request handler
1060 * NOTE
1061 * On Windows if dwHeaderLength includes the trailing '\0', then
1062 * HttpAddRequestHeadersW() adds it too. However this results in an
1063 * invalid Http header which is rejected by some servers so we probably
1064 * don't need to match Windows on that point.
1066 * RETURNS
1067 * TRUE on success
1068 * FALSE on failure
1071 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1072 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1074 http_request_t *request;
1075 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1077 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1079 if (!lpszHeader)
1080 return TRUE;
1082 request = (http_request_t*) get_handle_object( hHttpRequest );
1083 if (request && request->hdr.htype == WH_HHTTPREQ)
1084 res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier );
1085 if( request )
1086 WININET_Release( &request->hdr );
1088 if(res != ERROR_SUCCESS)
1089 SetLastError(res);
1090 return res == ERROR_SUCCESS;
1093 /***********************************************************************
1094 * HttpAddRequestHeadersA (WININET.@)
1096 * Adds one or more HTTP header to the request handler
1098 * RETURNS
1099 * TRUE on success
1100 * FALSE on failure
1103 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1104 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1106 DWORD len;
1107 LPWSTR hdr;
1108 BOOL r;
1110 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1112 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1113 hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
1114 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1115 if( dwHeaderLength != ~0U )
1116 dwHeaderLength = len;
1118 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1120 HeapFree( GetProcessHeap(), 0, hdr );
1122 return r;
1125 /***********************************************************************
1126 * HttpOpenRequestA (WININET.@)
1128 * Open a HTTP request handle
1130 * RETURNS
1131 * HINTERNET a HTTP request handle on success
1132 * NULL on failure
1135 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1136 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1137 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1138 DWORD dwFlags, DWORD_PTR dwContext)
1140 LPWSTR szVerb = NULL, szObjectName = NULL;
1141 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1142 INT acceptTypesCount;
1143 HINTERNET rc = FALSE;
1144 LPCSTR *types;
1146 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1147 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1148 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1149 dwFlags, dwContext);
1151 if (lpszVerb)
1153 szVerb = heap_strdupAtoW(lpszVerb);
1154 if ( !szVerb )
1155 goto end;
1158 if (lpszObjectName)
1160 szObjectName = heap_strdupAtoW(lpszObjectName);
1161 if ( !szObjectName )
1162 goto end;
1165 if (lpszVersion)
1167 szVersion = heap_strdupAtoW(lpszVersion);
1168 if ( !szVersion )
1169 goto end;
1172 if (lpszReferrer)
1174 szReferrer = heap_strdupAtoW(lpszReferrer);
1175 if ( !szReferrer )
1176 goto end;
1179 if (lpszAcceptTypes)
1181 acceptTypesCount = 0;
1182 types = lpszAcceptTypes;
1183 while (*types)
1185 __TRY
1187 /* find out how many there are */
1188 if (*types && **types)
1190 TRACE("accept type: %s\n", debugstr_a(*types));
1191 acceptTypesCount++;
1194 __EXCEPT_PAGE_FAULT
1196 WARN("invalid accept type pointer\n");
1198 __ENDTRY;
1199 types++;
1201 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1202 if (!szAcceptTypes) goto end;
1204 acceptTypesCount = 0;
1205 types = lpszAcceptTypes;
1206 while (*types)
1208 __TRY
1210 if (*types && **types)
1211 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1213 __EXCEPT_PAGE_FAULT
1215 /* ignore invalid pointer */
1217 __ENDTRY;
1218 types++;
1220 szAcceptTypes[acceptTypesCount] = NULL;
1223 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1224 szVersion, szReferrer,
1225 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1227 end:
1228 if (szAcceptTypes)
1230 acceptTypesCount = 0;
1231 while (szAcceptTypes[acceptTypesCount])
1233 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1234 acceptTypesCount++;
1236 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1238 HeapFree(GetProcessHeap(), 0, szReferrer);
1239 HeapFree(GetProcessHeap(), 0, szVersion);
1240 HeapFree(GetProcessHeap(), 0, szObjectName);
1241 HeapFree(GetProcessHeap(), 0, szVerb);
1243 return rc;
1246 /***********************************************************************
1247 * HTTP_EncodeBase64
1249 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1251 UINT n = 0, x;
1252 static const CHAR HTTP_Base64Enc[] =
1253 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1255 while( len > 0 )
1257 /* first 6 bits, all from bin[0] */
1258 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1259 x = (bin[0] & 3) << 4;
1261 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1262 if( len == 1 )
1264 base64[n++] = HTTP_Base64Enc[x];
1265 base64[n++] = '=';
1266 base64[n++] = '=';
1267 break;
1269 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1270 x = ( bin[1] & 0x0f ) << 2;
1272 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1273 if( len == 2 )
1275 base64[n++] = HTTP_Base64Enc[x];
1276 base64[n++] = '=';
1277 break;
1279 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1281 /* last 6 bits, all from bin [2] */
1282 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1283 bin += 3;
1284 len -= 3;
1286 base64[n] = 0;
1287 return n;
1290 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1291 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1292 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1293 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1294 static const signed char HTTP_Base64Dec[256] =
1296 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1297 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1298 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1299 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1300 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1301 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1302 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1303 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1304 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1305 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1306 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1307 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1308 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1309 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1310 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1311 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1312 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1313 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1314 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1315 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1316 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1317 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1318 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1319 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1320 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1321 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1323 #undef CH
1325 /***********************************************************************
1326 * HTTP_DecodeBase64
1328 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1330 unsigned int n = 0;
1332 while(*base64)
1334 signed char in[4];
1336 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1337 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1338 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1339 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1341 WARN("invalid base64: %s\n", debugstr_w(base64));
1342 return 0;
1344 if (bin)
1345 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1346 n++;
1348 if ((base64[2] == '=') && (base64[3] == '='))
1349 break;
1350 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1351 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1353 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1354 return 0;
1356 if (bin)
1357 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1358 n++;
1360 if (base64[3] == '=')
1361 break;
1362 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1363 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1365 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1366 return 0;
1368 if (bin)
1369 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1370 n++;
1372 base64 += 4;
1375 return n;
1378 /***********************************************************************
1379 * HTTP_InsertAuthorization
1381 * Insert or delete the authorization field in the request header.
1383 static BOOL HTTP_InsertAuthorization( http_request_t *request, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1385 if (pAuthInfo)
1387 static const WCHAR wszSpace[] = {' ',0};
1388 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1389 unsigned int len;
1390 WCHAR *authorization = NULL;
1392 if (pAuthInfo->auth_data_len)
1394 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1395 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1396 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1397 if (!authorization)
1398 return FALSE;
1400 strcpyW(authorization, pAuthInfo->scheme);
1401 strcatW(authorization, wszSpace);
1402 HTTP_EncodeBase64(pAuthInfo->auth_data,
1403 pAuthInfo->auth_data_len,
1404 authorization+strlenW(authorization));
1406 /* clear the data as it isn't valid now that it has been sent to the
1407 * server, unless it's Basic authentication which doesn't do
1408 * connection tracking */
1409 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1411 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1412 pAuthInfo->auth_data = NULL;
1413 pAuthInfo->auth_data_len = 0;
1417 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1419 HTTP_ProcessHeader(request, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1421 HeapFree(GetProcessHeap(), 0, authorization);
1423 return TRUE;
1426 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1428 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1429 DWORD size;
1431 size = sizeof(new_location);
1432 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1434 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1435 strcpyW( url, new_location );
1437 else
1439 static const WCHAR slash[] = { '/',0 };
1440 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
1441 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
1442 http_session_t *session = req->session;
1444 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1445 size += strlenW( session->hostName ) + strlenW( req->path );
1447 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1449 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1450 sprintfW( url, formatSSL, session->hostName, session->hostPort );
1451 else
1452 sprintfW( url, format, session->hostName, session->hostPort );
1453 if (req->path[0] != '/') strcatW( url, slash );
1454 strcatW( url, req->path );
1456 TRACE("url=%s\n", debugstr_w(url));
1457 return url;
1460 /***********************************************************************
1461 * HTTP_DealWithProxy
1463 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_request_t *request)
1465 WCHAR buf[MAXHOSTNAME];
1466 WCHAR protoProxy[MAXHOSTNAME + 15];
1467 DWORD protoProxyLen = sizeof(protoProxy) / sizeof(protoProxy[0]);
1468 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1469 static WCHAR szNul[] = { 0 };
1470 URL_COMPONENTSW UrlComponents;
1471 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1472 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1473 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1475 memset( &UrlComponents, 0, sizeof UrlComponents );
1476 UrlComponents.dwStructSize = sizeof UrlComponents;
1477 UrlComponents.lpszHostName = buf;
1478 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1480 if (!INTERNET_FindProxyForProtocol(hIC->proxy, protoHttp, protoProxy, &protoProxyLen))
1481 return FALSE;
1482 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1483 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1484 sprintfW(proxy, szFormat, protoProxy);
1485 else
1486 strcpyW(proxy, protoProxy);
1487 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1488 return FALSE;
1489 if( UrlComponents.dwHostNameLength == 0 )
1490 return FALSE;
1492 if( !request->path )
1493 request->path = szNul;
1495 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1496 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1498 HeapFree(GetProcessHeap(), 0, session->serverName);
1499 session->serverName = heap_strdupW(UrlComponents.lpszHostName);
1500 session->serverPort = UrlComponents.nPort;
1502 TRACE("proxy server=%s port=%d\n", debugstr_w(session->serverName), session->serverPort);
1503 return TRUE;
1506 #ifndef INET6_ADDRSTRLEN
1507 #define INET6_ADDRSTRLEN 46
1508 #endif
1510 static DWORD HTTP_ResolveName(http_request_t *request)
1512 char szaddr[INET6_ADDRSTRLEN];
1513 http_session_t *session = request->session;
1514 const void *addr;
1516 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1517 INTERNET_STATUS_RESOLVING_NAME,
1518 session->serverName,
1519 (strlenW(session->serverName)+1) * sizeof(WCHAR));
1521 session->sa_len = sizeof(session->socketAddress);
1522 if (!GetAddress(session->serverName, session->serverPort,
1523 (struct sockaddr *)&session->socketAddress, &session->sa_len))
1524 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1526 switch (session->socketAddress.ss_family)
1528 case AF_INET:
1529 addr = &((struct sockaddr_in *)&session->socketAddress)->sin_addr;
1530 break;
1531 case AF_INET6:
1532 addr = &((struct sockaddr_in6 *)&session->socketAddress)->sin6_addr;
1533 break;
1534 default:
1535 WARN("unsupported family %d\n", session->socketAddress.ss_family);
1536 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1538 inet_ntop(session->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1539 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1540 INTERNET_STATUS_NAME_RESOLVED,
1541 szaddr, strlen(szaddr)+1);
1543 TRACE("resolved %s to %s\n", debugstr_w(session->serverName), szaddr);
1544 return ERROR_SUCCESS;
1547 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1549 static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
1550 static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
1551 static const WCHAR slash[] = { '/',0 };
1552 LPHTTPHEADERW host_header;
1553 LPCWSTR scheme;
1555 host_header = HTTP_GetHeader(req, hostW);
1556 if(!host_header)
1557 return FALSE;
1559 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1560 scheme = https;
1561 else
1562 scheme = http;
1563 strcpyW(buf, scheme);
1564 strcatW(buf, host_header->lpszValue);
1565 if (req->path[0] != '/')
1566 strcatW(buf, slash);
1567 strcatW(buf, req->path);
1568 return TRUE;
1572 /***********************************************************************
1573 * HTTPREQ_Destroy (internal)
1575 * Deallocate request handle
1578 static void HTTPREQ_Destroy(object_header_t *hdr)
1580 http_request_t *request = (http_request_t*) hdr;
1581 DWORD i;
1583 TRACE("\n");
1585 if(request->hCacheFile) {
1586 WCHAR url[INTERNET_MAX_URL_LENGTH];
1588 CloseHandle(request->hCacheFile);
1590 if(HTTP_GetRequestURL(request, url)) {
1591 DWORD headersLen;
1593 headersLen = request->rawHeaders ? strlenW(request->rawHeaders) : 0;
1594 CommitUrlCacheEntryW(url, request->cacheFile, request->expires,
1595 request->last_modified, NORMAL_CACHE_ENTRY,
1596 request->rawHeaders, headersLen, NULL, 0);
1600 HeapFree(GetProcessHeap(), 0, request->cacheFile);
1602 DeleteCriticalSection( &request->read_section );
1603 WININET_Release(&request->session->hdr);
1605 destroy_authinfo(request->authInfo);
1606 destroy_authinfo(request->proxyAuthInfo);
1608 HeapFree(GetProcessHeap(), 0, request->path);
1609 HeapFree(GetProcessHeap(), 0, request->verb);
1610 HeapFree(GetProcessHeap(), 0, request->rawHeaders);
1611 HeapFree(GetProcessHeap(), 0, request->version);
1612 HeapFree(GetProcessHeap(), 0, request->statusText);
1614 for (i = 0; i < request->nCustHeaders; i++)
1616 HeapFree(GetProcessHeap(), 0, request->custHeaders[i].lpszField);
1617 HeapFree(GetProcessHeap(), 0, request->custHeaders[i].lpszValue);
1620 if(request->gzip_stream)
1621 release_gzip_stream(request);
1623 HeapFree(GetProcessHeap(), 0, request->custHeaders);
1626 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1628 http_request_t *request = (http_request_t*) hdr;
1630 TRACE("%p\n",request);
1632 if (!NETCON_connected(&request->netConnection))
1633 return;
1635 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1636 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1638 NETCON_close(&request->netConnection);
1640 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1641 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1644 static BOOL HTTP_KeepAlive(http_request_t *request)
1646 WCHAR szVersion[10];
1647 WCHAR szConnectionResponse[20];
1648 DWORD dwBufferSize = sizeof(szVersion);
1649 BOOL keepalive = FALSE;
1651 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1652 * the connection is keep-alive by default */
1653 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1654 && !strcmpiW(szVersion, g_szHttp1_1))
1656 keepalive = TRUE;
1659 dwBufferSize = sizeof(szConnectionResponse);
1660 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1661 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1663 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1666 return keepalive;
1669 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1671 http_request_t *req = (http_request_t*)hdr;
1673 switch(option) {
1674 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1676 http_session_t *session = req->session;
1677 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1679 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1681 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1682 return ERROR_INSUFFICIENT_BUFFER;
1683 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1684 /* FIXME: can't get a SOCKET from our connection since we don't use
1685 * winsock
1687 info->Socket = 0;
1688 /* FIXME: get source port from req->netConnection */
1689 info->SourcePort = 0;
1690 info->DestPort = session->hostPort;
1691 info->Flags = 0;
1692 if (HTTP_KeepAlive(req))
1693 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1694 if (session->appInfo->proxy && session->appInfo->proxy[0] != 0)
1695 info->Flags |= IDSI_FLAG_PROXY;
1696 if (req->netConnection.useSSL)
1697 info->Flags |= IDSI_FLAG_SECURE;
1699 return ERROR_SUCCESS;
1702 case INTERNET_OPTION_SECURITY_FLAGS:
1704 DWORD flags;
1705 int bits;
1707 if (*size < sizeof(ULONG))
1708 return ERROR_INSUFFICIENT_BUFFER;
1710 *size = sizeof(DWORD);
1711 flags = 0;
1712 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1713 flags |= SECURITY_FLAG_SECURE;
1714 flags |= req->netConnection.security_flags;
1715 bits = NETCON_GetCipherStrength(&req->netConnection);
1716 if (bits >= 128)
1717 flags |= SECURITY_FLAG_STRENGTH_STRONG;
1718 else if (bits >= 56)
1719 flags |= SECURITY_FLAG_STRENGTH_MEDIUM;
1720 else
1721 flags |= SECURITY_FLAG_STRENGTH_WEAK;
1722 *(DWORD *)buffer = flags;
1723 return ERROR_SUCCESS;
1726 case INTERNET_OPTION_HANDLE_TYPE:
1727 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1729 if (*size < sizeof(ULONG))
1730 return ERROR_INSUFFICIENT_BUFFER;
1732 *size = sizeof(DWORD);
1733 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1734 return ERROR_SUCCESS;
1736 case INTERNET_OPTION_URL: {
1737 WCHAR url[INTERNET_MAX_URL_LENGTH];
1738 HTTPHEADERW *host;
1739 DWORD len;
1740 WCHAR *pch;
1742 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1744 TRACE("INTERNET_OPTION_URL\n");
1746 host = HTTP_GetHeader(req, hostW);
1747 strcpyW(url, httpW);
1748 strcatW(url, host->lpszValue);
1749 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1750 *pch = 0;
1751 strcatW(url, req->path);
1753 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1755 if(unicode) {
1756 len = (strlenW(url)+1) * sizeof(WCHAR);
1757 if(*size < len)
1758 return ERROR_INSUFFICIENT_BUFFER;
1760 *size = len;
1761 strcpyW(buffer, url);
1762 return ERROR_SUCCESS;
1763 }else {
1764 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1765 if(len > *size)
1766 return ERROR_INSUFFICIENT_BUFFER;
1768 *size = len;
1769 return ERROR_SUCCESS;
1773 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1774 INTERNET_CACHE_ENTRY_INFOW *info;
1775 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1776 WCHAR url[INTERNET_MAX_URL_LENGTH];
1777 DWORD nbytes, error;
1778 BOOL ret;
1780 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1782 if (*size < sizeof(*ts))
1784 *size = sizeof(*ts);
1785 return ERROR_INSUFFICIENT_BUFFER;
1787 nbytes = 0;
1788 HTTP_GetRequestURL(req, url);
1789 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1790 error = GetLastError();
1791 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1793 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1794 return ERROR_OUTOFMEMORY;
1796 GetUrlCacheEntryInfoW(url, info, &nbytes);
1798 ts->ftExpires = info->ExpireTime;
1799 ts->ftLastModified = info->LastModifiedTime;
1801 HeapFree(GetProcessHeap(), 0, info);
1802 *size = sizeof(*ts);
1803 return ERROR_SUCCESS;
1805 return error;
1808 case INTERNET_OPTION_DATAFILE_NAME: {
1809 DWORD req_size;
1811 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1813 if(!req->cacheFile) {
1814 *size = 0;
1815 return ERROR_INTERNET_ITEM_NOT_FOUND;
1818 if(unicode) {
1819 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
1820 if(*size < req_size)
1821 return ERROR_INSUFFICIENT_BUFFER;
1823 *size = req_size;
1824 memcpy(buffer, req->cacheFile, *size);
1825 return ERROR_SUCCESS;
1826 }else {
1827 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
1828 if (req_size > *size)
1829 return ERROR_INSUFFICIENT_BUFFER;
1831 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
1832 -1, buffer, *size, NULL, NULL);
1833 return ERROR_SUCCESS;
1837 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1838 PCCERT_CONTEXT context;
1840 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
1841 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
1842 return ERROR_INSUFFICIENT_BUFFER;
1845 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1846 if(context) {
1847 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
1848 DWORD len;
1850 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1851 info->ftExpiry = context->pCertInfo->NotAfter;
1852 info->ftStart = context->pCertInfo->NotBefore;
1853 len = CertNameToStrA(context->dwCertEncodingType,
1854 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1855 info->lpszSubjectInfo = LocalAlloc(0, len);
1856 if(info->lpszSubjectInfo)
1857 CertNameToStrA(context->dwCertEncodingType,
1858 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1859 info->lpszSubjectInfo, len);
1860 len = CertNameToStrA(context->dwCertEncodingType,
1861 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1862 info->lpszIssuerInfo = LocalAlloc(0, len);
1863 if(info->lpszIssuerInfo)
1864 CertNameToStrA(context->dwCertEncodingType,
1865 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1866 info->lpszIssuerInfo, len);
1867 info->dwKeySize = NETCON_GetCipherStrength(&req->netConnection);
1868 CertFreeCertificateContext(context);
1869 return ERROR_SUCCESS;
1874 return INET_QueryOption(hdr, option, buffer, size, unicode);
1877 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1879 http_request_t *req = (http_request_t*)hdr;
1881 switch(option) {
1882 case INTERNET_OPTION_SECURITY_FLAGS:
1884 DWORD flags;
1886 if (!buffer || size != sizeof(DWORD))
1887 return ERROR_INVALID_PARAMETER;
1888 flags = *(DWORD *)buffer;
1889 TRACE("%08x\n", flags);
1890 req->netConnection.security_flags = flags;
1891 return ERROR_SUCCESS;
1893 case INTERNET_OPTION_SEND_TIMEOUT:
1894 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1895 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1897 if (size != sizeof(DWORD))
1898 return ERROR_INVALID_PARAMETER;
1900 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
1901 *(DWORD*)buffer);
1903 case INTERNET_OPTION_USERNAME:
1904 HeapFree(GetProcessHeap(), 0, req->session->userName);
1905 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1906 return ERROR_SUCCESS;
1908 case INTERNET_OPTION_PASSWORD:
1909 HeapFree(GetProcessHeap(), 0, req->session->password);
1910 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
1911 return ERROR_SUCCESS;
1912 case INTERNET_OPTION_HTTP_DECODING:
1913 if(size != sizeof(BOOL))
1914 return ERROR_INVALID_PARAMETER;
1915 req->decoding = *(BOOL*)buffer;
1916 return ERROR_SUCCESS;
1919 return ERROR_INTERNET_INVALID_OPTION;
1922 /* read some more data into the read buffer (the read section must be held) */
1923 static DWORD read_more_data( http_request_t *req, int maxlen )
1925 DWORD res;
1926 int len;
1928 if (req->read_pos)
1930 /* move existing data to the start of the buffer */
1931 if(req->read_size)
1932 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
1933 req->read_pos = 0;
1936 if (maxlen == -1) maxlen = sizeof(req->read_buf);
1938 res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
1939 maxlen - req->read_size, 0, &len );
1940 if(res == ERROR_SUCCESS)
1941 req->read_size += len;
1943 return res;
1946 /* remove some amount of data from the read buffer (the read section must be held) */
1947 static void remove_data( http_request_t *req, int count )
1949 if (!(req->read_size -= count)) req->read_pos = 0;
1950 else req->read_pos += count;
1953 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
1955 int count, bytes_read, pos = 0;
1956 DWORD res;
1958 EnterCriticalSection( &req->read_section );
1959 for (;;)
1961 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
1963 if (eol)
1965 count = eol - (req->read_buf + req->read_pos);
1966 bytes_read = count + 1;
1968 else count = bytes_read = req->read_size;
1970 count = min( count, *len - pos );
1971 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
1972 pos += count;
1973 remove_data( req, bytes_read );
1974 if (eol) break;
1976 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
1978 *len = 0;
1979 TRACE( "returning empty string\n" );
1980 LeaveCriticalSection( &req->read_section );
1981 INTERNET_SetLastError(res);
1982 return FALSE;
1985 LeaveCriticalSection( &req->read_section );
1987 if (pos < *len)
1989 if (pos && buffer[pos - 1] == '\r') pos--;
1990 *len = pos + 1;
1992 buffer[*len - 1] = 0;
1993 TRACE( "returning %s\n", debugstr_a(buffer));
1994 return TRUE;
1997 /* discard data contents until we reach end of line (the read section must be held) */
1998 static DWORD discard_eol( http_request_t *req )
2000 DWORD res;
2004 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2005 if (eol)
2007 remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
2008 break;
2010 req->read_pos = req->read_size = 0; /* discard everything */
2011 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
2012 } while (req->read_size);
2013 return ERROR_SUCCESS;
2016 /* read the size of the next chunk (the read section must be held) */
2017 static DWORD start_next_chunk( http_request_t *req )
2019 DWORD chunk_size = 0, res;
2021 if (!req->contentLength) return ERROR_SUCCESS;
2022 if (req->contentLength == req->contentRead)
2024 /* read terminator for the previous chunk */
2025 if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
2026 req->contentLength = ~0u;
2027 req->contentRead = 0;
2029 for (;;)
2031 while (req->read_size)
2033 char ch = req->read_buf[req->read_pos];
2034 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2035 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2036 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2037 else if (ch == ';' || ch == '\r' || ch == '\n')
2039 TRACE( "reading %u byte chunk\n", chunk_size );
2040 req->contentLength = chunk_size;
2041 req->contentRead = 0;
2042 return discard_eol( req );
2044 remove_data( req, 1 );
2046 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
2047 if (!req->read_size)
2049 req->contentLength = req->contentRead = 0;
2050 return ERROR_SUCCESS;
2055 /* check if we have reached the end of the data to read (the read section must be held) */
2056 static BOOL end_of_read_data( http_request_t *req )
2058 if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2059 if (req->read_chunked) return (req->contentLength == 0);
2060 if (req->contentLength == ~0u) return FALSE;
2061 return (req->contentLength == req->contentRead);
2064 /* fetch some more data into the read buffer (the read section must be held) */
2065 static DWORD refill_read_buffer( http_request_t *req )
2067 int len = sizeof(req->read_buf);
2068 DWORD res;
2070 if (req->read_chunked && (req->contentLength == ~0u || req->contentLength == req->contentRead))
2072 if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
2075 if (req->contentLength != ~0u) len = min( len, req->contentLength - req->contentRead );
2076 if (len <= req->read_size) return ERROR_SUCCESS;
2078 if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
2079 if (!req->read_size) req->contentLength = req->contentRead = 0;
2080 return ERROR_SUCCESS;
2083 static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
2085 DWORD ret = ERROR_SUCCESS;
2086 int read = 0;
2088 #ifdef HAVE_ZLIB
2089 z_stream *zstream = &req->gzip_stream->zstream;
2090 DWORD buf_avail;
2091 int zres;
2093 while(read < size && !req->gzip_stream->end_of_data) {
2094 if(!req->read_size) {
2095 if((!sync && read) || (ret = refill_read_buffer(req)) != ERROR_SUCCESS)
2096 break;
2099 if(req->contentRead == req->contentLength)
2100 break;
2102 buf_avail = req->contentLength == ~0 ? req->read_size : min(req->read_size, req->contentLength-req->contentRead);
2104 zstream->next_in = req->read_buf+req->read_pos;
2105 zstream->avail_in = buf_avail;
2106 zstream->next_out = buf+read;
2107 zstream->avail_out = size-read;
2108 zres = inflate(zstream, Z_FULL_FLUSH);
2109 read = size - zstream->avail_out;
2110 req->contentRead += buf_avail-zstream->avail_in;
2111 remove_data(req, buf_avail-zstream->avail_in);
2112 if(zres == Z_STREAM_END) {
2113 TRACE("end of data\n");
2114 req->gzip_stream->end_of_data = TRUE;
2115 inflateEnd(&req->gzip_stream->zstream);
2116 }else if(zres != Z_OK) {
2117 WARN("inflate failed %d\n", zres);
2118 if(!read)
2119 ret = ERROR_INTERNET_DECODING_FAILED;
2120 break;
2123 #endif
2125 *read_ret = read;
2126 return ret;
2129 static DWORD refill_gzip_buffer(http_request_t *req)
2131 DWORD res;
2132 int len;
2134 if(req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
2135 return ERROR_SUCCESS;
2137 if(req->gzip_stream->buf_pos) {
2138 if(req->gzip_stream->buf_size)
2139 memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
2140 req->gzip_stream->buf_pos = 0;
2143 res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
2144 sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
2145 if(res == ERROR_SUCCESS)
2146 req->gzip_stream->buf_size += len;
2148 return res;
2151 static DWORD refill_buffer( http_request_t *req )
2153 return req->gzip_stream ? refill_gzip_buffer(req) : refill_read_buffer(req);
2156 /* return the size of data available to be read immediately (the read section must be held) */
2157 static DWORD get_avail_data( http_request_t *req )
2159 if (req->gzip_stream)
2160 return req->gzip_stream->buf_size;
2161 if (req->read_chunked && (req->contentLength == ~0u || req->contentLength == req->contentRead))
2162 return 0;
2163 return min( req->read_size, req->contentLength - req->contentRead );
2166 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2168 INTERNET_ASYNC_RESULT iar;
2169 DWORD res;
2171 TRACE("%p\n", req);
2173 EnterCriticalSection( &req->read_section );
2174 if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
2175 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2176 iar.dwError = first_notif ? 0 : get_avail_data(req);
2177 }else {
2178 iar.dwResult = 0;
2179 iar.dwError = res;
2181 LeaveCriticalSection( &req->read_section );
2183 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2184 sizeof(INTERNET_ASYNC_RESULT));
2187 /* read data from the http connection (the read section must be held) */
2188 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2190 BOOL finished_reading = FALSE;
2191 int len, bytes_read = 0;
2192 DWORD ret = ERROR_SUCCESS;
2194 EnterCriticalSection( &req->read_section );
2196 if (req->read_chunked && (req->contentLength == ~0u || req->contentLength == req->contentRead))
2198 if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
2201 if(req->gzip_stream) {
2202 if(req->gzip_stream->buf_size) {
2203 bytes_read = min(req->gzip_stream->buf_size, size);
2204 memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
2205 req->gzip_stream->buf_pos += bytes_read;
2206 req->gzip_stream->buf_size -= bytes_read;
2207 }else if(!req->read_size && !req->gzip_stream->end_of_data) {
2208 refill_buffer(req);
2211 if(size > bytes_read) {
2212 ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
2213 if(ret == ERROR_SUCCESS)
2214 bytes_read += len;
2217 finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
2218 }else {
2219 if (req->contentLength != ~0u) size = min( size, req->contentLength - req->contentRead );
2221 if (req->read_size) {
2222 bytes_read = min( req->read_size, size );
2223 memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
2224 remove_data( req, bytes_read );
2227 if (size > bytes_read && (!bytes_read || sync)) {
2228 if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
2229 sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2230 bytes_read += len;
2231 /* always return success, even if the network layer returns an error */
2234 finished_reading = !bytes_read && req->contentRead == req->contentLength;
2235 req->contentRead += bytes_read;
2237 done:
2238 *read = bytes_read;
2240 TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->contentRead, req->contentLength );
2241 LeaveCriticalSection( &req->read_section );
2243 if(ret == ERROR_SUCCESS && req->cacheFile) {
2244 BOOL res;
2245 DWORD dwBytesWritten;
2247 res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
2248 if(!res)
2249 WARN("WriteFile failed: %u\n", GetLastError());
2252 if(finished_reading)
2253 HTTP_FinishedReading(req);
2255 return ret;
2259 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2261 http_request_t *req = (http_request_t*)hdr;
2262 DWORD res;
2264 EnterCriticalSection( &req->read_section );
2265 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2266 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2268 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2269 if(res == ERROR_SUCCESS)
2270 res = hdr->dwError;
2271 LeaveCriticalSection( &req->read_section );
2273 return res;
2276 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2278 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2279 http_request_t *req = (http_request_t*)workRequest->hdr;
2280 INTERNET_ASYNC_RESULT iar;
2281 DWORD res;
2283 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2285 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2286 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2288 iar.dwResult = res == ERROR_SUCCESS;
2289 iar.dwError = res;
2291 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2292 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2293 sizeof(INTERNET_ASYNC_RESULT));
2296 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2297 DWORD flags, DWORD_PTR context)
2299 http_request_t *req = (http_request_t*)hdr;
2300 DWORD res, size, read, error = ERROR_SUCCESS;
2302 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2303 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2305 if (buffers->dwStructSize != sizeof(*buffers))
2306 return ERROR_INVALID_PARAMETER;
2308 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2310 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2312 WORKREQUEST workRequest;
2314 if (TryEnterCriticalSection( &req->read_section ))
2316 if (get_avail_data(req))
2318 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2319 &buffers->dwBufferLength, FALSE);
2320 size = buffers->dwBufferLength;
2321 LeaveCriticalSection( &req->read_section );
2322 goto done;
2324 LeaveCriticalSection( &req->read_section );
2327 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2328 workRequest.hdr = WININET_AddRef(&req->hdr);
2329 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2331 INTERNET_AsyncCall(&workRequest);
2333 return ERROR_IO_PENDING;
2336 read = 0;
2337 size = buffers->dwBufferLength;
2339 EnterCriticalSection( &req->read_section );
2340 if(hdr->dwError == ERROR_SUCCESS)
2341 hdr->dwError = INTERNET_HANDLE_IN_USE;
2342 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2343 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2345 while(1) {
2346 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2347 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2348 if(res == ERROR_SUCCESS)
2349 read += buffers->dwBufferLength;
2350 else
2351 break;
2353 if(!req->read_chunked || read==size || req->contentLength!=req->contentRead
2354 || !req->contentLength || (req->gzip_stream && req->gzip_stream->end_of_data))
2355 break;
2356 LeaveCriticalSection( &req->read_section );
2358 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2359 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2360 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2361 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2363 EnterCriticalSection( &req->read_section );
2366 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2367 hdr->dwError = ERROR_SUCCESS;
2368 else
2369 error = hdr->dwError;
2371 LeaveCriticalSection( &req->read_section );
2372 size = buffers->dwBufferLength;
2373 buffers->dwBufferLength = read;
2375 done:
2376 if (res == ERROR_SUCCESS) {
2377 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2378 &size, sizeof(size));
2381 return res==ERROR_SUCCESS ? error : res;
2384 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2386 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2387 http_request_t *req = (http_request_t*)workRequest->hdr;
2388 INTERNET_ASYNC_RESULT iar;
2389 DWORD res;
2391 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2393 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2394 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2396 iar.dwResult = res == ERROR_SUCCESS;
2397 iar.dwError = res;
2399 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2400 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2401 sizeof(INTERNET_ASYNC_RESULT));
2404 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2405 DWORD flags, DWORD_PTR context)
2408 http_request_t *req = (http_request_t*)hdr;
2409 DWORD res, size, read, error = ERROR_SUCCESS;
2411 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2412 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2414 if (buffers->dwStructSize != sizeof(*buffers))
2415 return ERROR_INVALID_PARAMETER;
2417 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2419 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2421 WORKREQUEST workRequest;
2423 if (TryEnterCriticalSection( &req->read_section ))
2425 if (get_avail_data(req))
2427 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2428 &buffers->dwBufferLength, FALSE);
2429 size = buffers->dwBufferLength;
2430 LeaveCriticalSection( &req->read_section );
2431 goto done;
2433 LeaveCriticalSection( &req->read_section );
2436 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2437 workRequest.hdr = WININET_AddRef(&req->hdr);
2438 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2440 INTERNET_AsyncCall(&workRequest);
2442 return ERROR_IO_PENDING;
2445 read = 0;
2446 size = buffers->dwBufferLength;
2448 EnterCriticalSection( &req->read_section );
2449 if(hdr->dwError == ERROR_SUCCESS)
2450 hdr->dwError = INTERNET_HANDLE_IN_USE;
2451 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2452 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2454 while(1) {
2455 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2456 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2457 if(res == ERROR_SUCCESS)
2458 read += buffers->dwBufferLength;
2459 else
2460 break;
2462 if(!req->read_chunked || read==size || req->contentLength!=req->contentRead
2463 || !req->contentLength || (req->gzip_stream && req->gzip_stream->end_of_data))
2464 break;
2465 LeaveCriticalSection( &req->read_section );
2467 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2468 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2469 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2470 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2472 EnterCriticalSection( &req->read_section );
2475 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2476 hdr->dwError = ERROR_SUCCESS;
2477 else
2478 error = hdr->dwError;
2480 LeaveCriticalSection( &req->read_section );
2481 size = buffers->dwBufferLength;
2482 buffers->dwBufferLength = read;
2484 done:
2485 if (res == ERROR_SUCCESS) {
2486 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2487 &size, sizeof(size));
2490 return res==ERROR_SUCCESS ? error : res;
2493 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2495 DWORD res;
2496 http_request_t *request = (http_request_t*)hdr;
2498 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2500 *written = 0;
2501 res = NETCON_send(&request->netConnection, buffer, size, 0, (LPINT)written);
2502 if (res == ERROR_SUCCESS)
2503 request->bytesWritten += *written;
2505 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2506 return res;
2509 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2511 http_request_t *req = (http_request_t*)workRequest->hdr;
2513 HTTP_ReceiveRequestData(req, FALSE);
2516 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2518 http_request_t *req = (http_request_t*)hdr;
2520 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2522 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2524 WORKREQUEST workRequest;
2526 /* never wait, if we can't enter the section we queue an async request right away */
2527 if (TryEnterCriticalSection( &req->read_section ))
2529 if ((*available = get_avail_data( req ))) goto done;
2530 if (end_of_read_data( req )) goto done;
2531 LeaveCriticalSection( &req->read_section );
2534 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2535 workRequest.hdr = WININET_AddRef( &req->hdr );
2537 INTERNET_AsyncCall(&workRequest);
2539 return ERROR_IO_PENDING;
2542 EnterCriticalSection( &req->read_section );
2544 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2546 refill_buffer( req );
2547 *available = get_avail_data( req );
2550 done:
2551 if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */
2553 DWORD extra;
2554 if (NETCON_query_data_available(&req->netConnection, &extra))
2555 *available = min( *available + extra, req->contentLength - req->contentRead );
2557 LeaveCriticalSection( &req->read_section );
2559 TRACE( "returning %u\n", *available );
2560 return ERROR_SUCCESS;
2563 static const object_vtbl_t HTTPREQVtbl = {
2564 HTTPREQ_Destroy,
2565 HTTPREQ_CloseConnection,
2566 HTTPREQ_QueryOption,
2567 HTTPREQ_SetOption,
2568 HTTPREQ_ReadFile,
2569 HTTPREQ_ReadFileExA,
2570 HTTPREQ_ReadFileExW,
2571 HTTPREQ_WriteFile,
2572 HTTPREQ_QueryDataAvailable,
2573 NULL
2576 /***********************************************************************
2577 * HTTP_HttpOpenRequestW (internal)
2579 * Open a HTTP request handle
2581 * RETURNS
2582 * HINTERNET a HTTP request handle on success
2583 * NULL on failure
2586 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
2587 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2588 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2589 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2591 appinfo_t *hIC = NULL;
2592 http_request_t *request;
2593 LPWSTR lpszHostName = NULL;
2594 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2595 DWORD len, res;
2597 TRACE("-->\n");
2599 assert( session->hdr.htype == WH_HHTTPSESSION );
2600 hIC = session->appInfo;
2602 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
2603 if(!request)
2604 return ERROR_OUTOFMEMORY;
2606 request->hdr.htype = WH_HHTTPREQ;
2607 request->hdr.dwFlags = dwFlags;
2608 request->hdr.dwContext = dwContext;
2609 request->contentLength = ~0u;
2611 InitializeCriticalSection( &request->read_section );
2613 WININET_AddRef( &session->hdr );
2614 request->session = session;
2615 list_add_head( &session->hdr.children, &request->hdr.entry );
2617 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2618 (strlenW(session->hostName) + 7 /* length of ":65535" + 1 */));
2619 if (NULL == lpszHostName)
2621 res = ERROR_OUTOFMEMORY;
2622 goto lend;
2625 if ((res = NETCON_init(&request->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2626 goto lend;
2628 if (lpszObjectName && *lpszObjectName) {
2629 HRESULT rc;
2631 len = 0;
2632 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2633 if (rc != E_POINTER)
2634 len = strlenW(lpszObjectName)+1;
2635 request->path = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2636 rc = UrlEscapeW(lpszObjectName, request->path, &len,
2637 URL_ESCAPE_SPACES_ONLY);
2638 if (rc != S_OK)
2640 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2641 strcpyW(request->path,lpszObjectName);
2643 }else {
2644 static const WCHAR slashW[] = {'/',0};
2646 request->path = heap_strdupW(slashW);
2649 if (lpszReferrer && *lpszReferrer)
2650 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2652 if (lpszAcceptTypes)
2654 int i;
2655 for (i = 0; lpszAcceptTypes[i]; i++)
2657 if (!*lpszAcceptTypes[i]) continue;
2658 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
2659 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2660 HTTP_ADDHDR_FLAG_REQ |
2661 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2665 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2666 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2668 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
2669 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
2670 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
2672 sprintfW(lpszHostName, szHostForm, session->hostName, session->hostPort);
2673 HTTP_ProcessHeader(request, hostW, lpszHostName,
2674 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2676 else
2677 HTTP_ProcessHeader(request, hostW, session->hostName,
2678 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2680 if (session->serverPort == INTERNET_INVALID_PORT_NUMBER)
2681 session->serverPort = (dwFlags & INTERNET_FLAG_SECURE ?
2682 INTERNET_DEFAULT_HTTPS_PORT :
2683 INTERNET_DEFAULT_HTTP_PORT);
2685 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
2686 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2687 INTERNET_DEFAULT_HTTPS_PORT :
2688 INTERNET_DEFAULT_HTTP_PORT);
2690 if (NULL != hIC->proxy && hIC->proxy[0] != 0)
2691 HTTP_DealWithProxy( hIC, session, request );
2693 INTERNET_SendCallback(&session->hdr, dwContext,
2694 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
2695 sizeof(HINTERNET));
2697 lend:
2698 TRACE("<-- %u (%p)\n", res, request);
2700 HeapFree(GetProcessHeap(), 0, lpszHostName);
2701 if(res != ERROR_SUCCESS) {
2702 WININET_Release( &request->hdr );
2703 *ret = NULL;
2704 return res;
2707 *ret = request->hdr.hInternet;
2708 return ERROR_SUCCESS;
2711 /***********************************************************************
2712 * HttpOpenRequestW (WININET.@)
2714 * Open a HTTP request handle
2716 * RETURNS
2717 * HINTERNET a HTTP request handle on success
2718 * NULL on failure
2721 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2722 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2723 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2724 DWORD dwFlags, DWORD_PTR dwContext)
2726 http_session_t *session;
2727 HINTERNET handle = NULL;
2728 DWORD res;
2730 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2731 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2732 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2733 dwFlags, dwContext);
2734 if(lpszAcceptTypes!=NULL)
2736 int i;
2737 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2738 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2741 session = (http_session_t*) get_handle_object( hHttpSession );
2742 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
2744 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2745 goto lend;
2749 * My tests seem to show that the windows version does not
2750 * become asynchronous until after this point. And anyhow
2751 * if this call was asynchronous then how would you get the
2752 * necessary HINTERNET pointer returned by this function.
2755 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
2756 lpszVersion, lpszReferrer, lpszAcceptTypes,
2757 dwFlags, dwContext, &handle);
2758 lend:
2759 if( session )
2760 WININET_Release( &session->hdr );
2761 TRACE("returning %p\n", handle);
2762 if(res != ERROR_SUCCESS)
2763 SetLastError(res);
2764 return handle;
2767 /* read any content returned by the server so that the connection can be
2768 * reused */
2769 static void HTTP_DrainContent(http_request_t *req)
2771 DWORD bytes_read;
2773 if (!NETCON_connected(&req->netConnection)) return;
2775 if (req->contentLength == -1)
2777 NETCON_close(&req->netConnection);
2778 return;
2780 if (!strcmpW(req->verb, szHEAD)) return;
2784 char buffer[2048];
2785 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2786 return;
2787 } while (bytes_read);
2790 static const LPCWSTR header_lookup[] = {
2791 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
2792 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
2793 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
2794 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
2795 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
2796 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
2797 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
2798 szAllow, /* HTTP_QUERY_ALLOW = 7 */
2799 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
2800 szDate, /* HTTP_QUERY_DATE = 9 */
2801 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
2802 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
2803 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
2804 szURI, /* HTTP_QUERY_URI = 13 */
2805 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
2806 NULL, /* HTTP_QUERY_COST = 15 */
2807 NULL, /* HTTP_QUERY_LINK = 16 */
2808 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
2809 NULL, /* HTTP_QUERY_VERSION = 18 */
2810 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
2811 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
2812 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
2813 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
2814 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
2815 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
2816 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
2817 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
2818 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
2819 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
2820 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
2821 NULL, /* HTTP_QUERY_FORWARDED = 30 */
2822 NULL, /* HTTP_QUERY_FROM = 31 */
2823 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
2824 szLocation, /* HTTP_QUERY_LOCATION = 33 */
2825 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
2826 szReferer, /* HTTP_QUERY_REFERER = 35 */
2827 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
2828 szServer, /* HTTP_QUERY_SERVER = 37 */
2829 NULL, /* HTTP_TITLE = 38 */
2830 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
2831 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
2832 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
2833 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
2834 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
2835 szCookie, /* HTTP_QUERY_COOKIE = 44 */
2836 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
2837 NULL, /* HTTP_QUERY_REFRESH = 46 */
2838 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
2839 szAge, /* HTTP_QUERY_AGE = 48 */
2840 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
2841 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
2842 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
2843 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
2844 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
2845 szETag, /* HTTP_QUERY_ETAG = 54 */
2846 hostW, /* HTTP_QUERY_HOST = 55 */
2847 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
2848 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
2849 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
2850 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
2851 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
2852 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
2853 szRange, /* HTTP_QUERY_RANGE = 62 */
2854 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
2855 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
2856 szVary, /* HTTP_QUERY_VARY = 65 */
2857 szVia, /* HTTP_QUERY_VIA = 66 */
2858 szWarning, /* HTTP_QUERY_WARNING = 67 */
2859 szExpect, /* HTTP_QUERY_EXPECT = 68 */
2860 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
2861 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
2864 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
2866 /***********************************************************************
2867 * HTTP_HttpQueryInfoW (internal)
2869 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
2870 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
2872 LPHTTPHEADERW lphttpHdr = NULL;
2873 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
2874 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
2875 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
2876 INT index = -1;
2878 /* Find requested header structure */
2879 switch (level)
2881 case HTTP_QUERY_CUSTOM:
2882 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
2883 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
2884 break;
2885 case HTTP_QUERY_RAW_HEADERS_CRLF:
2887 LPWSTR headers;
2888 DWORD len = 0;
2889 DWORD res = ERROR_INVALID_PARAMETER;
2891 if (request_only)
2892 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
2893 else
2894 headers = request->rawHeaders;
2896 if (headers)
2897 len = strlenW(headers) * sizeof(WCHAR);
2899 if (len + sizeof(WCHAR) > *lpdwBufferLength)
2901 len += sizeof(WCHAR);
2902 res = ERROR_INSUFFICIENT_BUFFER;
2904 else if (lpBuffer)
2906 if (headers)
2907 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
2908 else
2910 len = strlenW(szCrLf) * sizeof(WCHAR);
2911 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
2913 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
2914 res = ERROR_SUCCESS;
2916 *lpdwBufferLength = len;
2918 if (request_only)
2919 HeapFree(GetProcessHeap(), 0, headers);
2920 return res;
2922 case HTTP_QUERY_RAW_HEADERS:
2924 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
2925 DWORD i, size = 0;
2926 LPWSTR pszString = lpBuffer;
2928 for (i = 0; ppszRawHeaderLines[i]; i++)
2929 size += strlenW(ppszRawHeaderLines[i]) + 1;
2931 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
2933 HTTP_FreeTokens(ppszRawHeaderLines);
2934 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
2935 return ERROR_INSUFFICIENT_BUFFER;
2937 if (pszString)
2939 for (i = 0; ppszRawHeaderLines[i]; i++)
2941 DWORD len = strlenW(ppszRawHeaderLines[i]);
2942 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
2943 pszString += len+1;
2945 *pszString = '\0';
2946 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
2948 *lpdwBufferLength = size * sizeof(WCHAR);
2949 HTTP_FreeTokens(ppszRawHeaderLines);
2951 return ERROR_SUCCESS;
2953 case HTTP_QUERY_STATUS_TEXT:
2954 if (request->statusText)
2956 DWORD len = strlenW(request->statusText);
2957 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2959 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2960 return ERROR_INSUFFICIENT_BUFFER;
2962 if (lpBuffer)
2964 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
2965 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2967 *lpdwBufferLength = len * sizeof(WCHAR);
2968 return ERROR_SUCCESS;
2970 break;
2971 case HTTP_QUERY_VERSION:
2972 if (request->version)
2974 DWORD len = strlenW(request->version);
2975 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
2977 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
2978 return ERROR_INSUFFICIENT_BUFFER;
2980 if (lpBuffer)
2982 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
2983 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
2985 *lpdwBufferLength = len * sizeof(WCHAR);
2986 return ERROR_SUCCESS;
2988 break;
2989 case HTTP_QUERY_CONTENT_ENCODING:
2990 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
2991 requested_index,request_only);
2992 break;
2993 default:
2994 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
2996 if (level < LAST_TABLE_HEADER && header_lookup[level])
2997 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
2998 requested_index,request_only);
3001 if (index >= 0)
3002 lphttpHdr = &request->custHeaders[index];
3004 /* Ensure header satisfies requested attributes */
3005 if (!lphttpHdr ||
3006 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3007 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3009 return ERROR_HTTP_HEADER_NOT_FOUND;
3012 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
3014 /* coalesce value to requested type */
3015 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3017 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3018 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3020 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3022 time_t tmpTime;
3023 struct tm tmpTM;
3024 SYSTEMTIME *STHook;
3026 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3028 tmpTM = *gmtime(&tmpTime);
3029 STHook = (SYSTEMTIME *)lpBuffer;
3030 STHook->wDay = tmpTM.tm_mday;
3031 STHook->wHour = tmpTM.tm_hour;
3032 STHook->wMilliseconds = 0;
3033 STHook->wMinute = tmpTM.tm_min;
3034 STHook->wDayOfWeek = tmpTM.tm_wday;
3035 STHook->wMonth = tmpTM.tm_mon + 1;
3036 STHook->wSecond = tmpTM.tm_sec;
3037 STHook->wYear = tmpTM.tm_year;
3039 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3040 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3041 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3043 else if (lphttpHdr->lpszValue)
3045 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3047 if (len > *lpdwBufferLength)
3049 *lpdwBufferLength = len;
3050 return ERROR_INSUFFICIENT_BUFFER;
3052 if (lpBuffer)
3054 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3055 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3057 *lpdwBufferLength = len - sizeof(WCHAR);
3059 return ERROR_SUCCESS;
3062 /***********************************************************************
3063 * HttpQueryInfoW (WININET.@)
3065 * Queries for information about an HTTP request
3067 * RETURNS
3068 * TRUE on success
3069 * FALSE on failure
3072 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3073 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3075 http_request_t *request;
3076 DWORD res;
3078 if (TRACE_ON(wininet)) {
3079 #define FE(x) { x, #x }
3080 static const wininet_flag_info query_flags[] = {
3081 FE(HTTP_QUERY_MIME_VERSION),
3082 FE(HTTP_QUERY_CONTENT_TYPE),
3083 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3084 FE(HTTP_QUERY_CONTENT_ID),
3085 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3086 FE(HTTP_QUERY_CONTENT_LENGTH),
3087 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3088 FE(HTTP_QUERY_ALLOW),
3089 FE(HTTP_QUERY_PUBLIC),
3090 FE(HTTP_QUERY_DATE),
3091 FE(HTTP_QUERY_EXPIRES),
3092 FE(HTTP_QUERY_LAST_MODIFIED),
3093 FE(HTTP_QUERY_MESSAGE_ID),
3094 FE(HTTP_QUERY_URI),
3095 FE(HTTP_QUERY_DERIVED_FROM),
3096 FE(HTTP_QUERY_COST),
3097 FE(HTTP_QUERY_LINK),
3098 FE(HTTP_QUERY_PRAGMA),
3099 FE(HTTP_QUERY_VERSION),
3100 FE(HTTP_QUERY_STATUS_CODE),
3101 FE(HTTP_QUERY_STATUS_TEXT),
3102 FE(HTTP_QUERY_RAW_HEADERS),
3103 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3104 FE(HTTP_QUERY_CONNECTION),
3105 FE(HTTP_QUERY_ACCEPT),
3106 FE(HTTP_QUERY_ACCEPT_CHARSET),
3107 FE(HTTP_QUERY_ACCEPT_ENCODING),
3108 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3109 FE(HTTP_QUERY_AUTHORIZATION),
3110 FE(HTTP_QUERY_CONTENT_ENCODING),
3111 FE(HTTP_QUERY_FORWARDED),
3112 FE(HTTP_QUERY_FROM),
3113 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3114 FE(HTTP_QUERY_LOCATION),
3115 FE(HTTP_QUERY_ORIG_URI),
3116 FE(HTTP_QUERY_REFERER),
3117 FE(HTTP_QUERY_RETRY_AFTER),
3118 FE(HTTP_QUERY_SERVER),
3119 FE(HTTP_QUERY_TITLE),
3120 FE(HTTP_QUERY_USER_AGENT),
3121 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3122 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3123 FE(HTTP_QUERY_ACCEPT_RANGES),
3124 FE(HTTP_QUERY_SET_COOKIE),
3125 FE(HTTP_QUERY_COOKIE),
3126 FE(HTTP_QUERY_REQUEST_METHOD),
3127 FE(HTTP_QUERY_REFRESH),
3128 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3129 FE(HTTP_QUERY_AGE),
3130 FE(HTTP_QUERY_CACHE_CONTROL),
3131 FE(HTTP_QUERY_CONTENT_BASE),
3132 FE(HTTP_QUERY_CONTENT_LOCATION),
3133 FE(HTTP_QUERY_CONTENT_MD5),
3134 FE(HTTP_QUERY_CONTENT_RANGE),
3135 FE(HTTP_QUERY_ETAG),
3136 FE(HTTP_QUERY_HOST),
3137 FE(HTTP_QUERY_IF_MATCH),
3138 FE(HTTP_QUERY_IF_NONE_MATCH),
3139 FE(HTTP_QUERY_IF_RANGE),
3140 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3141 FE(HTTP_QUERY_MAX_FORWARDS),
3142 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3143 FE(HTTP_QUERY_RANGE),
3144 FE(HTTP_QUERY_TRANSFER_ENCODING),
3145 FE(HTTP_QUERY_UPGRADE),
3146 FE(HTTP_QUERY_VARY),
3147 FE(HTTP_QUERY_VIA),
3148 FE(HTTP_QUERY_WARNING),
3149 FE(HTTP_QUERY_CUSTOM)
3151 static const wininet_flag_info modifier_flags[] = {
3152 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3153 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3154 FE(HTTP_QUERY_FLAG_NUMBER),
3155 FE(HTTP_QUERY_FLAG_COALESCE)
3157 #undef FE
3158 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3159 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3160 DWORD i;
3162 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3163 TRACE(" Attribute:");
3164 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3165 if (query_flags[i].val == info) {
3166 TRACE(" %s", query_flags[i].name);
3167 break;
3170 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3171 TRACE(" Unknown (%08x)", info);
3174 TRACE(" Modifier:");
3175 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3176 if (modifier_flags[i].val & info_mod) {
3177 TRACE(" %s", modifier_flags[i].name);
3178 info_mod &= ~ modifier_flags[i].val;
3182 if (info_mod) {
3183 TRACE(" Unknown (%08x)", info_mod);
3185 TRACE("\n");
3188 request = (http_request_t*) get_handle_object( hHttpRequest );
3189 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3191 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3192 goto lend;
3195 if (lpBuffer == NULL)
3196 *lpdwBufferLength = 0;
3197 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3198 lpBuffer, lpdwBufferLength, lpdwIndex);
3200 lend:
3201 if( request )
3202 WININET_Release( &request->hdr );
3204 TRACE("%u <--\n", res);
3205 if(res != ERROR_SUCCESS)
3206 SetLastError(res);
3207 return res == ERROR_SUCCESS;
3210 /***********************************************************************
3211 * HttpQueryInfoA (WININET.@)
3213 * Queries for information about an HTTP request
3215 * RETURNS
3216 * TRUE on success
3217 * FALSE on failure
3220 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3221 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3223 BOOL result;
3224 DWORD len;
3225 WCHAR* bufferW;
3227 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3228 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3230 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3231 lpdwBufferLength, lpdwIndex );
3234 if (lpBuffer)
3236 DWORD alloclen;
3237 len = (*lpdwBufferLength)*sizeof(WCHAR);
3238 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3240 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3241 if (alloclen < len)
3242 alloclen = len;
3244 else
3245 alloclen = len;
3246 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3247 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3248 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3249 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3250 } else
3252 bufferW = NULL;
3253 len = 0;
3256 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3257 &len, lpdwIndex );
3258 if( result )
3260 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3261 lpBuffer, *lpdwBufferLength, NULL, NULL );
3262 *lpdwBufferLength = len - 1;
3264 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3266 else
3267 /* since the strings being returned from HttpQueryInfoW should be
3268 * only ASCII characters, it is reasonable to assume that all of
3269 * the Unicode characters can be reduced to a single byte */
3270 *lpdwBufferLength = len / sizeof(WCHAR);
3272 HeapFree(GetProcessHeap(), 0, bufferW );
3274 return result;
3277 /***********************************************************************
3278 * HTTP_GetRedirectURL (internal)
3280 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3282 static WCHAR szHttp[] = {'h','t','t','p',0};
3283 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3284 http_session_t *session = request->session;
3285 URL_COMPONENTSW urlComponents;
3286 DWORD url_length = 0;
3287 LPWSTR orig_url;
3288 LPWSTR combined_url;
3290 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3291 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3292 urlComponents.dwSchemeLength = 0;
3293 urlComponents.lpszHostName = session->hostName;
3294 urlComponents.dwHostNameLength = 0;
3295 urlComponents.nPort = session->hostPort;
3296 urlComponents.lpszUserName = session->userName;
3297 urlComponents.dwUserNameLength = 0;
3298 urlComponents.lpszPassword = NULL;
3299 urlComponents.dwPasswordLength = 0;
3300 urlComponents.lpszUrlPath = request->path;
3301 urlComponents.dwUrlPathLength = 0;
3302 urlComponents.lpszExtraInfo = NULL;
3303 urlComponents.dwExtraInfoLength = 0;
3305 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3306 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3307 return NULL;
3309 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3311 /* convert from bytes to characters */
3312 url_length = url_length / sizeof(WCHAR) - 1;
3313 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3315 HeapFree(GetProcessHeap(), 0, orig_url);
3316 return NULL;
3319 url_length = 0;
3320 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3321 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3323 HeapFree(GetProcessHeap(), 0, orig_url);
3324 return NULL;
3326 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3328 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3330 HeapFree(GetProcessHeap(), 0, orig_url);
3331 HeapFree(GetProcessHeap(), 0, combined_url);
3332 return NULL;
3334 HeapFree(GetProcessHeap(), 0, orig_url);
3335 return combined_url;
3339 /***********************************************************************
3340 * HTTP_HandleRedirect (internal)
3342 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3344 http_session_t *session = request->session;
3345 appinfo_t *hIC = session->appInfo;
3346 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3347 WCHAR path[INTERNET_MAX_URL_LENGTH];
3348 int index;
3350 if(lpszUrl[0]=='/')
3352 /* if it's an absolute path, keep the same session info */
3353 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3355 else
3357 URL_COMPONENTSW urlComponents;
3358 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3359 static WCHAR szHttp[] = {'h','t','t','p',0};
3360 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3362 userName[0] = 0;
3363 hostName[0] = 0;
3364 protocol[0] = 0;
3366 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3367 urlComponents.lpszScheme = protocol;
3368 urlComponents.dwSchemeLength = 32;
3369 urlComponents.lpszHostName = hostName;
3370 urlComponents.dwHostNameLength = MAXHOSTNAME;
3371 urlComponents.lpszUserName = userName;
3372 urlComponents.dwUserNameLength = 1024;
3373 urlComponents.lpszPassword = NULL;
3374 urlComponents.dwPasswordLength = 0;
3375 urlComponents.lpszUrlPath = path;
3376 urlComponents.dwUrlPathLength = 2048;
3377 urlComponents.lpszExtraInfo = NULL;
3378 urlComponents.dwExtraInfoLength = 0;
3379 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3380 return INTERNET_GetLastError();
3382 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3383 (request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3385 TRACE("redirect from secure page to non-secure page\n");
3386 /* FIXME: warn about from secure redirect to non-secure page */
3387 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3389 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3390 !(request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3392 TRACE("redirect from non-secure page to secure page\n");
3393 /* FIXME: notify about redirect to secure page */
3394 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3397 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3399 if (lstrlenW(protocol)>4) /*https*/
3400 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3401 else /*http*/
3402 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3405 #if 0
3407 * This upsets redirects to binary files on sourceforge.net
3408 * and gives an html page instead of the target file
3409 * Examination of the HTTP request sent by native wininet.dll
3410 * reveals that it doesn't send a referrer in that case.
3411 * Maybe there's a flag that enables this, or maybe a referrer
3412 * shouldn't be added in case of a redirect.
3415 /* consider the current host as the referrer */
3416 if (session->lpszServerName && *session->lpszServerName)
3417 HTTP_ProcessHeader(request, HTTP_REFERER, session->lpszServerName,
3418 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3419 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3420 #endif
3422 HeapFree(GetProcessHeap(), 0, session->hostName);
3423 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3424 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3426 int len;
3427 static const WCHAR fmt[] = {'%','s',':','%','i',0};
3428 len = lstrlenW(hostName);
3429 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3430 session->hostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3431 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3433 else
3434 session->hostName = heap_strdupW(hostName);
3436 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3438 HeapFree(GetProcessHeap(), 0, session->userName);
3439 session->userName = NULL;
3440 if (userName[0])
3441 session->userName = heap_strdupW(userName);
3443 if (!using_proxy)
3445 if (strcmpiW(session->serverName, hostName) || session->serverPort != urlComponents.nPort)
3447 DWORD res;
3449 HeapFree(GetProcessHeap(), 0, session->serverName);
3450 session->serverName = heap_strdupW(hostName);
3451 session->serverPort = urlComponents.nPort;
3453 NETCON_close(&request->netConnection);
3454 if ((res = HTTP_ResolveName(request)) != ERROR_SUCCESS)
3455 return res;
3457 res = NETCON_init(&request->netConnection, request->hdr.dwFlags & INTERNET_FLAG_SECURE);
3458 if (res != ERROR_SUCCESS)
3459 return res;
3461 request->read_pos = request->read_size = 0;
3462 request->read_chunked = FALSE;
3465 else
3466 TRACE("Redirect through proxy\n");
3469 HeapFree(GetProcessHeap(), 0, request->path);
3470 request->path=NULL;
3471 if (*path)
3473 DWORD needed = 0;
3474 HRESULT rc;
3476 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3477 if (rc != E_POINTER)
3478 needed = strlenW(path)+1;
3479 request->path = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3480 rc = UrlEscapeW(path, request->path, &needed,
3481 URL_ESCAPE_SPACES_ONLY);
3482 if (rc != S_OK)
3484 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3485 strcpyW(request->path,path);
3489 /* Remove custom content-type/length headers on redirects. */
3490 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3491 if (0 <= index)
3492 HTTP_DeleteCustomHeader(request, index);
3493 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3494 if (0 <= index)
3495 HTTP_DeleteCustomHeader(request, index);
3497 return ERROR_SUCCESS;
3500 /***********************************************************************
3501 * HTTP_build_req (internal)
3503 * concatenate all the strings in the request together
3505 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3507 LPCWSTR *t;
3508 LPWSTR str;
3510 for( t = list; *t ; t++ )
3511 len += strlenW( *t );
3512 len++;
3514 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3515 *str = 0;
3517 for( t = list; *t ; t++ )
3518 strcatW( str, *t );
3520 return str;
3523 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3525 LPWSTR lpszPath;
3526 LPWSTR requestString;
3527 INT len;
3528 INT cnt;
3529 INT responseLen;
3530 char *ascii_req;
3531 DWORD res;
3532 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3533 static const WCHAR szFormat[] = {'%','s',':','%','d',0};
3534 http_session_t *session = request->session;
3536 TRACE("\n");
3538 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( session->hostName ) + 13)*sizeof(WCHAR) );
3539 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
3540 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
3541 HeapFree( GetProcessHeap(), 0, lpszPath );
3543 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3544 NULL, 0, NULL, NULL );
3545 len--; /* the nul terminator isn't needed */
3546 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3547 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3548 ascii_req, len, NULL, NULL );
3549 HeapFree( GetProcessHeap(), 0, requestString );
3551 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3553 res = NETCON_send( &request->netConnection, ascii_req, len, 0, &cnt );
3554 HeapFree( GetProcessHeap(), 0, ascii_req );
3555 if (res != ERROR_SUCCESS)
3556 return res;
3558 responseLen = HTTP_GetResponseHeaders( request, TRUE );
3559 if (!responseLen)
3560 return ERROR_HTTP_INVALID_HEADER;
3562 return ERROR_SUCCESS;
3565 static void HTTP_InsertCookies(http_request_t *request)
3567 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3568 LPWSTR lpszCookies, lpszUrl = NULL;
3569 DWORD nCookieSize, size;
3570 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
3572 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(request->path)) * sizeof(WCHAR);
3573 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3574 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, request->path);
3576 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3578 int cnt = 0;
3579 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3581 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3582 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3584 cnt += sprintfW(lpszCookies, szCookie);
3585 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3586 strcatW(lpszCookies, szCrLf);
3588 HTTP_HttpAddRequestHeadersW(request, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3589 HeapFree(GetProcessHeap(), 0, lpszCookies);
3592 HeapFree(GetProcessHeap(), 0, lpszUrl);
3595 static WORD HTTP_ParseDay(LPCWSTR day)
3597 static const WCHAR days[7][4] = {{ 's','u','n',0 },
3598 { 'm','o','n',0 },
3599 { 't','u','e',0 },
3600 { 'w','e','d',0 },
3601 { 't','h','u',0 },
3602 { 'f','r','i',0 },
3603 { 's','a','t',0 }};
3604 int i;
3605 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
3606 if (!strcmpiW(day, days[i]))
3607 return i;
3609 /* Invalid */
3610 return 7;
3613 static WORD HTTP_ParseMonth(LPCWSTR month)
3615 static const WCHAR jan[] = { 'j','a','n',0 };
3616 static const WCHAR feb[] = { 'f','e','b',0 };
3617 static const WCHAR mar[] = { 'm','a','r',0 };
3618 static const WCHAR apr[] = { 'a','p','r',0 };
3619 static const WCHAR may[] = { 'm','a','y',0 };
3620 static const WCHAR jun[] = { 'j','u','n',0 };
3621 static const WCHAR jul[] = { 'j','u','l',0 };
3622 static const WCHAR aug[] = { 'a','u','g',0 };
3623 static const WCHAR sep[] = { 's','e','p',0 };
3624 static const WCHAR oct[] = { 'o','c','t',0 };
3625 static const WCHAR nov[] = { 'n','o','v',0 };
3626 static const WCHAR dec[] = { 'd','e','c',0 };
3628 if (!strcmpiW(month, jan)) return 1;
3629 if (!strcmpiW(month, feb)) return 2;
3630 if (!strcmpiW(month, mar)) return 3;
3631 if (!strcmpiW(month, apr)) return 4;
3632 if (!strcmpiW(month, may)) return 5;
3633 if (!strcmpiW(month, jun)) return 6;
3634 if (!strcmpiW(month, jul)) return 7;
3635 if (!strcmpiW(month, aug)) return 8;
3636 if (!strcmpiW(month, sep)) return 9;
3637 if (!strcmpiW(month, oct)) return 10;
3638 if (!strcmpiW(month, nov)) return 11;
3639 if (!strcmpiW(month, dec)) return 12;
3640 /* Invalid */
3641 return 0;
3644 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
3645 * optionally preceded by whitespace.
3646 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
3647 * st, and sets *str to the first character after the time format.
3649 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
3651 LPCWSTR ptr = *str;
3652 WCHAR *nextPtr;
3653 unsigned long num;
3655 while (isspaceW(*ptr))
3656 ptr++;
3658 num = strtoulW(ptr, &nextPtr, 10);
3659 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
3661 ERR("unexpected time format %s\n", debugstr_w(ptr));
3662 return FALSE;
3664 if (num > 23)
3666 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
3667 return FALSE;
3669 ptr = nextPtr + 1;
3670 st->wHour = (WORD)num;
3671 num = strtoulW(ptr, &nextPtr, 10);
3672 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
3674 ERR("unexpected time format %s\n", debugstr_w(ptr));
3675 return FALSE;
3677 if (num > 59)
3679 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
3680 return FALSE;
3682 ptr = nextPtr + 1;
3683 st->wMinute = (WORD)num;
3684 num = strtoulW(ptr, &nextPtr, 10);
3685 if (!nextPtr || nextPtr <= ptr)
3687 ERR("unexpected time format %s\n", debugstr_w(ptr));
3688 return FALSE;
3690 if (num > 59)
3692 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
3693 return FALSE;
3695 ptr = nextPtr + 1;
3696 *str = ptr;
3697 st->wSecond = (WORD)num;
3698 return TRUE;
3701 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
3703 static const WCHAR gmt[]= { 'G','M','T',0 };
3704 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
3705 LPCWSTR ptr;
3706 SYSTEMTIME st = { 0 };
3707 unsigned long num;
3709 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
3710 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
3711 *dayPtr = *ptr;
3712 *dayPtr = 0;
3713 st.wDayOfWeek = HTTP_ParseDay(day);
3714 if (st.wDayOfWeek >= 7)
3716 ERR("unexpected weekday %s\n", debugstr_w(day));
3717 return FALSE;
3720 while (isspaceW(*ptr))
3721 ptr++;
3723 for (monthPtr = month; !isspace(*ptr) &&
3724 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
3725 monthPtr++, ptr++)
3726 *monthPtr = *ptr;
3727 *monthPtr = 0;
3728 st.wMonth = HTTP_ParseMonth(month);
3729 if (!st.wMonth || st.wMonth > 12)
3731 ERR("unexpected month %s\n", debugstr_w(month));
3732 return FALSE;
3735 while (isspaceW(*ptr))
3736 ptr++;
3738 num = strtoulW(ptr, &nextPtr, 10);
3739 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
3741 ERR("unexpected day %s\n", debugstr_w(ptr));
3742 return FALSE;
3744 ptr = nextPtr;
3745 st.wDay = (WORD)num;
3747 while (isspaceW(*ptr))
3748 ptr++;
3750 if (!HTTP_ParseTime(&st, &ptr))
3751 return FALSE;
3753 while (isspaceW(*ptr))
3754 ptr++;
3756 num = strtoulW(ptr, &nextPtr, 10);
3757 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
3759 ERR("unexpected year %s\n", debugstr_w(ptr));
3760 return FALSE;
3762 ptr = nextPtr;
3763 st.wYear = (WORD)num;
3765 while (isspaceW(*ptr))
3766 ptr++;
3768 /* asctime() doesn't report a timezone, but some web servers do, so accept
3769 * with or without GMT.
3771 if (*ptr && strcmpW(ptr, gmt))
3773 ERR("unexpected timezone %s\n", debugstr_w(ptr));
3774 return FALSE;
3776 return SystemTimeToFileTime(&st, ft);
3779 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
3781 static const WCHAR gmt[]= { 'G','M','T',0 };
3782 WCHAR *nextPtr, day[4], month[4], *monthPtr;
3783 LPCWSTR ptr;
3784 unsigned long num;
3785 SYSTEMTIME st = { 0 };
3787 ptr = strchrW(value, ',');
3788 if (!ptr)
3789 return FALSE;
3790 if (ptr - value != 3)
3792 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
3793 return FALSE;
3795 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
3796 day[3] = 0;
3797 st.wDayOfWeek = HTTP_ParseDay(day);
3798 if (st.wDayOfWeek > 6)
3800 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
3801 return FALSE;
3803 ptr++;
3805 while (isspaceW(*ptr))
3806 ptr++;
3808 num = strtoulW(ptr, &nextPtr, 10);
3809 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
3811 ERR("unexpected day %s\n", debugstr_w(value));
3812 return FALSE;
3814 ptr = nextPtr;
3815 st.wDay = (WORD)num;
3817 while (isspaceW(*ptr))
3818 ptr++;
3820 for (monthPtr = month; !isspace(*ptr) &&
3821 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
3822 monthPtr++, ptr++)
3823 *monthPtr = *ptr;
3824 *monthPtr = 0;
3825 st.wMonth = HTTP_ParseMonth(month);
3826 if (!st.wMonth || st.wMonth > 12)
3828 ERR("unexpected month %s\n", debugstr_w(month));
3829 return FALSE;
3832 while (isspaceW(*ptr))
3833 ptr++;
3835 num = strtoulW(ptr, &nextPtr, 10);
3836 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
3838 ERR("unexpected year %s\n", debugstr_w(value));
3839 return FALSE;
3841 ptr = nextPtr;
3842 st.wYear = (WORD)num;
3844 if (!HTTP_ParseTime(&st, &ptr))
3845 return FALSE;
3847 while (isspaceW(*ptr))
3848 ptr++;
3850 if (strcmpW(ptr, gmt))
3852 ERR("unexpected time zone %s\n", debugstr_w(ptr));
3853 return FALSE;
3855 return SystemTimeToFileTime(&st, ft);
3858 /* FIXME: only accepts dates in RFC 1123 format and asctime() format,
3859 * which may not be the only formats actually seen in the wild.
3860 * http://www.hackcraft.net/web/datetime/ suggests at least RFC 850 dates
3861 * should be accepted as well.
3863 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
3865 BOOL ret;
3867 if (strchrW(value, ','))
3868 ret = HTTP_ParseRfc1123Date(value, ft);
3869 else
3871 ret = HTTP_ParseDateAsAsctime(value, ft);
3872 if (!ret)
3873 ERR("unexpected date format %s\n", debugstr_w(value));
3875 return ret;
3878 static void HTTP_ProcessExpires(http_request_t *request)
3880 BOOL expirationFound = FALSE;
3881 int headerIndex;
3883 /* Look for a Cache-Control header with a max-age directive, as it takes
3884 * precedence over the Expires header.
3886 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
3887 if (headerIndex != -1)
3889 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
3890 LPWSTR ptr;
3892 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
3894 LPWSTR comma = strchrW(ptr, ','), end, equal;
3896 if (comma)
3897 end = comma;
3898 else
3899 end = ptr + strlenW(ptr);
3900 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
3902 if (*equal == '=')
3904 static const WCHAR max_age[] = {
3905 'm','a','x','-','a','g','e',0 };
3907 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
3909 LPWSTR nextPtr;
3910 unsigned long age;
3912 age = strtoulW(equal + 1, &nextPtr, 10);
3913 if (nextPtr > equal + 1)
3915 LARGE_INTEGER ft;
3917 NtQuerySystemTime( &ft );
3918 /* Age is in seconds, FILETIME resolution is in
3919 * 100 nanosecond intervals.
3921 ft.QuadPart += age * (ULONGLONG)1000000;
3922 request->expires.dwLowDateTime = ft.u.LowPart;
3923 request->expires.dwHighDateTime = ft.u.HighPart;
3924 expirationFound = TRUE;
3928 if (comma)
3930 ptr = comma + 1;
3931 while (isspaceW(*ptr))
3932 ptr++;
3934 else
3935 ptr = NULL;
3938 if (!expirationFound)
3940 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
3941 if (headerIndex != -1)
3943 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
3944 FILETIME ft;
3946 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
3948 expirationFound = TRUE;
3949 request->expires = ft;
3953 if (!expirationFound)
3955 LARGE_INTEGER t;
3957 /* With no known age, default to 10 minutes until expiration. */
3958 NtQuerySystemTime( &t );
3959 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
3960 request->expires.dwLowDateTime = t.u.LowPart;
3961 request->expires.dwHighDateTime = t.u.HighPart;
3965 static void HTTP_ProcessLastModified(http_request_t *request)
3967 int headerIndex;
3969 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
3970 if (headerIndex != -1)
3972 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
3973 FILETIME ft;
3975 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
3976 request->last_modified = ft;
3980 static void HTTP_CacheRequest(http_request_t *request)
3982 WCHAR url[INTERNET_MAX_URL_LENGTH];
3983 WCHAR cacheFileName[MAX_PATH+1];
3984 BOOL b;
3986 b = HTTP_GetRequestURL(request, url);
3987 if(!b) {
3988 WARN("Could not get URL\n");
3989 return;
3992 b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
3993 if(b) {
3994 HeapFree(GetProcessHeap(), 0, request->cacheFile);
3995 CloseHandle(request->hCacheFile);
3997 request->cacheFile = heap_strdupW(cacheFileName);
3998 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
3999 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4000 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4001 WARN("Could not create file: %u\n", GetLastError());
4002 request->hCacheFile = NULL;
4004 }else {
4005 WARN("Could not create cache entry: %08x\n", GetLastError());
4009 /***********************************************************************
4010 * HTTP_HttpSendRequestW (internal)
4012 * Sends the specified request to the HTTP server
4014 * RETURNS
4015 * ERROR_SUCCESS on success
4016 * win32 error code on failure
4019 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4020 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4021 DWORD dwContentLength, BOOL bEndRequest)
4023 INT cnt;
4024 BOOL redirected = FALSE;
4025 LPWSTR requestString = NULL;
4026 INT responseLen;
4027 BOOL loop_next;
4028 INTERNET_ASYNC_RESULT iar;
4029 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4030 static const WCHAR szContentLength[] =
4031 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4032 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4033 DWORD res;
4035 TRACE("--> %p\n", request);
4037 assert(request->hdr.htype == WH_HHTTPREQ);
4039 /* if the verb is NULL default to GET */
4040 if (!request->verb)
4041 request->verb = heap_strdupW(szGET);
4043 if (dwContentLength || strcmpW(request->verb, szGET))
4045 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4046 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4047 request->bytesToWrite = dwContentLength;
4049 if (request->session->appInfo->agent)
4051 WCHAR *agent_header;
4052 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4053 int len;
4055 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4056 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
4057 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4059 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4060 HeapFree(GetProcessHeap(), 0, agent_header);
4062 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4064 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4065 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4067 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4069 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4070 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4071 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4076 DWORD len;
4077 BOOL reusing_connection;
4078 char *ascii_req;
4080 loop_next = FALSE;
4082 /* like native, just in case the caller forgot to call InternetReadFile
4083 * for all the data */
4084 HTTP_DrainContent(request);
4085 request->contentRead = 0;
4086 if(redirected) {
4087 request->contentLength = ~0u;
4088 request->bytesToWrite = 0;
4091 if (TRACE_ON(wininet))
4093 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4094 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4097 HTTP_FixURL(request);
4098 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4100 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4102 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4103 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4105 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4106 HTTP_InsertCookies(request);
4108 /* add the headers the caller supplied */
4109 if( lpszHeaders && dwHeaderLength )
4111 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength,
4112 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4115 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4117 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4118 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4119 HeapFree(GetProcessHeap(), 0, url);
4121 else
4122 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4125 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4127 /* Send the request and store the results */
4128 if(NETCON_connected(&request->netConnection))
4129 reusing_connection = TRUE;
4130 else
4131 reusing_connection = FALSE;
4133 if ((res = HTTP_OpenConnection(request)) != ERROR_SUCCESS)
4134 goto lend;
4136 /* send the request as ASCII, tack on the optional data */
4137 if (!lpOptional || redirected)
4138 dwOptionalLength = 0;
4139 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4140 NULL, 0, NULL, NULL );
4141 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
4142 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4143 ascii_req, len, NULL, NULL );
4144 if( lpOptional )
4145 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4146 len = (len + dwOptionalLength - 1);
4147 ascii_req[len] = 0;
4148 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4150 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4151 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4153 res = NETCON_send(&request->netConnection, ascii_req, len, 0, &cnt);
4154 HeapFree( GetProcessHeap(), 0, ascii_req );
4156 request->bytesWritten = dwOptionalLength;
4158 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4159 INTERNET_STATUS_REQUEST_SENT,
4160 &len, sizeof(DWORD));
4162 if (bEndRequest)
4164 DWORD dwBufferSize;
4165 DWORD dwStatusCode;
4167 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4168 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4170 if (res != ERROR_SUCCESS)
4171 goto lend;
4173 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4174 /* FIXME: We should know that connection is closed before sending
4175 * headers. Otherwise wrong callbacks are executed */
4176 if(!responseLen && reusing_connection) {
4177 TRACE("Connection closed by server, reconnecting\n");
4178 loop_next = TRUE;
4179 continue;
4182 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4183 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4184 sizeof(DWORD));
4186 HTTP_ProcessCookies(request);
4187 HTTP_ProcessExpires(request);
4188 HTTP_ProcessLastModified(request);
4190 if (!set_content_length( request )) HTTP_FinishedReading(request);
4192 dwBufferSize = sizeof(dwStatusCode);
4193 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4194 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
4195 dwStatusCode = 0;
4197 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4199 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4200 dwBufferSize=sizeof(szNewLocation);
4201 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
4202 dwStatusCode == HTTP_STATUS_MOVED ||
4203 dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
4204 HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
4206 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD))
4208 HeapFree(GetProcessHeap(), 0, request->verb);
4209 request->verb = heap_strdupW(szGET);
4211 HTTP_DrainContent(request);
4212 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4214 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4215 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4216 res = HTTP_HandleRedirect(request, new_url);
4217 if (res == ERROR_SUCCESS)
4219 HeapFree(GetProcessHeap(), 0, requestString);
4220 loop_next = TRUE;
4222 HeapFree( GetProcessHeap(), 0, new_url );
4224 redirected = TRUE;
4227 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4229 WCHAR szAuthValue[2048];
4230 dwBufferSize=2048;
4231 if (dwStatusCode == HTTP_STATUS_DENIED)
4233 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4234 DWORD dwIndex = 0;
4235 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4237 if (HTTP_DoAuthorization(request, szAuthValue,
4238 &request->authInfo,
4239 request->session->userName,
4240 request->session->password,
4241 Host->lpszValue))
4243 HeapFree(GetProcessHeap(), 0, requestString);
4244 loop_next = TRUE;
4245 break;
4249 if(!loop_next) {
4250 TRACE("Cleaning wrong authorization data\n");
4251 destroy_authinfo(request->authInfo);
4252 request->authInfo = NULL;
4255 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
4257 DWORD dwIndex = 0;
4258 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4260 if (HTTP_DoAuthorization(request, szAuthValue,
4261 &request->proxyAuthInfo,
4262 request->session->appInfo->proxyUsername,
4263 request->session->appInfo->proxyPassword,
4264 NULL))
4266 loop_next = TRUE;
4267 break;
4271 if(!loop_next) {
4272 TRACE("Cleaning wrong proxy authorization data\n");
4273 destroy_authinfo(request->proxyAuthInfo);
4274 request->proxyAuthInfo = NULL;
4279 else
4280 res = ERROR_SUCCESS;
4282 while (loop_next);
4284 if(res == ERROR_SUCCESS)
4285 HTTP_CacheRequest(request);
4287 lend:
4289 HeapFree(GetProcessHeap(), 0, requestString);
4291 /* TODO: send notification for P3P header */
4293 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4295 if (res == ERROR_SUCCESS && request->bytesWritten == request->bytesToWrite)
4296 HTTP_ReceiveRequestData(request, TRUE);
4297 else
4299 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)request->hdr.hInternet : 0);
4300 iar.dwError = res;
4302 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4303 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4304 sizeof(INTERNET_ASYNC_RESULT));
4308 TRACE("<--\n");
4309 return res;
4312 /***********************************************************************
4314 * Helper functions for the HttpSendRequest(Ex) functions
4317 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
4319 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4320 http_request_t *request = (http_request_t*) workRequest->hdr;
4322 TRACE("%p\n", request);
4324 HTTP_HttpSendRequestW(request, req->lpszHeader,
4325 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
4326 req->dwContentLength, req->bEndRequest);
4328 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
4332 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
4334 INT responseLen;
4335 DWORD dwBufferSize;
4336 DWORD res = ERROR_SUCCESS;
4338 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4339 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4341 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4342 if (!responseLen)
4343 res = ERROR_HTTP_HEADER_NOT_FOUND;
4345 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4346 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
4348 /* process cookies here. Is this right? */
4349 HTTP_ProcessCookies(request);
4350 HTTP_ProcessExpires(request);
4351 HTTP_ProcessLastModified(request);
4353 if (!set_content_length( request )) HTTP_FinishedReading(request);
4355 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
4357 DWORD dwCode,dwCodeLength = sizeof(DWORD);
4358 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
4359 && (dwCode == 302 || dwCode == 301 || dwCode == 303))
4361 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4362 dwBufferSize=sizeof(szNewLocation);
4363 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
4365 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD))
4367 HeapFree(GetProcessHeap(), 0, request->verb);
4368 request->verb = heap_strdupW(szGET);
4370 HTTP_DrainContent(request);
4371 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4373 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4374 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4375 res = HTTP_HandleRedirect(request, new_url);
4376 if (res == ERROR_SUCCESS)
4377 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
4378 HeapFree( GetProcessHeap(), 0, new_url );
4384 if (res == ERROR_SUCCESS) {
4385 HTTP_ReceiveRequestData(request, TRUE);
4386 }else {
4387 INTERNET_ASYNC_RESULT iar = {0, res};
4389 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4390 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4391 sizeof(INTERNET_ASYNC_RESULT));
4394 return res;
4397 /***********************************************************************
4398 * HttpEndRequestA (WININET.@)
4400 * Ends an HTTP request that was started by HttpSendRequestEx
4402 * RETURNS
4403 * TRUE if successful
4404 * FALSE on failure
4407 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
4408 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4410 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
4412 if (lpBuffersOut)
4414 SetLastError(ERROR_INVALID_PARAMETER);
4415 return FALSE;
4418 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
4421 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
4423 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
4424 http_request_t *request = (http_request_t*)work->hdr;
4426 TRACE("%p\n", request);
4428 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
4431 /***********************************************************************
4432 * HttpEndRequestW (WININET.@)
4434 * Ends an HTTP request that was started by HttpSendRequestEx
4436 * RETURNS
4437 * TRUE if successful
4438 * FALSE on failure
4441 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
4442 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4444 http_request_t *request;
4445 DWORD res;
4447 TRACE("-->\n");
4449 if (lpBuffersOut)
4451 SetLastError(ERROR_INVALID_PARAMETER);
4452 return FALSE;
4455 request = (http_request_t*) get_handle_object( hRequest );
4457 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
4459 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
4460 if (request)
4461 WININET_Release( &request->hdr );
4462 return FALSE;
4464 request->hdr.dwFlags |= dwFlags;
4466 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4468 WORKREQUEST work;
4469 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
4471 work.asyncproc = AsyncHttpEndRequestProc;
4472 work.hdr = WININET_AddRef( &request->hdr );
4474 work_endrequest = &work.u.HttpEndRequestW;
4475 work_endrequest->dwFlags = dwFlags;
4476 work_endrequest->dwContext = dwContext;
4478 INTERNET_AsyncCall(&work);
4479 res = ERROR_IO_PENDING;
4481 else
4482 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
4484 WININET_Release( &request->hdr );
4485 TRACE("%u <--\n", res);
4486 if(res != ERROR_SUCCESS)
4487 SetLastError(res);
4488 return res == ERROR_SUCCESS;
4491 /***********************************************************************
4492 * HttpSendRequestExA (WININET.@)
4494 * Sends the specified request to the HTTP server and allows chunked
4495 * transfers.
4497 * RETURNS
4498 * Success: TRUE
4499 * Failure: FALSE, call GetLastError() for more information.
4501 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
4502 LPINTERNET_BUFFERSA lpBuffersIn,
4503 LPINTERNET_BUFFERSA lpBuffersOut,
4504 DWORD dwFlags, DWORD_PTR dwContext)
4506 INTERNET_BUFFERSW BuffersInW;
4507 BOOL rc = FALSE;
4508 DWORD headerlen;
4509 LPWSTR header = NULL;
4511 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4512 lpBuffersOut, dwFlags, dwContext);
4514 if (lpBuffersIn)
4516 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
4517 if (lpBuffersIn->lpcszHeader)
4519 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
4520 lpBuffersIn->dwHeadersLength,0,0);
4521 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
4522 if (!(BuffersInW.lpcszHeader = header))
4524 SetLastError(ERROR_OUTOFMEMORY);
4525 return FALSE;
4527 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
4528 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4529 header, headerlen);
4531 else
4532 BuffersInW.lpcszHeader = NULL;
4533 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
4534 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
4535 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
4536 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
4537 BuffersInW.Next = NULL;
4540 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
4542 HeapFree(GetProcessHeap(),0,header);
4544 return rc;
4547 /***********************************************************************
4548 * HttpSendRequestExW (WININET.@)
4550 * Sends the specified request to the HTTP server and allows chunked
4551 * transfers
4553 * RETURNS
4554 * Success: TRUE
4555 * Failure: FALSE, call GetLastError() for more information.
4557 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
4558 LPINTERNET_BUFFERSW lpBuffersIn,
4559 LPINTERNET_BUFFERSW lpBuffersOut,
4560 DWORD dwFlags, DWORD_PTR dwContext)
4562 http_request_t *request;
4563 http_session_t *session;
4564 appinfo_t *hIC;
4565 DWORD res;
4567 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4568 lpBuffersOut, dwFlags, dwContext);
4570 request = (http_request_t*) get_handle_object( hRequest );
4572 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
4574 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4575 goto lend;
4578 session = request->session;
4579 assert(session->hdr.htype == WH_HHTTPSESSION);
4580 hIC = session->appInfo;
4581 assert(hIC->hdr.htype == WH_HINIT);
4583 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4585 WORKREQUEST workRequest;
4586 struct WORKREQ_HTTPSENDREQUESTW *req;
4588 workRequest.asyncproc = AsyncHttpSendRequestProc;
4589 workRequest.hdr = WININET_AddRef( &request->hdr );
4590 req = &workRequest.u.HttpSendRequestW;
4591 if (lpBuffersIn)
4593 DWORD size = 0;
4595 if (lpBuffersIn->lpcszHeader)
4597 if (lpBuffersIn->dwHeadersLength == ~0u)
4598 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
4599 else
4600 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
4602 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
4603 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
4605 else req->lpszHeader = NULL;
4607 req->dwHeaderLength = size / sizeof(WCHAR);
4608 req->lpOptional = lpBuffersIn->lpvBuffer;
4609 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
4610 req->dwContentLength = lpBuffersIn->dwBufferTotal;
4612 else
4614 req->lpszHeader = NULL;
4615 req->dwHeaderLength = 0;
4616 req->lpOptional = NULL;
4617 req->dwOptionalLength = 0;
4618 req->dwContentLength = 0;
4621 req->bEndRequest = FALSE;
4623 INTERNET_AsyncCall(&workRequest);
4625 * This is from windows.
4627 res = ERROR_IO_PENDING;
4629 else
4631 if (lpBuffersIn)
4632 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4633 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
4634 lpBuffersIn->dwBufferTotal, FALSE);
4635 else
4636 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
4639 lend:
4640 if ( request )
4641 WININET_Release( &request->hdr );
4643 TRACE("<---\n");
4644 SetLastError(res);
4645 return res == ERROR_SUCCESS;
4648 /***********************************************************************
4649 * HttpSendRequestW (WININET.@)
4651 * Sends the specified request to the HTTP server
4653 * RETURNS
4654 * TRUE on success
4655 * FALSE on failure
4658 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4659 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4661 http_request_t *request;
4662 http_session_t *session = NULL;
4663 appinfo_t *hIC = NULL;
4664 DWORD res = ERROR_SUCCESS;
4666 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4667 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4669 request = (http_request_t*) get_handle_object( hHttpRequest );
4670 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
4672 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4673 goto lend;
4676 session = request->session;
4677 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
4679 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4680 goto lend;
4683 hIC = session->appInfo;
4684 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
4686 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4687 goto lend;
4690 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4692 WORKREQUEST workRequest;
4693 struct WORKREQ_HTTPSENDREQUESTW *req;
4695 workRequest.asyncproc = AsyncHttpSendRequestProc;
4696 workRequest.hdr = WININET_AddRef( &request->hdr );
4697 req = &workRequest.u.HttpSendRequestW;
4698 if (lpszHeaders)
4700 DWORD size;
4702 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4703 else size = dwHeaderLength * sizeof(WCHAR);
4705 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4706 memcpy(req->lpszHeader, lpszHeaders, size);
4708 else
4709 req->lpszHeader = 0;
4710 req->dwHeaderLength = dwHeaderLength;
4711 req->lpOptional = lpOptional;
4712 req->dwOptionalLength = dwOptionalLength;
4713 req->dwContentLength = dwOptionalLength;
4714 req->bEndRequest = TRUE;
4716 INTERNET_AsyncCall(&workRequest);
4718 * This is from windows.
4720 res = ERROR_IO_PENDING;
4722 else
4724 res = HTTP_HttpSendRequestW(request, lpszHeaders,
4725 dwHeaderLength, lpOptional, dwOptionalLength,
4726 dwOptionalLength, TRUE);
4728 lend:
4729 if( request )
4730 WININET_Release( &request->hdr );
4732 SetLastError(res);
4733 return res == ERROR_SUCCESS;
4736 /***********************************************************************
4737 * HttpSendRequestA (WININET.@)
4739 * Sends the specified request to the HTTP server
4741 * RETURNS
4742 * TRUE on success
4743 * FALSE on failure
4746 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4747 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4749 BOOL result;
4750 LPWSTR szHeaders=NULL;
4751 DWORD nLen=dwHeaderLength;
4752 if(lpszHeaders!=NULL)
4754 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4755 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4756 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4758 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4759 HeapFree(GetProcessHeap(),0,szHeaders);
4760 return result;
4763 /***********************************************************************
4764 * HTTPSESSION_Destroy (internal)
4766 * Deallocate session handle
4769 static void HTTPSESSION_Destroy(object_header_t *hdr)
4771 http_session_t *session = (http_session_t*) hdr;
4773 TRACE("%p\n", session);
4775 WININET_Release(&session->appInfo->hdr);
4777 HeapFree(GetProcessHeap(), 0, session->hostName);
4778 HeapFree(GetProcessHeap(), 0, session->serverName);
4779 HeapFree(GetProcessHeap(), 0, session->password);
4780 HeapFree(GetProcessHeap(), 0, session->userName);
4783 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4785 switch(option) {
4786 case INTERNET_OPTION_HANDLE_TYPE:
4787 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
4789 if (*size < sizeof(ULONG))
4790 return ERROR_INSUFFICIENT_BUFFER;
4792 *size = sizeof(DWORD);
4793 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
4794 return ERROR_SUCCESS;
4797 return INET_QueryOption(hdr, option, buffer, size, unicode);
4800 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
4802 http_session_t *ses = (http_session_t*)hdr;
4804 switch(option) {
4805 case INTERNET_OPTION_USERNAME:
4807 HeapFree(GetProcessHeap(), 0, ses->userName);
4808 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4809 return ERROR_SUCCESS;
4811 case INTERNET_OPTION_PASSWORD:
4813 HeapFree(GetProcessHeap(), 0, ses->password);
4814 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
4815 return ERROR_SUCCESS;
4817 default: break;
4820 return ERROR_INTERNET_INVALID_OPTION;
4823 static const object_vtbl_t HTTPSESSIONVtbl = {
4824 HTTPSESSION_Destroy,
4825 NULL,
4826 HTTPSESSION_QueryOption,
4827 HTTPSESSION_SetOption,
4828 NULL,
4829 NULL,
4830 NULL,
4831 NULL,
4832 NULL
4836 /***********************************************************************
4837 * HTTP_Connect (internal)
4839 * Create http session handle
4841 * RETURNS
4842 * HINTERNET a session handle on success
4843 * NULL on failure
4846 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
4847 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
4848 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
4849 DWORD dwInternalFlags, HINTERNET *ret)
4851 http_session_t *session = NULL;
4853 TRACE("-->\n");
4855 if (!lpszServerName || !lpszServerName[0])
4856 return ERROR_INVALID_PARAMETER;
4858 assert( hIC->hdr.htype == WH_HINIT );
4860 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
4861 if (!session)
4862 return ERROR_OUTOFMEMORY;
4865 * According to my tests. The name is not resolved until a request is sent
4868 session->hdr.htype = WH_HHTTPSESSION;
4869 session->hdr.dwFlags = dwFlags;
4870 session->hdr.dwContext = dwContext;
4871 session->hdr.dwInternalFlags |= dwInternalFlags;
4873 WININET_AddRef( &hIC->hdr );
4874 session->appInfo = hIC;
4875 list_add_head( &hIC->hdr.children, &session->hdr.entry );
4877 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
4878 if(hIC->proxyBypass)
4879 FIXME("Proxy bypass is ignored.\n");
4881 session->serverName = heap_strdupW(lpszServerName);
4882 session->hostName = heap_strdupW(lpszServerName);
4883 if (lpszUserName && lpszUserName[0])
4884 session->userName = heap_strdupW(lpszUserName);
4885 if (lpszPassword && lpszPassword[0])
4886 session->password = heap_strdupW(lpszPassword);
4887 session->serverPort = serverPort;
4888 session->hostPort = serverPort;
4890 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
4891 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
4893 INTERNET_SendCallback(&hIC->hdr, dwContext,
4894 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
4895 sizeof(HINTERNET));
4899 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
4900 * windows
4903 TRACE("%p --> %p\n", hIC, session);
4905 *ret = session->hdr.hInternet;
4906 return ERROR_SUCCESS;
4910 /***********************************************************************
4911 * HTTP_OpenConnection (internal)
4913 * Connect to a web server
4915 * RETURNS
4917 * TRUE on success
4918 * FALSE on failure
4920 static DWORD HTTP_OpenConnection(http_request_t *request)
4922 http_session_t *session;
4923 appinfo_t *hIC = NULL;
4924 char szaddr[INET6_ADDRSTRLEN];
4925 const void *addr;
4926 DWORD res = ERROR_SUCCESS;
4928 TRACE("-->\n");
4931 if (request->hdr.htype != WH_HHTTPREQ)
4933 res = ERROR_INVALID_PARAMETER;
4934 goto lend;
4937 if (NETCON_connected(&request->netConnection))
4938 goto lend;
4939 if ((res = HTTP_ResolveName(request)) != ERROR_SUCCESS) goto lend;
4941 session = request->session;
4943 hIC = session->appInfo;
4944 switch (session->socketAddress.ss_family)
4946 case AF_INET:
4947 addr = &((struct sockaddr_in *)&session->socketAddress)->sin_addr;
4948 break;
4949 case AF_INET6:
4950 addr = &((struct sockaddr_in6 *)&session->socketAddress)->sin6_addr;
4951 break;
4952 default:
4953 WARN("unsupported family %d\n", session->socketAddress.ss_family);
4954 return ERROR_INTERNET_NAME_NOT_RESOLVED;
4956 inet_ntop(session->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
4957 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4958 INTERNET_STATUS_CONNECTING_TO_SERVER,
4959 szaddr,
4960 strlen(szaddr)+1);
4962 res = NETCON_create(&request->netConnection, session->socketAddress.ss_family, SOCK_STREAM, 0);
4963 if (res != ERROR_SUCCESS)
4965 WARN("Socket creation failed: %u\n", res);
4966 goto lend;
4969 res = NETCON_connect(&request->netConnection, (struct sockaddr *)&session->socketAddress,
4970 session->sa_len);
4971 if(res != ERROR_SUCCESS)
4972 goto lend;
4974 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4975 INTERNET_STATUS_CONNECTED_TO_SERVER,
4976 szaddr, strlen(szaddr)+1);
4978 if (request->hdr.dwFlags & INTERNET_FLAG_SECURE)
4980 /* Note: we differ from Microsoft's WinINet here. they seem to have
4981 * a bug that causes no status callbacks to be sent when starting
4982 * a tunnel to a proxy server using the CONNECT verb. i believe our
4983 * behaviour to be more correct and to not cause any incompatibilities
4984 * because using a secure connection through a proxy server is a rare
4985 * case that would be hard for anyone to depend on */
4986 if (hIC->proxy && (res = HTTP_SecureProxyConnect(request)) != ERROR_SUCCESS) {
4987 HTTPREQ_CloseConnection(&request->hdr);
4988 goto lend;
4991 res = NETCON_secure_connect(&request->netConnection, session->hostName);
4992 if(res != ERROR_SUCCESS)
4994 WARN("Couldn't connect securely to host\n");
4996 if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
4997 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
4998 || res == ERROR_INTERNET_INVALID_CA
4999 || res == ERROR_INTERNET_SEC_CERT_NO_REV
5000 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
5001 || res == ERROR_INTERNET_SEC_CERT_REVOKED
5002 || res == ERROR_INTERNET_SEC_INVALID_CERT
5003 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
5004 res = ERROR_INTERNET_SEC_CERT_ERRORS;
5006 HTTPREQ_CloseConnection(&request->hdr);
5007 goto lend;
5012 lend:
5013 request->read_pos = request->read_size = 0;
5014 request->read_chunked = FALSE;
5015 if(request->gzip_stream)
5016 release_gzip_stream(request);
5018 TRACE("%d <--\n", res);
5019 return res;
5023 /***********************************************************************
5024 * HTTP_clear_response_headers (internal)
5026 * clear out any old response headers
5028 static void HTTP_clear_response_headers( http_request_t *request )
5030 DWORD i;
5032 for( i=0; i<request->nCustHeaders; i++)
5034 if( !request->custHeaders[i].lpszField )
5035 continue;
5036 if( !request->custHeaders[i].lpszValue )
5037 continue;
5038 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5039 continue;
5040 HTTP_DeleteCustomHeader( request, i );
5041 i--;
5045 /***********************************************************************
5046 * HTTP_GetResponseHeaders (internal)
5048 * Read server response
5050 * RETURNS
5052 * TRUE on success
5053 * FALSE on error
5055 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5057 INT cbreaks = 0;
5058 WCHAR buffer[MAX_REPLY_LEN];
5059 DWORD buflen = MAX_REPLY_LEN;
5060 BOOL bSuccess = FALSE;
5061 INT rc = 0;
5062 char bufferA[MAX_REPLY_LEN];
5063 LPWSTR status_code = NULL, status_text = NULL;
5064 DWORD cchMaxRawHeaders = 1024;
5065 LPWSTR lpszRawHeaders = NULL;
5066 LPWSTR temp;
5067 DWORD cchRawHeaders = 0;
5068 BOOL codeHundred = FALSE;
5070 TRACE("-->\n");
5072 if (!NETCON_connected(&request->netConnection))
5073 goto lend;
5075 do {
5076 static const WCHAR szHundred[] = {'1','0','0',0};
5078 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5080 buflen = MAX_REPLY_LEN;
5081 if (!read_line(request, bufferA, &buflen))
5082 goto lend;
5084 /* clear old response headers (eg. from a redirect response) */
5085 if (clear) {
5086 HTTP_clear_response_headers( request );
5087 clear = FALSE;
5090 rc += buflen;
5091 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5092 /* check is this a status code line? */
5093 if (!strncmpW(buffer, g_szHttp1_0, 4))
5095 /* split the version from the status code */
5096 status_code = strchrW( buffer, ' ' );
5097 if( !status_code )
5098 goto lend;
5099 *status_code++=0;
5101 /* split the status code from the status text */
5102 status_text = strchrW( status_code, ' ' );
5103 if( !status_text )
5104 goto lend;
5105 *status_text++=0;
5107 TRACE("version [%s] status code [%s] status text [%s]\n",
5108 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5110 codeHundred = (!strcmpW(status_code, szHundred));
5112 else if (!codeHundred)
5114 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5116 HeapFree(GetProcessHeap(), 0, request->version);
5117 HeapFree(GetProcessHeap(), 0, request->statusText);
5119 request->version = heap_strdupW(g_szHttp1_0);
5120 request->statusText = heap_strdupW(szOK);
5122 HeapFree(GetProcessHeap(), 0, request->rawHeaders);
5123 request->rawHeaders = heap_strdupW(szDefaultHeader);
5125 bSuccess = TRUE;
5126 goto lend;
5128 } while (codeHundred);
5130 /* Add status code */
5131 HTTP_ProcessHeader(request, szStatus, status_code,
5132 HTTP_ADDHDR_FLAG_REPLACE);
5134 HeapFree(GetProcessHeap(),0,request->version);
5135 HeapFree(GetProcessHeap(),0,request->statusText);
5137 request->version = heap_strdupW(buffer);
5138 request->statusText = heap_strdupW(status_text);
5140 /* Restore the spaces */
5141 *(status_code-1) = ' ';
5142 *(status_text-1) = ' ';
5144 /* regenerate raw headers */
5145 lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5146 if (!lpszRawHeaders) goto lend;
5148 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5149 cchMaxRawHeaders *= 2;
5150 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5151 if (temp == NULL) goto lend;
5152 lpszRawHeaders = temp;
5153 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5154 cchRawHeaders += (buflen-1);
5155 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5156 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5157 lpszRawHeaders[cchRawHeaders] = '\0';
5159 /* Parse each response line */
5162 buflen = MAX_REPLY_LEN;
5163 if (read_line(request, bufferA, &buflen))
5165 LPWSTR * pFieldAndValue;
5167 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5169 if (!bufferA[0]) break;
5170 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5172 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5173 if (pFieldAndValue)
5175 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5176 cchMaxRawHeaders *= 2;
5177 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5178 if (temp == NULL) goto lend;
5179 lpszRawHeaders = temp;
5180 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5181 cchRawHeaders += (buflen-1);
5182 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5183 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5184 lpszRawHeaders[cchRawHeaders] = '\0';
5186 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5187 HTTP_ADDREQ_FLAG_ADD );
5189 HTTP_FreeTokens(pFieldAndValue);
5192 else
5194 cbreaks++;
5195 if (cbreaks >= 2)
5196 break;
5198 }while(1);
5200 /* make sure the response header is terminated with an empty line. Some apps really
5201 truly care about that empty line being there for some reason. Just add it to the
5202 header. */
5203 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5205 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5206 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5207 if (temp == NULL) goto lend;
5208 lpszRawHeaders = temp;
5211 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5213 HeapFree(GetProcessHeap(), 0, request->rawHeaders);
5214 request->rawHeaders = lpszRawHeaders;
5215 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5216 bSuccess = TRUE;
5218 lend:
5220 TRACE("<--\n");
5221 if (bSuccess)
5222 return rc;
5223 else
5225 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
5226 return 0;
5230 /***********************************************************************
5231 * HTTP_InterpretHttpHeader (internal)
5233 * Parse server response
5235 * RETURNS
5237 * Pointer to array of field, value, NULL on success.
5238 * NULL on error.
5240 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5242 LPWSTR * pTokenPair;
5243 LPWSTR pszColon;
5244 INT len;
5246 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
5248 pszColon = strchrW(buffer, ':');
5249 /* must have two tokens */
5250 if (!pszColon)
5252 HTTP_FreeTokens(pTokenPair);
5253 if (buffer[0])
5254 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5255 return NULL;
5258 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
5259 if (!pTokenPair[0])
5261 HTTP_FreeTokens(pTokenPair);
5262 return NULL;
5264 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5265 pTokenPair[0][pszColon - buffer] = '\0';
5267 /* skip colon */
5268 pszColon++;
5269 len = strlenW(pszColon);
5270 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
5271 if (!pTokenPair[1])
5273 HTTP_FreeTokens(pTokenPair);
5274 return NULL;
5276 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5278 strip_spaces(pTokenPair[0]);
5279 strip_spaces(pTokenPair[1]);
5281 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5282 return pTokenPair;
5285 /***********************************************************************
5286 * HTTP_ProcessHeader (internal)
5288 * Stuff header into header tables according to <dwModifier>
5292 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5294 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5296 LPHTTPHEADERW lphttpHdr = NULL;
5297 INT index = -1;
5298 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5299 DWORD res = ERROR_HTTP_INVALID_HEADER;
5301 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5303 /* REPLACE wins out over ADD */
5304 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5305 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5307 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5308 index = -1;
5309 else
5310 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5312 if (index >= 0)
5314 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5315 return ERROR_HTTP_INVALID_HEADER;
5316 lphttpHdr = &request->custHeaders[index];
5318 else if (value)
5320 HTTPHEADERW hdr;
5322 hdr.lpszField = (LPWSTR)field;
5323 hdr.lpszValue = (LPWSTR)value;
5324 hdr.wFlags = hdr.wCount = 0;
5326 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5327 hdr.wFlags |= HDR_ISREQUEST;
5329 return HTTP_InsertCustomHeader(request, &hdr);
5331 /* no value to delete */
5332 else return ERROR_SUCCESS;
5334 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5335 lphttpHdr->wFlags |= HDR_ISREQUEST;
5336 else
5337 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5339 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5341 HTTP_DeleteCustomHeader( request, index );
5343 if (value)
5345 HTTPHEADERW hdr;
5347 hdr.lpszField = (LPWSTR)field;
5348 hdr.lpszValue = (LPWSTR)value;
5349 hdr.wFlags = hdr.wCount = 0;
5351 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5352 hdr.wFlags |= HDR_ISREQUEST;
5354 return HTTP_InsertCustomHeader(request, &hdr);
5357 return ERROR_SUCCESS;
5359 else if (dwModifier & COALESCEFLAGS)
5361 LPWSTR lpsztmp;
5362 WCHAR ch = 0;
5363 INT len = 0;
5364 INT origlen = strlenW(lphttpHdr->lpszValue);
5365 INT valuelen = strlenW(value);
5367 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5369 ch = ',';
5370 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5372 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5374 ch = ';';
5375 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5378 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5380 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5381 if (lpsztmp)
5383 lphttpHdr->lpszValue = lpsztmp;
5384 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5385 if (ch > 0)
5387 lphttpHdr->lpszValue[origlen] = ch;
5388 origlen++;
5389 lphttpHdr->lpszValue[origlen] = ' ';
5390 origlen++;
5393 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5394 lphttpHdr->lpszValue[len] = '\0';
5395 res = ERROR_SUCCESS;
5397 else
5399 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
5400 res = ERROR_OUTOFMEMORY;
5403 TRACE("<-- %d\n", res);
5404 return res;
5408 /***********************************************************************
5409 * HTTP_FinishedReading (internal)
5411 * Called when all content from server has been read by client.
5414 static BOOL HTTP_FinishedReading(http_request_t *request)
5416 BOOL keepalive = HTTP_KeepAlive(request);
5418 TRACE("\n");
5421 if (!keepalive)
5423 HTTPREQ_CloseConnection(&request->hdr);
5426 /* FIXME: store data in the URL cache here */
5428 return TRUE;
5432 /***********************************************************************
5433 * HTTP_GetCustomHeaderIndex (internal)
5435 * Return index of custom header from header array
5438 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
5439 int requested_index, BOOL request_only)
5441 DWORD index;
5443 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5445 for (index = 0; index < request->nCustHeaders; index++)
5447 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
5448 continue;
5450 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
5451 continue;
5453 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
5454 continue;
5456 if (requested_index == 0)
5457 break;
5458 requested_index --;
5461 if (index >= request->nCustHeaders)
5462 index = -1;
5464 TRACE("Return: %d\n", index);
5465 return index;
5469 /***********************************************************************
5470 * HTTP_InsertCustomHeader (internal)
5472 * Insert header into array
5475 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
5477 INT count;
5478 LPHTTPHEADERW lph = NULL;
5480 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5481 count = request->nCustHeaders + 1;
5482 if (count > 1)
5483 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, request->custHeaders, sizeof(HTTPHEADERW) * count);
5484 else
5485 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
5487 if (!lph)
5488 return ERROR_OUTOFMEMORY;
5490 request->custHeaders = lph;
5491 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
5492 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
5493 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
5494 request->custHeaders[count-1].wCount= lpHdr->wCount;
5495 request->nCustHeaders++;
5497 return ERROR_SUCCESS;
5501 /***********************************************************************
5502 * HTTP_DeleteCustomHeader (internal)
5504 * Delete header from array
5505 * If this function is called, the indexs may change.
5507 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
5509 if( request->nCustHeaders <= 0 )
5510 return FALSE;
5511 if( index >= request->nCustHeaders )
5512 return FALSE;
5513 request->nCustHeaders--;
5515 HeapFree(GetProcessHeap(), 0, request->custHeaders[index].lpszField);
5516 HeapFree(GetProcessHeap(), 0, request->custHeaders[index].lpszValue);
5518 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
5519 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
5520 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
5522 return TRUE;
5526 /***********************************************************************
5527 * HTTP_VerifyValidHeader (internal)
5529 * Verify the given header is not invalid for the given http request
5532 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
5534 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5535 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
5536 return ERROR_HTTP_INVALID_HEADER;
5538 return ERROR_SUCCESS;
5541 /***********************************************************************
5542 * IsHostInProxyBypassList (@)
5544 * Undocumented
5547 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
5549 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
5550 return FALSE;
5553 /***********************************************************************
5554 * InternetShowSecurityInfoByURLA (@)
5556 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
5558 FIXME("stub: %s %p\n", url, window);
5559 return FALSE;
5562 /***********************************************************************
5563 * InternetShowSecurityInfoByURLW (@)
5565 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
5567 FIXME("stub: %s %p\n", debugstr_w(url), window);
5568 return FALSE;