d3dcompiler: Move blob and strip functions to blob.c.
[wine.git] / dlls / wininet / http.c
blob5258c2e86b69499d10df68bf2196db7af5efdeb2
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
10 * Copyright 2011 Jacek Caban for CodeWeavers
12 * Ulrich Czekalla
13 * David Hammerton
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "config.h"
31 #include "wine/port.h"
33 #if defined(__MINGW32__) || defined (_MSC_VER)
34 #include <ws2tcpip.h>
35 #endif
37 #include <sys/types.h>
38 #ifdef HAVE_SYS_SOCKET_H
39 # include <sys/socket.h>
40 #endif
41 #ifdef HAVE_ARPA_INET_H
42 # include <arpa/inet.h>
43 #endif
44 #include <stdarg.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #ifdef HAVE_UNISTD_H
48 # include <unistd.h>
49 #endif
50 #include <time.h>
51 #include <assert.h>
52 #ifdef HAVE_ZLIB
53 # include <zlib.h>
54 #endif
56 #include "windef.h"
57 #include "winbase.h"
58 #include "wininet.h"
59 #include "winerror.h"
60 #include "winternl.h"
61 #define NO_SHLWAPI_STREAM
62 #define NO_SHLWAPI_REG
63 #define NO_SHLWAPI_STRFCNS
64 #define NO_SHLWAPI_GDI
65 #include "shlwapi.h"
66 #include "sspi.h"
67 #include "wincrypt.h"
69 #include "internet.h"
70 #include "wine/debug.h"
71 #include "wine/exception.h"
72 #include "wine/unicode.h"
74 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
76 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
77 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
78 static const WCHAR szOK[] = {'O','K',0};
79 static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
80 static const WCHAR hostW[] = { 'H','o','s','t',0 };
81 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
82 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
83 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
84 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
85 static const WCHAR szGET[] = { 'G','E','T', 0 };
86 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
87 static const WCHAR szCrLf[] = {'\r','\n', 0};
89 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
90 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
91 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
92 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
93 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
94 static const WCHAR szAge[] = { 'A','g','e',0 };
95 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
96 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
97 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
98 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
99 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
100 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
101 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
102 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
103 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
104 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
105 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
106 static const WCHAR szContent_Transfer_Encoding[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
107 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
108 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
109 static const WCHAR szDate[] = { 'D','a','t','e',0 };
110 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
111 static const WCHAR szETag[] = { 'E','T','a','g',0 };
112 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
113 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
114 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
115 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
116 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
117 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
118 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
119 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
120 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
121 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
122 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
123 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
124 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
125 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
126 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
127 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
128 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
129 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
130 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
131 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
132 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
133 static const WCHAR szUnless_Modified_Since[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
134 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
135 static const WCHAR szURI[] = { 'U','R','I',0 };
136 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
137 static const WCHAR szVary[] = { 'V','a','r','y',0 };
138 static const WCHAR szVia[] = { 'V','i','a',0 };
139 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
140 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
142 #define MAXHOSTNAME 100
143 #define MAX_FIELD_VALUE_LEN 256
144 #define MAX_FIELD_LEN 256
146 #define HTTP_REFERER szReferer
147 #define HTTP_ACCEPT szAccept
148 #define HTTP_USERAGENT szUser_Agent
150 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
151 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
152 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
153 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
154 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
155 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
156 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
158 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
160 struct HttpAuthInfo
162 LPWSTR scheme;
163 CredHandle cred;
164 CtxtHandle ctx;
165 TimeStamp exp;
166 ULONG attr;
167 ULONG max_token;
168 void *auth_data;
169 unsigned int auth_data_len;
170 BOOL finished; /* finished authenticating */
174 typedef struct _basicAuthorizationData
176 struct list entry;
178 LPWSTR host;
179 LPWSTR realm;
180 LPSTR authorization;
181 UINT authorizationLen;
182 } basicAuthorizationData;
184 typedef struct _authorizationData
186 struct list entry;
188 LPWSTR host;
189 LPWSTR scheme;
190 LPWSTR domain;
191 UINT domain_len;
192 LPWSTR user;
193 UINT user_len;
194 LPWSTR password;
195 UINT password_len;
196 } authorizationData;
198 static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
199 static struct list authorizationCache = LIST_INIT(authorizationCache);
201 static CRITICAL_SECTION authcache_cs;
202 static CRITICAL_SECTION_DEBUG critsect_debug =
204 0, 0, &authcache_cs,
205 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
206 0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
208 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
210 static DWORD HTTP_OpenConnection(http_request_t *req);
211 static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
212 static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
213 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
214 static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
215 static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
216 static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
217 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
218 static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
219 static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
220 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
221 static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
222 static void HTTP_DrainContent(http_request_t *req);
223 static BOOL HTTP_FinishedReading(http_request_t *req);
225 static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
227 int HeaderIndex = 0;
228 HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
229 if (HeaderIndex == -1)
230 return NULL;
231 else
232 return &req->custHeaders[HeaderIndex];
235 typedef enum {
236 READMODE_SYNC,
237 READMODE_ASYNC,
238 READMODE_NOBLOCK
239 } read_mode_t;
241 struct data_stream_vtbl_t {
242 DWORD (*get_avail_data)(data_stream_t*,http_request_t*);
243 BOOL (*end_of_data)(data_stream_t*,http_request_t*);
244 DWORD (*read)(data_stream_t*,http_request_t*,BYTE*,DWORD,DWORD*,read_mode_t);
245 void (*destroy)(data_stream_t*);
248 typedef struct {
249 data_stream_t data_stream;
251 BYTE buf[READ_BUFFER_SIZE];
252 DWORD buf_size;
253 DWORD buf_pos;
254 DWORD chunk_size;
255 } chunked_stream_t;
257 static void inline destroy_data_stream(data_stream_t *stream)
259 stream->vtbl->destroy(stream);
262 static void reset_data_stream(http_request_t *req)
264 destroy_data_stream(req->data_stream);
265 req->data_stream = &req->netconn_stream.data_stream;
266 req->read_pos = req->read_size = req->netconn_stream.content_read = 0;
267 req->read_chunked = req->read_gzip = FALSE;
270 #ifdef HAVE_ZLIB
272 typedef struct {
273 data_stream_t stream;
274 data_stream_t *parent_stream;
275 z_stream zstream;
276 BYTE buf[READ_BUFFER_SIZE];
277 DWORD buf_size;
278 DWORD buf_pos;
279 BOOL end_of_data;
280 } gzip_stream_t;
282 static DWORD gzip_get_avail_data(data_stream_t *stream, http_request_t *req)
284 /* Allow reading only from read buffer */
285 return 0;
288 static BOOL gzip_end_of_data(data_stream_t *stream, http_request_t *req)
290 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
291 return gzip_stream->end_of_data;
294 static DWORD gzip_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
295 DWORD *read, read_mode_t read_mode)
297 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
298 z_stream *zstream = &gzip_stream->zstream;
299 DWORD current_read, ret_read = 0;
300 BOOL end;
301 int zres;
302 DWORD res = ERROR_SUCCESS;
304 while(size && !gzip_stream->end_of_data) {
305 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
307 if(gzip_stream->buf_size <= 64 && !end) {
308 if(gzip_stream->buf_pos) {
309 if(gzip_stream->buf_size)
310 memmove(gzip_stream->buf, gzip_stream->buf+gzip_stream->buf_pos, gzip_stream->buf_size);
311 gzip_stream->buf_pos = 0;
313 res = gzip_stream->parent_stream->vtbl->read(gzip_stream->parent_stream, req, gzip_stream->buf+gzip_stream->buf_size,
314 sizeof(gzip_stream->buf)-gzip_stream->buf_size, &current_read, read_mode);
315 gzip_stream->buf_size += current_read;
316 if(res != ERROR_SUCCESS)
317 break;
318 end = gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req);
319 if(!current_read && !end) {
320 if(read_mode != READMODE_NOBLOCK) {
321 WARN("unexpected end of data\n");
322 gzip_stream->end_of_data = TRUE;
324 break;
326 if(gzip_stream->buf_size <= 64 && !end)
327 continue;
330 zstream->next_in = gzip_stream->buf+gzip_stream->buf_pos;
331 zstream->avail_in = gzip_stream->buf_size-(end ? 0 : 64);
332 zstream->next_out = buf+ret_read;
333 zstream->avail_out = size;
334 zres = inflate(&gzip_stream->zstream, 0);
335 current_read = size - zstream->avail_out;
336 size -= current_read;
337 ret_read += current_read;
338 gzip_stream->buf_size -= zstream->next_in - (gzip_stream->buf+gzip_stream->buf_pos);
339 gzip_stream->buf_pos = zstream->next_in-gzip_stream->buf;
340 if(zres == Z_STREAM_END) {
341 TRACE("end of data\n");
342 gzip_stream->end_of_data = TRUE;
343 inflateEnd(zstream);
344 }else if(zres != Z_OK) {
345 WARN("inflate failed %d: %s\n", zres, debugstr_a(zstream->msg));
346 if(!ret_read)
347 res = ERROR_INTERNET_DECODING_FAILED;
348 break;
351 if(ret_read && read_mode == READMODE_ASYNC)
352 read_mode = READMODE_NOBLOCK;
355 TRACE("read %u bytes\n", ret_read);
356 *read = ret_read;
357 return res;
360 static void gzip_destroy(data_stream_t *stream)
362 gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
364 destroy_data_stream(gzip_stream->parent_stream);
366 if(!gzip_stream->end_of_data)
367 inflateEnd(&gzip_stream->zstream);
368 heap_free(gzip_stream);
371 static const data_stream_vtbl_t gzip_stream_vtbl = {
372 gzip_get_avail_data,
373 gzip_end_of_data,
374 gzip_read,
375 gzip_destroy
378 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
380 return HeapAlloc(GetProcessHeap(), 0, items*size);
383 static void wininet_zfree(voidpf opaque, voidpf address)
385 HeapFree(GetProcessHeap(), 0, address);
388 static DWORD init_gzip_stream(http_request_t *req)
390 gzip_stream_t *gzip_stream;
391 int index, zres;
393 gzip_stream = heap_alloc_zero(sizeof(gzip_stream_t));
394 if(!gzip_stream)
395 return ERROR_OUTOFMEMORY;
397 gzip_stream->stream.vtbl = &gzip_stream_vtbl;
398 gzip_stream->zstream.zalloc = wininet_zalloc;
399 gzip_stream->zstream.zfree = wininet_zfree;
401 zres = inflateInit2(&gzip_stream->zstream, 0x1f);
402 if(zres != Z_OK) {
403 ERR("inflateInit failed: %d\n", zres);
404 HeapFree(GetProcessHeap(), 0, gzip_stream);
405 return ERROR_OUTOFMEMORY;
408 index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
409 if(index != -1)
410 HTTP_DeleteCustomHeader(req, index);
412 if(req->read_size) {
413 memcpy(gzip_stream->buf, req->read_buf+req->read_pos, req->read_size);
414 gzip_stream->buf_size = req->read_size;
415 req->read_pos = req->read_size = 0;
418 req->read_gzip = TRUE;
419 gzip_stream->parent_stream = req->data_stream;
420 req->data_stream = &gzip_stream->stream;
421 return ERROR_SUCCESS;
424 #else
426 static DWORD init_gzip_stream(http_request_t *req)
428 ERR("gzip stream not supported, missing zlib.\n");
429 return ERROR_SUCCESS;
432 #endif
434 /***********************************************************************
435 * HTTP_Tokenize (internal)
437 * Tokenize a string, allocating memory for the tokens.
439 static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
441 LPWSTR * token_array;
442 int tokens = 0;
443 int i;
444 LPCWSTR next_token;
446 if (string)
448 /* empty string has no tokens */
449 if (*string)
450 tokens++;
451 /* count tokens */
452 for (i = 0; string[i]; i++)
454 if (!strncmpW(string+i, token_string, strlenW(token_string)))
456 DWORD j;
457 tokens++;
458 /* we want to skip over separators, but not the null terminator */
459 for (j = 0; j < strlenW(token_string) - 1; j++)
460 if (!string[i+j])
461 break;
462 i += j;
467 /* add 1 for terminating NULL */
468 token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
469 token_array[tokens] = NULL;
470 if (!tokens)
471 return token_array;
472 for (i = 0; i < tokens; i++)
474 int len;
475 next_token = strstrW(string, token_string);
476 if (!next_token) next_token = string+strlenW(string);
477 len = next_token - string;
478 token_array[i] = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
479 memcpy(token_array[i], string, len*sizeof(WCHAR));
480 token_array[i][len] = '\0';
481 string = next_token+strlenW(token_string);
483 return token_array;
486 /***********************************************************************
487 * HTTP_FreeTokens (internal)
489 * Frees memory returned from HTTP_Tokenize.
491 static void HTTP_FreeTokens(LPWSTR * token_array)
493 int i;
494 for (i = 0; token_array[i]; i++)
495 HeapFree(GetProcessHeap(), 0, token_array[i]);
496 HeapFree(GetProcessHeap(), 0, token_array);
499 static void HTTP_FixURL(http_request_t *request)
501 static const WCHAR szSlash[] = { '/',0 };
502 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
504 /* If we don't have a path we set it to root */
505 if (NULL == request->path)
506 request->path = heap_strdupW(szSlash);
507 else /* remove \r and \n*/
509 int nLen = strlenW(request->path);
510 while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n')))
512 nLen--;
513 request->path[nLen]='\0';
515 /* Replace '\' with '/' */
516 while (nLen>0) {
517 nLen--;
518 if (request->path[nLen] == '\\') request->path[nLen]='/';
522 if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
523 request->path, strlenW(request->path), szHttp, strlenW(szHttp) )
524 && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */
526 WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
527 (strlenW(request->path) + 2)*sizeof(WCHAR));
528 *fixurl = '/';
529 strcpyW(fixurl + 1, request->path);
530 HeapFree( GetProcessHeap(), 0, request->path );
531 request->path = fixurl;
535 static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *request, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
537 LPWSTR requestString;
538 DWORD len, n;
539 LPCWSTR *req;
540 UINT i;
541 LPWSTR p;
543 static const WCHAR szSpace[] = { ' ',0 };
544 static const WCHAR szColon[] = { ':',' ',0 };
545 static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
547 /* allocate space for an array of all the string pointers to be added */
548 len = (request->nCustHeaders)*4 + 10;
549 req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
551 /* add the verb, path and HTTP version string */
552 n = 0;
553 req[n++] = verb;
554 req[n++] = szSpace;
555 req[n++] = path;
556 req[n++] = szSpace;
557 req[n++] = version;
559 /* Append custom request headers */
560 for (i = 0; i < request->nCustHeaders; i++)
562 if (request->custHeaders[i].wFlags & HDR_ISREQUEST)
564 req[n++] = szCrLf;
565 req[n++] = request->custHeaders[i].lpszField;
566 req[n++] = szColon;
567 req[n++] = request->custHeaders[i].lpszValue;
569 TRACE("Adding custom header %s (%s)\n",
570 debugstr_w(request->custHeaders[i].lpszField),
571 debugstr_w(request->custHeaders[i].lpszValue));
575 if( n >= len )
576 ERR("oops. buffer overrun\n");
578 req[n] = NULL;
579 requestString = HTTP_build_req( req, 4 );
580 HeapFree( GetProcessHeap(), 0, req );
583 * Set (header) termination string for request
584 * Make sure there's exactly two new lines at the end of the request
586 p = &requestString[strlenW(requestString)-1];
587 while ( (*p == '\n') || (*p == '\r') )
588 p--;
589 strcpyW( p+1, sztwocrlf );
591 return requestString;
594 static void HTTP_ProcessCookies( http_request_t *request )
596 int HeaderIndex;
597 int numCookies = 0;
598 LPHTTPHEADERW setCookieHeader;
600 while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, szSet_Cookie, numCookies, FALSE)) != -1)
602 setCookieHeader = &request->custHeaders[HeaderIndex];
604 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
606 int len;
607 static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
608 LPWSTR buf_url;
609 LPHTTPHEADERW Host;
611 Host = HTTP_GetHeader(request, hostW);
612 len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(request->path);
613 buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
614 sprintfW(buf_url, szFmt, Host->lpszValue, request->path);
615 InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
617 HeapFree(GetProcessHeap(), 0, buf_url);
619 numCookies++;
623 static void strip_spaces(LPWSTR start)
625 LPWSTR str = start;
626 LPWSTR end;
628 while (*str == ' ' && *str != '\0')
629 str++;
631 if (str != start)
632 memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
634 end = start + strlenW(start) - 1;
635 while (end >= start && *end == ' ')
637 *end = '\0';
638 end--;
642 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
644 static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
645 static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
646 BOOL is_basic;
647 is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
648 ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
649 if (is_basic && pszRealm)
651 LPCWSTR token;
652 LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
653 LPCWSTR realm;
654 ptr++;
655 *pszRealm=NULL;
656 token = strchrW(ptr,'=');
657 if (!token)
658 return TRUE;
659 realm = ptr;
660 while (*realm == ' ' && *realm != '\0')
661 realm++;
662 if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
663 (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
665 token++;
666 while (*token == ' ' && *token != '\0')
667 token++;
668 if (*token == '\0')
669 return TRUE;
670 *pszRealm = heap_strdupW(token);
671 strip_spaces(*pszRealm);
675 return is_basic;
678 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
680 if (!authinfo) return;
682 if (SecIsValidHandle(&authinfo->ctx))
683 DeleteSecurityContext(&authinfo->ctx);
684 if (SecIsValidHandle(&authinfo->cred))
685 FreeCredentialsHandle(&authinfo->cred);
687 HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
688 HeapFree(GetProcessHeap(), 0, authinfo->scheme);
689 HeapFree(GetProcessHeap(), 0, authinfo);
692 static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
694 basicAuthorizationData *ad;
695 UINT rc = 0;
697 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
699 EnterCriticalSection(&authcache_cs);
700 LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, basicAuthorizationData, entry)
702 if (!strcmpiW(host,ad->host) && !strcmpW(realm,ad->realm))
704 TRACE("Authorization found in cache\n");
705 *auth_data = HeapAlloc(GetProcessHeap(),0,ad->authorizationLen);
706 memcpy(*auth_data,ad->authorization,ad->authorizationLen);
707 rc = ad->authorizationLen;
708 break;
711 LeaveCriticalSection(&authcache_cs);
712 return rc;
715 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
717 struct list *cursor;
718 basicAuthorizationData* ad = NULL;
720 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
722 EnterCriticalSection(&authcache_cs);
723 LIST_FOR_EACH(cursor, &basicAuthorizationCache)
725 basicAuthorizationData *check = LIST_ENTRY(cursor,basicAuthorizationData,entry);
726 if (!strcmpiW(host,check->host) && !strcmpW(realm,check->realm))
728 ad = check;
729 break;
733 if (ad)
735 TRACE("Found match in cache, replacing\n");
736 HeapFree(GetProcessHeap(),0,ad->authorization);
737 ad->authorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
738 memcpy(ad->authorization, auth_data, auth_data_len);
739 ad->authorizationLen = auth_data_len;
741 else
743 ad = HeapAlloc(GetProcessHeap(),0,sizeof(basicAuthorizationData));
744 ad->host = heap_strdupW(host);
745 ad->host = heap_strdupW(realm);
746 ad->authorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
747 memcpy(ad->authorization, auth_data, auth_data_len);
748 ad->authorizationLen = auth_data_len;
749 list_add_head(&basicAuthorizationCache,&ad->entry);
750 TRACE("authorization cached\n");
752 LeaveCriticalSection(&authcache_cs);
755 static BOOL retrieve_cached_authorization(LPWSTR host, LPWSTR scheme,
756 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
758 authorizationData *ad;
760 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
762 EnterCriticalSection(&authcache_cs);
763 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry) {
764 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
765 TRACE("Authorization found in cache\n");
767 nt_auth_identity->User = heap_strdupW(ad->user);
768 nt_auth_identity->Password = heap_strdupW(ad->password);
769 nt_auth_identity->Domain = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*ad->domain_len);
770 if(!nt_auth_identity->User || !nt_auth_identity->Password ||
771 (!nt_auth_identity->Domain && ad->domain_len)) {
772 HeapFree(GetProcessHeap(), 0, nt_auth_identity->User);
773 HeapFree(GetProcessHeap(), 0, nt_auth_identity->Password);
774 HeapFree(GetProcessHeap(), 0, nt_auth_identity->Domain);
775 break;
778 nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
779 nt_auth_identity->UserLength = ad->user_len;
780 nt_auth_identity->PasswordLength = ad->password_len;
781 memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
782 nt_auth_identity->DomainLength = ad->domain_len;
783 LeaveCriticalSection(&authcache_cs);
784 return TRUE;
787 LeaveCriticalSection(&authcache_cs);
789 return FALSE;
792 static void cache_authorization(LPWSTR host, LPWSTR scheme,
793 SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
795 authorizationData *ad;
796 BOOL found = FALSE;
798 TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
800 EnterCriticalSection(&authcache_cs);
801 LIST_FOR_EACH_ENTRY(ad, &authorizationCache, authorizationData, entry)
802 if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
803 found = TRUE;
804 break;
807 if(found) {
808 HeapFree(GetProcessHeap(), 0, ad->user);
809 HeapFree(GetProcessHeap(), 0, ad->password);
810 HeapFree(GetProcessHeap(), 0, ad->domain);
811 } else {
812 ad = HeapAlloc(GetProcessHeap(), 0, sizeof(authorizationData));
813 if(!ad) {
814 LeaveCriticalSection(&authcache_cs);
815 return;
818 ad->host = heap_strdupW(host);
819 ad->scheme = heap_strdupW(scheme);
820 list_add_head(&authorizationCache, &ad->entry);
823 ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
824 ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
825 ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
826 ad->user_len = nt_auth_identity->UserLength;
827 ad->password_len = nt_auth_identity->PasswordLength;
828 ad->domain_len = nt_auth_identity->DomainLength;
830 if(!ad->host || !ad->scheme || !ad->user || !ad->password
831 || (nt_auth_identity->Domain && !ad->domain)) {
832 HeapFree(GetProcessHeap(), 0, ad->host);
833 HeapFree(GetProcessHeap(), 0, ad->scheme);
834 HeapFree(GetProcessHeap(), 0, ad->user);
835 HeapFree(GetProcessHeap(), 0, ad->password);
836 HeapFree(GetProcessHeap(), 0, ad->domain);
837 list_remove(&ad->entry);
838 HeapFree(GetProcessHeap(), 0, ad);
841 LeaveCriticalSection(&authcache_cs);
844 static BOOL HTTP_DoAuthorization( http_request_t *request, LPCWSTR pszAuthValue,
845 struct HttpAuthInfo **ppAuthInfo,
846 LPWSTR domain_and_username, LPWSTR password,
847 LPWSTR host )
849 SECURITY_STATUS sec_status;
850 struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
851 BOOL first = FALSE;
852 LPWSTR szRealm = NULL;
854 TRACE("%s\n", debugstr_w(pszAuthValue));
856 if (!pAuthInfo)
858 TimeStamp exp;
860 first = TRUE;
861 pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
862 if (!pAuthInfo)
863 return FALSE;
865 SecInvalidateHandle(&pAuthInfo->cred);
866 SecInvalidateHandle(&pAuthInfo->ctx);
867 memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
868 pAuthInfo->attr = 0;
869 pAuthInfo->auth_data = NULL;
870 pAuthInfo->auth_data_len = 0;
871 pAuthInfo->finished = FALSE;
873 if (is_basic_auth_value(pszAuthValue,NULL))
875 static const WCHAR szBasic[] = {'B','a','s','i','c',0};
876 pAuthInfo->scheme = heap_strdupW(szBasic);
877 if (!pAuthInfo->scheme)
879 HeapFree(GetProcessHeap(), 0, pAuthInfo);
880 return FALSE;
883 else
885 PVOID pAuthData;
886 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
888 pAuthInfo->scheme = heap_strdupW(pszAuthValue);
889 if (!pAuthInfo->scheme)
891 HeapFree(GetProcessHeap(), 0, pAuthInfo);
892 return FALSE;
895 if (domain_and_username)
897 WCHAR *user = strchrW(domain_and_username, '\\');
898 WCHAR *domain = domain_and_username;
900 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
902 pAuthData = &nt_auth_identity;
904 if (user) user++;
905 else
907 user = domain_and_username;
908 domain = NULL;
911 nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
912 nt_auth_identity.User = user;
913 nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
914 nt_auth_identity.Domain = domain;
915 nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
916 nt_auth_identity.Password = password;
917 nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
919 cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
921 else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
922 pAuthData = &nt_auth_identity;
923 else
924 /* use default credentials */
925 pAuthData = NULL;
927 sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
928 SECPKG_CRED_OUTBOUND, NULL,
929 pAuthData, NULL,
930 NULL, &pAuthInfo->cred,
931 &exp);
933 if(pAuthData && !domain_and_username) {
934 HeapFree(GetProcessHeap(), 0, nt_auth_identity.User);
935 HeapFree(GetProcessHeap(), 0, nt_auth_identity.Domain);
936 HeapFree(GetProcessHeap(), 0, nt_auth_identity.Password);
939 if (sec_status == SEC_E_OK)
941 PSecPkgInfoW sec_pkg_info;
942 sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
943 if (sec_status == SEC_E_OK)
945 pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
946 FreeContextBuffer(sec_pkg_info);
949 if (sec_status != SEC_E_OK)
951 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
952 debugstr_w(pAuthInfo->scheme), sec_status);
953 HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
954 HeapFree(GetProcessHeap(), 0, pAuthInfo);
955 return FALSE;
958 *ppAuthInfo = pAuthInfo;
960 else if (pAuthInfo->finished)
961 return FALSE;
963 if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
964 strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
966 ERR("authentication scheme changed from %s to %s\n",
967 debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
968 return FALSE;
971 if (is_basic_auth_value(pszAuthValue,&szRealm))
973 int userlen;
974 int passlen;
975 char *auth_data = NULL;
976 UINT auth_data_len = 0;
978 TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
980 if (!domain_and_username)
982 if (host && szRealm)
983 auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
984 if (auth_data_len == 0)
986 HeapFree(GetProcessHeap(),0,szRealm);
987 return FALSE;
990 else
992 userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
993 passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
995 /* length includes a nul terminator, which will be re-used for the ':' */
996 auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
997 if (!auth_data)
999 HeapFree(GetProcessHeap(),0,szRealm);
1000 return FALSE;
1003 WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
1004 auth_data[userlen] = ':';
1005 WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
1006 auth_data_len = userlen + 1 + passlen;
1007 if (host && szRealm)
1008 cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
1011 pAuthInfo->auth_data = auth_data;
1012 pAuthInfo->auth_data_len = auth_data_len;
1013 pAuthInfo->finished = TRUE;
1014 HeapFree(GetProcessHeap(),0,szRealm);
1016 return TRUE;
1018 else
1020 LPCWSTR pszAuthData;
1021 SecBufferDesc out_desc, in_desc;
1022 SecBuffer out, in;
1023 unsigned char *buffer;
1024 ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
1025 ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
1027 in.BufferType = SECBUFFER_TOKEN;
1028 in.cbBuffer = 0;
1029 in.pvBuffer = NULL;
1031 in_desc.ulVersion = 0;
1032 in_desc.cBuffers = 1;
1033 in_desc.pBuffers = &in;
1035 pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
1036 if (*pszAuthData == ' ')
1038 pszAuthData++;
1039 in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
1040 in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
1041 HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
1044 buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
1046 out.BufferType = SECBUFFER_TOKEN;
1047 out.cbBuffer = pAuthInfo->max_token;
1048 out.pvBuffer = buffer;
1050 out_desc.ulVersion = 0;
1051 out_desc.cBuffers = 1;
1052 out_desc.pBuffers = &out;
1054 sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
1055 first ? NULL : &pAuthInfo->ctx,
1056 first ? request->session->serverName : NULL,
1057 context_req, 0, SECURITY_NETWORK_DREP,
1058 in.pvBuffer ? &in_desc : NULL,
1059 0, &pAuthInfo->ctx, &out_desc,
1060 &pAuthInfo->attr, &pAuthInfo->exp);
1061 if (sec_status == SEC_E_OK)
1063 pAuthInfo->finished = TRUE;
1064 pAuthInfo->auth_data = out.pvBuffer;
1065 pAuthInfo->auth_data_len = out.cbBuffer;
1066 TRACE("sending last auth packet\n");
1068 else if (sec_status == SEC_I_CONTINUE_NEEDED)
1070 pAuthInfo->auth_data = out.pvBuffer;
1071 pAuthInfo->auth_data_len = out.cbBuffer;
1072 TRACE("sending next auth packet\n");
1074 else
1076 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
1077 HeapFree(GetProcessHeap(), 0, out.pvBuffer);
1078 destroy_authinfo(pAuthInfo);
1079 *ppAuthInfo = NULL;
1080 return FALSE;
1084 return TRUE;
1087 /***********************************************************************
1088 * HTTP_HttpAddRequestHeadersW (internal)
1090 static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *request,
1091 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1093 LPWSTR lpszStart;
1094 LPWSTR lpszEnd;
1095 LPWSTR buffer;
1096 DWORD len, res = ERROR_HTTP_INVALID_HEADER;
1098 TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
1100 if( dwHeaderLength == ~0U )
1101 len = strlenW(lpszHeader);
1102 else
1103 len = dwHeaderLength;
1104 buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
1105 lstrcpynW( buffer, lpszHeader, len + 1);
1107 lpszStart = buffer;
1111 LPWSTR * pFieldAndValue;
1113 lpszEnd = lpszStart;
1115 while (*lpszEnd != '\0')
1117 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1118 break;
1119 lpszEnd++;
1122 if (*lpszStart == '\0')
1123 break;
1125 if (*lpszEnd == '\r' || *lpszEnd == '\n')
1127 *lpszEnd = '\0';
1128 lpszEnd++; /* Jump over newline */
1130 TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1131 if (*lpszStart == '\0')
1133 /* Skip 0-length headers */
1134 lpszStart = lpszEnd;
1135 res = ERROR_SUCCESS;
1136 continue;
1138 pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1139 if (pFieldAndValue)
1141 res = HTTP_VerifyValidHeader(request, pFieldAndValue[0]);
1142 if (res == ERROR_SUCCESS)
1143 res = HTTP_ProcessHeader(request, pFieldAndValue[0],
1144 pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1145 HTTP_FreeTokens(pFieldAndValue);
1148 lpszStart = lpszEnd;
1149 } while (res == ERROR_SUCCESS);
1151 HeapFree(GetProcessHeap(), 0, buffer);
1153 return res;
1156 /***********************************************************************
1157 * HttpAddRequestHeadersW (WININET.@)
1159 * Adds one or more HTTP header to the request handler
1161 * NOTE
1162 * On Windows if dwHeaderLength includes the trailing '\0', then
1163 * HttpAddRequestHeadersW() adds it too. However this results in an
1164 * invalid Http header which is rejected by some servers so we probably
1165 * don't need to match Windows on that point.
1167 * RETURNS
1168 * TRUE on success
1169 * FALSE on failure
1172 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
1173 LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1175 http_request_t *request;
1176 DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1178 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1180 if (!lpszHeader)
1181 return TRUE;
1183 request = (http_request_t*) get_handle_object( hHttpRequest );
1184 if (request && request->hdr.htype == WH_HHTTPREQ)
1185 res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier );
1186 if( request )
1187 WININET_Release( &request->hdr );
1189 if(res != ERROR_SUCCESS)
1190 SetLastError(res);
1191 return res == ERROR_SUCCESS;
1194 /***********************************************************************
1195 * HttpAddRequestHeadersA (WININET.@)
1197 * Adds one or more HTTP header to the request handler
1199 * RETURNS
1200 * TRUE on success
1201 * FALSE on failure
1204 BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
1205 LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1207 DWORD len;
1208 LPWSTR hdr;
1209 BOOL r;
1211 TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1213 len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
1214 hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
1215 MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
1216 if( dwHeaderLength != ~0U )
1217 dwHeaderLength = len;
1219 r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
1221 HeapFree( GetProcessHeap(), 0, hdr );
1223 return r;
1226 /***********************************************************************
1227 * HttpOpenRequestA (WININET.@)
1229 * Open a HTTP request handle
1231 * RETURNS
1232 * HINTERNET a HTTP request handle on success
1233 * NULL on failure
1236 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
1237 LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1238 LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1239 DWORD dwFlags, DWORD_PTR dwContext)
1241 LPWSTR szVerb = NULL, szObjectName = NULL;
1242 LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1243 INT acceptTypesCount;
1244 HINTERNET rc = FALSE;
1245 LPCSTR *types;
1247 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1248 debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1249 debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1250 dwFlags, dwContext);
1252 if (lpszVerb)
1254 szVerb = heap_strdupAtoW(lpszVerb);
1255 if ( !szVerb )
1256 goto end;
1259 if (lpszObjectName)
1261 szObjectName = heap_strdupAtoW(lpszObjectName);
1262 if ( !szObjectName )
1263 goto end;
1266 if (lpszVersion)
1268 szVersion = heap_strdupAtoW(lpszVersion);
1269 if ( !szVersion )
1270 goto end;
1273 if (lpszReferrer)
1275 szReferrer = heap_strdupAtoW(lpszReferrer);
1276 if ( !szReferrer )
1277 goto end;
1280 if (lpszAcceptTypes)
1282 acceptTypesCount = 0;
1283 types = lpszAcceptTypes;
1284 while (*types)
1286 __TRY
1288 /* find out how many there are */
1289 if (*types && **types)
1291 TRACE("accept type: %s\n", debugstr_a(*types));
1292 acceptTypesCount++;
1295 __EXCEPT_PAGE_FAULT
1297 WARN("invalid accept type pointer\n");
1299 __ENDTRY;
1300 types++;
1302 szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
1303 if (!szAcceptTypes) goto end;
1305 acceptTypesCount = 0;
1306 types = lpszAcceptTypes;
1307 while (*types)
1309 __TRY
1311 if (*types && **types)
1312 szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
1314 __EXCEPT_PAGE_FAULT
1316 /* ignore invalid pointer */
1318 __ENDTRY;
1319 types++;
1321 szAcceptTypes[acceptTypesCount] = NULL;
1324 rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
1325 szVersion, szReferrer,
1326 (LPCWSTR*)szAcceptTypes, dwFlags, dwContext);
1328 end:
1329 if (szAcceptTypes)
1331 acceptTypesCount = 0;
1332 while (szAcceptTypes[acceptTypesCount])
1334 HeapFree(GetProcessHeap(), 0, szAcceptTypes[acceptTypesCount]);
1335 acceptTypesCount++;
1337 HeapFree(GetProcessHeap(), 0, szAcceptTypes);
1339 HeapFree(GetProcessHeap(), 0, szReferrer);
1340 HeapFree(GetProcessHeap(), 0, szVersion);
1341 HeapFree(GetProcessHeap(), 0, szObjectName);
1342 HeapFree(GetProcessHeap(), 0, szVerb);
1344 return rc;
1347 /***********************************************************************
1348 * HTTP_EncodeBase64
1350 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1352 UINT n = 0, x;
1353 static const CHAR HTTP_Base64Enc[] =
1354 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1356 while( len > 0 )
1358 /* first 6 bits, all from bin[0] */
1359 base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1360 x = (bin[0] & 3) << 4;
1362 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1363 if( len == 1 )
1365 base64[n++] = HTTP_Base64Enc[x];
1366 base64[n++] = '=';
1367 base64[n++] = '=';
1368 break;
1370 base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1371 x = ( bin[1] & 0x0f ) << 2;
1373 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1374 if( len == 2 )
1376 base64[n++] = HTTP_Base64Enc[x];
1377 base64[n++] = '=';
1378 break;
1380 base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1382 /* last 6 bits, all from bin [2] */
1383 base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1384 bin += 3;
1385 len -= 3;
1387 base64[n] = 0;
1388 return n;
1391 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1392 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1393 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1394 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1395 static const signed char HTTP_Base64Dec[256] =
1397 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1398 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1399 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1400 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1401 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1402 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1403 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1404 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1405 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1406 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1407 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1408 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1409 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1410 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1411 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1412 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1413 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1414 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1415 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1416 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1417 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1418 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1419 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1420 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1421 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1422 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1424 #undef CH
1426 /***********************************************************************
1427 * HTTP_DecodeBase64
1429 static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
1431 unsigned int n = 0;
1433 while(*base64)
1435 signed char in[4];
1437 if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
1438 ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1439 base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
1440 ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1442 WARN("invalid base64: %s\n", debugstr_w(base64));
1443 return 0;
1445 if (bin)
1446 bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1447 n++;
1449 if ((base64[2] == '=') && (base64[3] == '='))
1450 break;
1451 if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
1452 ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1454 WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1455 return 0;
1457 if (bin)
1458 bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1459 n++;
1461 if (base64[3] == '=')
1462 break;
1463 if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
1464 ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1466 WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1467 return 0;
1469 if (bin)
1470 bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1471 n++;
1473 base64 += 4;
1476 return n;
1479 /***********************************************************************
1480 * HTTP_InsertAuthorization
1482 * Insert or delete the authorization field in the request header.
1484 static BOOL HTTP_InsertAuthorization( http_request_t *request, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
1486 if (pAuthInfo)
1488 static const WCHAR wszSpace[] = {' ',0};
1489 static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1490 unsigned int len;
1491 WCHAR *authorization = NULL;
1493 if (pAuthInfo->auth_data_len)
1495 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1496 len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
1497 authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
1498 if (!authorization)
1499 return FALSE;
1501 strcpyW(authorization, pAuthInfo->scheme);
1502 strcatW(authorization, wszSpace);
1503 HTTP_EncodeBase64(pAuthInfo->auth_data,
1504 pAuthInfo->auth_data_len,
1505 authorization+strlenW(authorization));
1507 /* clear the data as it isn't valid now that it has been sent to the
1508 * server, unless it's Basic authentication which doesn't do
1509 * connection tracking */
1510 if (strcmpiW(pAuthInfo->scheme, wszBasic))
1512 HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
1513 pAuthInfo->auth_data = NULL;
1514 pAuthInfo->auth_data_len = 0;
1518 TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1520 HTTP_ProcessHeader(request, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
1522 HeapFree(GetProcessHeap(), 0, authorization);
1524 return TRUE;
1527 static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
1529 WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
1530 DWORD size;
1532 size = sizeof(new_location);
1533 if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
1535 if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
1536 strcpyW( url, new_location );
1538 else
1540 static const WCHAR slash[] = { '/',0 };
1541 static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','u',0 };
1542 static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','u',0 };
1543 http_session_t *session = req->session;
1545 size = 16; /* "https://" + sizeof(port#) + ":/\0" */
1546 size += strlenW( session->hostName ) + strlenW( req->path );
1548 if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
1550 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1551 sprintfW( url, formatSSL, session->hostName, session->hostPort );
1552 else
1553 sprintfW( url, format, session->hostName, session->hostPort );
1554 if (req->path[0] != '/') strcatW( url, slash );
1555 strcatW( url, req->path );
1557 TRACE("url=%s\n", debugstr_w(url));
1558 return url;
1561 /***********************************************************************
1562 * HTTP_DealWithProxy
1564 static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_request_t *request)
1566 WCHAR buf[MAXHOSTNAME];
1567 WCHAR protoProxy[MAXHOSTNAME + 15];
1568 DWORD protoProxyLen = sizeof(protoProxy) / sizeof(protoProxy[0]);
1569 WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
1570 static WCHAR szNul[] = { 0 };
1571 URL_COMPONENTSW UrlComponents;
1572 static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1573 static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1574 static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
1576 memset( &UrlComponents, 0, sizeof UrlComponents );
1577 UrlComponents.dwStructSize = sizeof UrlComponents;
1578 UrlComponents.lpszHostName = buf;
1579 UrlComponents.dwHostNameLength = MAXHOSTNAME;
1581 if (!INTERNET_FindProxyForProtocol(hIC->proxy, protoHttp, protoProxy, &protoProxyLen))
1582 return FALSE;
1583 if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
1584 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
1585 sprintfW(proxy, szFormat, protoProxy);
1586 else
1587 strcpyW(proxy, protoProxy);
1588 if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
1589 return FALSE;
1590 if( UrlComponents.dwHostNameLength == 0 )
1591 return FALSE;
1593 if( !request->path )
1594 request->path = szNul;
1596 if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
1597 UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1599 HeapFree(GetProcessHeap(), 0, session->serverName);
1600 session->serverName = heap_strdupW(UrlComponents.lpszHostName);
1601 session->serverPort = UrlComponents.nPort;
1603 TRACE("proxy server=%s port=%d\n", debugstr_w(session->serverName), session->serverPort);
1604 return TRUE;
1607 #ifndef INET6_ADDRSTRLEN
1608 #define INET6_ADDRSTRLEN 46
1609 #endif
1611 static DWORD HTTP_ResolveName(http_request_t *request)
1613 char szaddr[INET6_ADDRSTRLEN];
1614 http_session_t *session = request->session;
1615 const void *addr;
1617 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1618 INTERNET_STATUS_RESOLVING_NAME,
1619 session->serverName,
1620 (strlenW(session->serverName)+1) * sizeof(WCHAR));
1622 session->sa_len = sizeof(session->socketAddress);
1623 if (!GetAddress(session->serverName, session->serverPort,
1624 (struct sockaddr *)&session->socketAddress, &session->sa_len))
1625 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1627 switch (session->socketAddress.ss_family)
1629 case AF_INET:
1630 addr = &((struct sockaddr_in *)&session->socketAddress)->sin_addr;
1631 break;
1632 case AF_INET6:
1633 addr = &((struct sockaddr_in6 *)&session->socketAddress)->sin6_addr;
1634 break;
1635 default:
1636 WARN("unsupported family %d\n", session->socketAddress.ss_family);
1637 return ERROR_INTERNET_NAME_NOT_RESOLVED;
1639 inet_ntop(session->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
1640 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1641 INTERNET_STATUS_NAME_RESOLVED,
1642 szaddr, strlen(szaddr)+1);
1644 TRACE("resolved %s to %s\n", debugstr_w(session->serverName), szaddr);
1645 return ERROR_SUCCESS;
1648 static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
1650 static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
1651 static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
1652 static const WCHAR slash[] = { '/',0 };
1653 LPHTTPHEADERW host_header;
1654 LPCWSTR scheme;
1656 host_header = HTTP_GetHeader(req, hostW);
1657 if(!host_header)
1658 return FALSE;
1660 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1661 scheme = https;
1662 else
1663 scheme = http;
1664 strcpyW(buf, scheme);
1665 strcatW(buf, host_header->lpszValue);
1666 if (req->path[0] != '/')
1667 strcatW(buf, slash);
1668 strcatW(buf, req->path);
1669 return TRUE;
1673 /***********************************************************************
1674 * HTTPREQ_Destroy (internal)
1676 * Deallocate request handle
1679 static void HTTPREQ_Destroy(object_header_t *hdr)
1681 http_request_t *request = (http_request_t*) hdr;
1682 DWORD i;
1684 TRACE("\n");
1686 if(request->hCacheFile) {
1687 WCHAR url[INTERNET_MAX_URL_LENGTH];
1689 CloseHandle(request->hCacheFile);
1691 if(HTTP_GetRequestURL(request, url)) {
1692 DWORD headersLen;
1694 headersLen = request->rawHeaders ? strlenW(request->rawHeaders) : 0;
1695 CommitUrlCacheEntryW(url, request->cacheFile, request->expires,
1696 request->last_modified, NORMAL_CACHE_ENTRY,
1697 request->rawHeaders, headersLen, NULL, 0);
1701 HeapFree(GetProcessHeap(), 0, request->cacheFile);
1703 DeleteCriticalSection( &request->read_section );
1704 WININET_Release(&request->session->hdr);
1706 destroy_authinfo(request->authInfo);
1707 destroy_authinfo(request->proxyAuthInfo);
1709 HeapFree(GetProcessHeap(), 0, request->path);
1710 HeapFree(GetProcessHeap(), 0, request->verb);
1711 HeapFree(GetProcessHeap(), 0, request->rawHeaders);
1712 HeapFree(GetProcessHeap(), 0, request->version);
1713 HeapFree(GetProcessHeap(), 0, request->statusText);
1715 for (i = 0; i < request->nCustHeaders; i++)
1717 HeapFree(GetProcessHeap(), 0, request->custHeaders[i].lpszField);
1718 HeapFree(GetProcessHeap(), 0, request->custHeaders[i].lpszValue);
1721 destroy_data_stream(request->data_stream);
1722 HeapFree(GetProcessHeap(), 0, request->custHeaders);
1725 static void HTTPREQ_CloseConnection(object_header_t *hdr)
1727 http_request_t *request = (http_request_t*) hdr;
1729 TRACE("%p\n",request);
1731 if (!NETCON_connected(&request->netConnection))
1732 return;
1734 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1735 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
1737 NETCON_close(&request->netConnection);
1739 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
1740 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
1743 static BOOL HTTP_KeepAlive(http_request_t *request)
1745 WCHAR szVersion[10];
1746 WCHAR szConnectionResponse[20];
1747 DWORD dwBufferSize = sizeof(szVersion);
1748 BOOL keepalive = FALSE;
1750 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1751 * the connection is keep-alive by default */
1752 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
1753 && !strcmpiW(szVersion, g_szHttp1_1))
1755 keepalive = TRUE;
1758 dwBufferSize = sizeof(szConnectionResponse);
1759 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
1760 || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
1762 keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
1765 return keepalive;
1768 static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1770 http_request_t *req = (http_request_t*)hdr;
1772 switch(option) {
1773 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
1775 http_session_t *session = req->session;
1776 INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
1778 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1780 if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
1781 return ERROR_INSUFFICIENT_BUFFER;
1782 *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
1783 /* FIXME: can't get a SOCKET from our connection since we don't use
1784 * winsock
1786 info->Socket = 0;
1787 /* FIXME: get source port from req->netConnection */
1788 info->SourcePort = 0;
1789 info->DestPort = session->hostPort;
1790 info->Flags = 0;
1791 if (HTTP_KeepAlive(req))
1792 info->Flags |= IDSI_FLAG_KEEP_ALIVE;
1793 if (session->appInfo->proxy && session->appInfo->proxy[0] != 0)
1794 info->Flags |= IDSI_FLAG_PROXY;
1795 if (req->netConnection.useSSL)
1796 info->Flags |= IDSI_FLAG_SECURE;
1798 return ERROR_SUCCESS;
1801 case INTERNET_OPTION_SECURITY_FLAGS:
1803 DWORD flags;
1804 int bits;
1806 if (*size < sizeof(ULONG))
1807 return ERROR_INSUFFICIENT_BUFFER;
1809 *size = sizeof(DWORD);
1810 flags = 0;
1811 if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
1812 flags |= SECURITY_FLAG_SECURE;
1813 flags |= req->netConnection.security_flags;
1814 bits = NETCON_GetCipherStrength(&req->netConnection);
1815 if (bits >= 128)
1816 flags |= SECURITY_FLAG_STRENGTH_STRONG;
1817 else if (bits >= 56)
1818 flags |= SECURITY_FLAG_STRENGTH_MEDIUM;
1819 else
1820 flags |= SECURITY_FLAG_STRENGTH_WEAK;
1821 *(DWORD *)buffer = flags;
1822 return ERROR_SUCCESS;
1825 case INTERNET_OPTION_HANDLE_TYPE:
1826 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1828 if (*size < sizeof(ULONG))
1829 return ERROR_INSUFFICIENT_BUFFER;
1831 *size = sizeof(DWORD);
1832 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
1833 return ERROR_SUCCESS;
1835 case INTERNET_OPTION_URL: {
1836 WCHAR url[INTERNET_MAX_URL_LENGTH];
1837 HTTPHEADERW *host;
1838 DWORD len;
1839 WCHAR *pch;
1841 static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
1843 TRACE("INTERNET_OPTION_URL\n");
1845 host = HTTP_GetHeader(req, hostW);
1846 strcpyW(url, httpW);
1847 strcatW(url, host->lpszValue);
1848 if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
1849 *pch = 0;
1850 strcatW(url, req->path);
1852 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
1854 if(unicode) {
1855 len = (strlenW(url)+1) * sizeof(WCHAR);
1856 if(*size < len)
1857 return ERROR_INSUFFICIENT_BUFFER;
1859 *size = len;
1860 strcpyW(buffer, url);
1861 return ERROR_SUCCESS;
1862 }else {
1863 len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
1864 if(len > *size)
1865 return ERROR_INSUFFICIENT_BUFFER;
1867 *size = len;
1868 return ERROR_SUCCESS;
1872 case INTERNET_OPTION_CACHE_TIMESTAMPS: {
1873 INTERNET_CACHE_ENTRY_INFOW *info;
1874 INTERNET_CACHE_TIMESTAMPS *ts = buffer;
1875 WCHAR url[INTERNET_MAX_URL_LENGTH];
1876 DWORD nbytes, error;
1877 BOOL ret;
1879 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
1881 if (*size < sizeof(*ts))
1883 *size = sizeof(*ts);
1884 return ERROR_INSUFFICIENT_BUFFER;
1886 nbytes = 0;
1887 HTTP_GetRequestURL(req, url);
1888 ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
1889 error = GetLastError();
1890 if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
1892 if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
1893 return ERROR_OUTOFMEMORY;
1895 GetUrlCacheEntryInfoW(url, info, &nbytes);
1897 ts->ftExpires = info->ExpireTime;
1898 ts->ftLastModified = info->LastModifiedTime;
1900 HeapFree(GetProcessHeap(), 0, info);
1901 *size = sizeof(*ts);
1902 return ERROR_SUCCESS;
1904 return error;
1907 case INTERNET_OPTION_DATAFILE_NAME: {
1908 DWORD req_size;
1910 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1912 if(!req->cacheFile) {
1913 *size = 0;
1914 return ERROR_INTERNET_ITEM_NOT_FOUND;
1917 if(unicode) {
1918 req_size = (lstrlenW(req->cacheFile)+1) * sizeof(WCHAR);
1919 if(*size < req_size)
1920 return ERROR_INSUFFICIENT_BUFFER;
1922 *size = req_size;
1923 memcpy(buffer, req->cacheFile, *size);
1924 return ERROR_SUCCESS;
1925 }else {
1926 req_size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile, -1, NULL, 0, NULL, NULL);
1927 if (req_size > *size)
1928 return ERROR_INSUFFICIENT_BUFFER;
1930 *size = WideCharToMultiByte(CP_ACP, 0, req->cacheFile,
1931 -1, buffer, *size, NULL, NULL);
1932 return ERROR_SUCCESS;
1936 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
1937 PCCERT_CONTEXT context;
1939 if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
1940 *size = sizeof(INTERNET_CERTIFICATE_INFOA);
1941 return ERROR_INSUFFICIENT_BUFFER;
1944 context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
1945 if(context) {
1946 INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
1947 DWORD len;
1949 memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
1950 info->ftExpiry = context->pCertInfo->NotAfter;
1951 info->ftStart = context->pCertInfo->NotBefore;
1952 len = CertNameToStrA(context->dwCertEncodingType,
1953 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
1954 info->lpszSubjectInfo = LocalAlloc(0, len);
1955 if(info->lpszSubjectInfo)
1956 CertNameToStrA(context->dwCertEncodingType,
1957 &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
1958 info->lpszSubjectInfo, len);
1959 len = CertNameToStrA(context->dwCertEncodingType,
1960 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
1961 info->lpszIssuerInfo = LocalAlloc(0, len);
1962 if(info->lpszIssuerInfo)
1963 CertNameToStrA(context->dwCertEncodingType,
1964 &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
1965 info->lpszIssuerInfo, len);
1966 info->dwKeySize = NETCON_GetCipherStrength(&req->netConnection);
1967 CertFreeCertificateContext(context);
1968 return ERROR_SUCCESS;
1973 return INET_QueryOption(hdr, option, buffer, size, unicode);
1976 static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
1978 http_request_t *req = (http_request_t*)hdr;
1980 switch(option) {
1981 case INTERNET_OPTION_SECURITY_FLAGS:
1983 DWORD flags;
1985 if (!buffer || size != sizeof(DWORD))
1986 return ERROR_INVALID_PARAMETER;
1987 flags = *(DWORD *)buffer;
1988 TRACE("%08x\n", flags);
1989 req->netConnection.security_flags = flags;
1990 return ERROR_SUCCESS;
1992 case INTERNET_OPTION_SEND_TIMEOUT:
1993 case INTERNET_OPTION_RECEIVE_TIMEOUT:
1994 TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
1996 if (size != sizeof(DWORD))
1997 return ERROR_INVALID_PARAMETER;
1999 return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
2000 *(DWORD*)buffer);
2002 case INTERNET_OPTION_USERNAME:
2003 HeapFree(GetProcessHeap(), 0, req->session->userName);
2004 if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2005 return ERROR_SUCCESS;
2007 case INTERNET_OPTION_PASSWORD:
2008 HeapFree(GetProcessHeap(), 0, req->session->password);
2009 if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2010 return ERROR_SUCCESS;
2011 case INTERNET_OPTION_HTTP_DECODING:
2012 if(size != sizeof(BOOL))
2013 return ERROR_INVALID_PARAMETER;
2014 req->decoding = *(BOOL*)buffer;
2015 return ERROR_SUCCESS;
2018 return ERROR_INTERNET_INVALID_OPTION;
2021 /* read some more data into the read buffer (the read section must be held) */
2022 static DWORD read_more_data( http_request_t *req, int maxlen )
2024 DWORD res;
2025 int len;
2027 if (req->read_pos)
2029 /* move existing data to the start of the buffer */
2030 if(req->read_size)
2031 memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2032 req->read_pos = 0;
2035 if (maxlen == -1) maxlen = sizeof(req->read_buf);
2037 res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
2038 maxlen - req->read_size, 0, &len );
2039 if(res == ERROR_SUCCESS)
2040 req->read_size += len;
2042 return res;
2045 /* remove some amount of data from the read buffer (the read section must be held) */
2046 static void remove_data( http_request_t *req, int count )
2048 if (!(req->read_size -= count)) req->read_pos = 0;
2049 else req->read_pos += count;
2052 static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
2054 int count, bytes_read, pos = 0;
2055 DWORD res;
2057 EnterCriticalSection( &req->read_section );
2058 for (;;)
2060 BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2062 if (eol)
2064 count = eol - (req->read_buf + req->read_pos);
2065 bytes_read = count + 1;
2067 else count = bytes_read = req->read_size;
2069 count = min( count, *len - pos );
2070 memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2071 pos += count;
2072 remove_data( req, bytes_read );
2073 if (eol) break;
2075 if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
2077 *len = 0;
2078 TRACE( "returning empty string\n" );
2079 LeaveCriticalSection( &req->read_section );
2080 INTERNET_SetLastError(res);
2081 return FALSE;
2084 LeaveCriticalSection( &req->read_section );
2086 if (pos < *len)
2088 if (pos && buffer[pos - 1] == '\r') pos--;
2089 *len = pos + 1;
2091 buffer[*len - 1] = 0;
2092 TRACE( "returning %s\n", debugstr_a(buffer));
2093 return TRUE;
2096 /* check if we have reached the end of the data to read (the read section must be held) */
2097 static BOOL end_of_read_data( http_request_t *req )
2099 return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2102 /* fetch some more data into the read buffer (the read section must be held) */
2103 static DWORD refill_read_buffer(http_request_t *req, read_mode_t read_mode)
2105 DWORD res, read=0;
2107 if(req->read_size == sizeof(req->read_buf))
2108 return ERROR_SUCCESS;
2110 if(req->read_pos) {
2111 if(req->read_size)
2112 memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2113 req->read_pos = 0;
2116 res = req->data_stream->vtbl->read(req->data_stream, req, req->read_buf+req->read_size,
2117 sizeof(req->read_buf)-req->read_size, &read, read_mode);
2118 req->read_size += read;
2120 TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2121 return res;
2124 /* return the size of data available to be read immediately (the read section must be held) */
2125 static DWORD get_avail_data( http_request_t *req )
2127 return req->read_size + req->data_stream->vtbl->get_avail_data(req->data_stream, req);
2130 static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
2132 DWORD avail = 0;
2134 NETCON_query_data_available(&req->netConnection, &avail);
2135 return avail;
2138 static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
2140 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2141 return netconn_stream->content_read == netconn_stream->content_length;
2144 static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2145 DWORD *read, read_mode_t read_mode)
2147 netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2148 DWORD ret_read = 0;
2149 int len;
2151 size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2153 if(read_mode == READMODE_NOBLOCK)
2154 size = min(size, netconn_get_avail_data(stream, req));
2156 if(size) {
2157 if(NETCON_recv(&req->netConnection, buf + ret_read, size,
2158 read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
2159 ret_read = len;
2162 netconn_stream->content_read += *read = ret_read;
2163 TRACE("read %u bytes\n", ret_read);
2164 return ERROR_SUCCESS;
2167 static void netconn_destroy(data_stream_t *stream)
2171 static const data_stream_vtbl_t netconn_stream_vtbl = {
2172 netconn_get_avail_data,
2173 netconn_end_of_data,
2174 netconn_read,
2175 netconn_destroy
2178 /* read some more data into the read buffer (the read section must be held) */
2179 static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *req, int maxlen)
2181 DWORD res;
2182 int len;
2184 if (stream->buf_pos)
2186 /* move existing data to the start of the buffer */
2187 if(stream->buf_size)
2188 memmove(stream->buf, stream->buf + stream->buf_pos, stream->buf_size);
2189 stream->buf_pos = 0;
2192 if (maxlen == -1) maxlen = sizeof(stream->buf);
2194 res = NETCON_recv( &req->netConnection, stream->buf + stream->buf_size,
2195 maxlen - stream->buf_size, 0, &len );
2196 if(res == ERROR_SUCCESS)
2197 stream->buf_size += len;
2199 return res;
2202 /* remove some amount of data from the read buffer (the read section must be held) */
2203 static void remove_chunked_data(chunked_stream_t *stream, int count)
2205 if (!(stream->buf_size -= count)) stream->buf_pos = 0;
2206 else stream->buf_pos += count;
2209 /* discard data contents until we reach end of line (the read section must be held) */
2210 static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
2212 DWORD res;
2216 BYTE *eol = memchr(stream->buf + stream->buf_pos, '\n', stream->buf_size);
2217 if (eol)
2219 remove_chunked_data(stream, (eol + 1) - (stream->buf + stream->buf_pos));
2220 break;
2222 stream->buf_pos = stream->buf_size = 0; /* discard everything */
2223 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2224 } while (stream->buf_size);
2225 return ERROR_SUCCESS;
2228 /* read the size of the next chunk (the read section must be held) */
2229 static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
2231 /* TODOO */
2232 DWORD chunk_size = 0, res;
2234 if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
2235 return res;
2237 for (;;)
2239 while (stream->buf_size)
2241 char ch = stream->buf[stream->buf_pos];
2242 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
2243 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
2244 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
2245 else if (ch == ';' || ch == '\r' || ch == '\n')
2247 TRACE( "reading %u byte chunk\n", chunk_size );
2248 stream->chunk_size = chunk_size;
2249 req->contentLength += chunk_size;
2250 return discard_chunked_eol(stream, req);
2252 remove_chunked_data(stream, 1);
2254 if ((res = read_more_chunked_data(stream, req, -1)) != ERROR_SUCCESS) return res;
2255 if (!stream->buf_size)
2257 stream->chunk_size = 0;
2258 return ERROR_SUCCESS;
2263 static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
2265 /* Allow reading only from read buffer */
2266 return 0;
2269 static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
2271 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2272 return !chunked_stream->chunk_size;
2275 static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
2276 DWORD *read, read_mode_t read_mode)
2278 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2279 DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
2281 if(chunked_stream->chunk_size == ~0u) {
2282 res = start_next_chunk(chunked_stream, req);
2283 if(res != ERROR_SUCCESS)
2284 return res;
2287 while(size && chunked_stream->chunk_size) {
2288 if(chunked_stream->buf_size) {
2289 read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
2291 /* this could block */
2292 if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
2293 break;
2295 memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2296 remove_chunked_data(chunked_stream, read_bytes);
2297 }else {
2298 read_bytes = min(size, chunked_stream->chunk_size);
2300 if(read_mode == READMODE_NOBLOCK) {
2301 DWORD avail;
2303 if(!NETCON_query_data_available(&req->netConnection, &avail) || !avail)
2304 break;
2305 if(read_bytes > avail)
2306 read_bytes = avail;
2308 /* this could block */
2309 if(read_bytes == chunked_stream->chunk_size)
2310 break;
2313 res = NETCON_recv(&req->netConnection, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
2314 if(res != ERROR_SUCCESS)
2315 break;
2318 chunked_stream->chunk_size -= read_bytes;
2319 size -= read_bytes;
2320 ret_read += read_bytes;
2321 if(!chunked_stream->chunk_size) {
2322 assert(read_mode != READMODE_NOBLOCK);
2323 res = start_next_chunk(chunked_stream, req);
2324 if(res != ERROR_SUCCESS)
2325 break;
2328 if(read_mode == READMODE_ASYNC)
2329 read_mode = READMODE_NOBLOCK;
2332 TRACE("read %u bytes\n", ret_read);
2333 *read = ret_read;
2334 return res;
2337 static void chunked_destroy(data_stream_t *stream)
2339 chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2340 heap_free(chunked_stream);
2343 static const data_stream_vtbl_t chunked_stream_vtbl = {
2344 chunked_get_avail_data,
2345 chunked_end_of_data,
2346 chunked_read,
2347 chunked_destroy
2350 /* set the request content length based on the headers */
2351 static DWORD set_content_length(http_request_t *request)
2353 static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2354 WCHAR encoding[20];
2355 DWORD size;
2357 size = sizeof(request->contentLength);
2358 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
2359 &request->contentLength, &size, NULL) != ERROR_SUCCESS)
2360 request->contentLength = ~0u;
2361 request->netconn_stream.content_length = request->contentLength;
2362 request->netconn_stream.content_read = request->read_size;
2364 size = sizeof(encoding);
2365 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
2366 !strcmpiW(encoding, szChunked))
2368 chunked_stream_t *chunked_stream;
2370 chunked_stream = heap_alloc(sizeof(*chunked_stream));
2371 if(!chunked_stream)
2372 return ERROR_OUTOFMEMORY;
2374 chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2375 chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2376 chunked_stream->chunk_size = ~0u;
2378 if(request->read_size) {
2379 memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2380 chunked_stream->buf_size = request->read_size;
2381 request->read_size = request->read_pos = 0;
2384 request->data_stream = &chunked_stream->data_stream;
2385 request->contentLength = ~0u;
2386 request->read_chunked = TRUE;
2389 if(request->decoding) {
2390 int encoding_idx;
2392 static const WCHAR gzipW[] = {'g','z','i','p',0};
2394 encoding_idx = HTTP_GetCustomHeaderIndex(request, szContent_Encoding, 0, FALSE);
2395 if(encoding_idx != -1 && !strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW))
2396 return init_gzip_stream(request);
2399 return ERROR_SUCCESS;
2402 static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
2404 INTERNET_ASYNC_RESULT iar;
2405 DWORD res;
2407 TRACE("%p\n", req);
2409 EnterCriticalSection( &req->read_section );
2410 if ((res = refill_read_buffer(req, READMODE_ASYNC)) == ERROR_SUCCESS) {
2411 iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
2412 iar.dwError = first_notif ? 0 : get_avail_data(req);
2413 if(!first_notif && !iar.dwError)
2414 ERR("No data reported!\n");
2415 }else {
2416 iar.dwResult = 0;
2417 iar.dwError = res;
2419 LeaveCriticalSection( &req->read_section );
2421 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2422 sizeof(INTERNET_ASYNC_RESULT));
2425 /* read data from the http connection (the read section must be held) */
2426 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
2428 DWORD current_read = 0, ret_read = 0;
2429 read_mode_t read_mode;
2430 DWORD res = ERROR_SUCCESS;
2432 read_mode = req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC ? READMODE_ASYNC : READMODE_SYNC;
2434 EnterCriticalSection( &req->read_section );
2436 if(req->read_size) {
2437 ret_read = min(size, req->read_size);
2438 memcpy(buffer, req->read_buf+req->read_pos, ret_read);
2439 req->read_size -= ret_read;
2440 req->read_pos += ret_read;
2441 if(read_mode == READMODE_ASYNC)
2442 read_mode = READMODE_NOBLOCK;
2445 if(ret_read < size) {
2446 res = req->data_stream->vtbl->read(req->data_stream, req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, read_mode);
2447 ret_read += current_read;
2450 LeaveCriticalSection( &req->read_section );
2452 *read = ret_read;
2453 TRACE( "retrieved %u bytes (%u)\n", ret_read, req->contentLength );
2455 if(req->hCacheFile && res == ERROR_SUCCESS && ret_read) {
2456 BOOL res;
2457 DWORD written;
2459 res = WriteFile(req->hCacheFile, buffer, ret_read, &written, NULL);
2460 if(!res)
2461 WARN("WriteFile failed: %u\n", GetLastError());
2464 if(end_of_read_data(req))
2465 HTTP_FinishedReading(req);
2467 return res;
2471 static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
2473 http_request_t *req = (http_request_t*)hdr;
2474 DWORD res;
2476 EnterCriticalSection( &req->read_section );
2477 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2478 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2480 res = HTTPREQ_Read(req, buffer, size, read, TRUE);
2481 if(res == ERROR_SUCCESS)
2482 res = hdr->dwError;
2483 LeaveCriticalSection( &req->read_section );
2485 return res;
2488 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
2490 struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
2491 http_request_t *req = (http_request_t*)workRequest->hdr;
2492 INTERNET_ASYNC_RESULT iar;
2493 DWORD res;
2495 TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
2497 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2498 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2500 iar.dwResult = res == ERROR_SUCCESS;
2501 iar.dwError = res;
2503 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2504 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2505 sizeof(INTERNET_ASYNC_RESULT));
2508 static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
2509 DWORD flags, DWORD_PTR context)
2511 http_request_t *req = (http_request_t*)hdr;
2512 DWORD res, size, read, error = ERROR_SUCCESS;
2514 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2515 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2517 if (buffers->dwStructSize != sizeof(*buffers))
2518 return ERROR_INVALID_PARAMETER;
2520 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2522 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2524 WORKREQUEST workRequest;
2526 if (TryEnterCriticalSection( &req->read_section ))
2528 if (get_avail_data(req))
2530 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2531 &buffers->dwBufferLength, FALSE);
2532 size = buffers->dwBufferLength;
2533 LeaveCriticalSection( &req->read_section );
2534 goto done;
2536 LeaveCriticalSection( &req->read_section );
2539 workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
2540 workRequest.hdr = WININET_AddRef(&req->hdr);
2541 workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
2543 INTERNET_AsyncCall(&workRequest);
2545 return ERROR_IO_PENDING;
2548 read = 0;
2549 size = buffers->dwBufferLength;
2551 EnterCriticalSection( &req->read_section );
2552 if(hdr->dwError == ERROR_SUCCESS)
2553 hdr->dwError = INTERNET_HANDLE_IN_USE;
2554 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2555 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2557 while(1) {
2558 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2559 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2560 if(res != ERROR_SUCCESS)
2561 break;
2563 read += buffers->dwBufferLength;
2564 if(read == size || end_of_read_data(req))
2565 break;
2567 LeaveCriticalSection( &req->read_section );
2569 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2570 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2571 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2572 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2574 EnterCriticalSection( &req->read_section );
2577 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2578 hdr->dwError = ERROR_SUCCESS;
2579 else
2580 error = hdr->dwError;
2582 LeaveCriticalSection( &req->read_section );
2583 size = buffers->dwBufferLength;
2584 buffers->dwBufferLength = read;
2586 done:
2587 if (res == ERROR_SUCCESS) {
2588 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2589 &size, sizeof(size));
2592 return res==ERROR_SUCCESS ? error : res;
2595 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
2597 struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
2598 http_request_t *req = (http_request_t*)workRequest->hdr;
2599 INTERNET_ASYNC_RESULT iar;
2600 DWORD res;
2602 TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
2604 res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
2605 data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
2607 iar.dwResult = res == ERROR_SUCCESS;
2608 iar.dwError = res;
2610 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2611 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2612 sizeof(INTERNET_ASYNC_RESULT));
2615 static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
2616 DWORD flags, DWORD_PTR context)
2619 http_request_t *req = (http_request_t*)hdr;
2620 DWORD res, size, read, error = ERROR_SUCCESS;
2622 if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
2623 FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
2625 if (buffers->dwStructSize != sizeof(*buffers))
2626 return ERROR_INVALID_PARAMETER;
2628 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2630 if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
2632 WORKREQUEST workRequest;
2634 if (TryEnterCriticalSection( &req->read_section ))
2636 if (get_avail_data(req))
2638 res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
2639 &buffers->dwBufferLength, FALSE);
2640 size = buffers->dwBufferLength;
2641 LeaveCriticalSection( &req->read_section );
2642 goto done;
2644 LeaveCriticalSection( &req->read_section );
2647 workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
2648 workRequest.hdr = WININET_AddRef(&req->hdr);
2649 workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
2651 INTERNET_AsyncCall(&workRequest);
2653 return ERROR_IO_PENDING;
2656 read = 0;
2657 size = buffers->dwBufferLength;
2659 EnterCriticalSection( &req->read_section );
2660 if(hdr->dwError == ERROR_SUCCESS)
2661 hdr->dwError = INTERNET_HANDLE_IN_USE;
2662 else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2663 hdr->dwError = ERROR_INTERNET_INTERNAL_ERROR;
2665 while(1) {
2666 res = HTTPREQ_Read(req, (char*)buffers->lpvBuffer+read, size-read,
2667 &buffers->dwBufferLength, !(flags & IRF_NO_WAIT));
2668 if(res != ERROR_SUCCESS)
2669 break;
2671 read += buffers->dwBufferLength;
2672 if(read == size || end_of_read_data(req))
2673 break;
2675 LeaveCriticalSection( &req->read_section );
2677 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2678 &buffers->dwBufferLength, sizeof(buffers->dwBufferLength));
2679 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2680 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2682 EnterCriticalSection( &req->read_section );
2685 if(hdr->dwError == INTERNET_HANDLE_IN_USE)
2686 hdr->dwError = ERROR_SUCCESS;
2687 else
2688 error = hdr->dwError;
2690 LeaveCriticalSection( &req->read_section );
2691 size = buffers->dwBufferLength;
2692 buffers->dwBufferLength = read;
2694 done:
2695 if (res == ERROR_SUCCESS) {
2696 INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2697 &size, sizeof(size));
2700 return res==ERROR_SUCCESS ? error : res;
2703 static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
2705 DWORD res;
2706 http_request_t *request = (http_request_t*)hdr;
2708 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2710 *written = 0;
2711 res = NETCON_send(&request->netConnection, buffer, size, 0, (LPINT)written);
2712 if (res == ERROR_SUCCESS)
2713 request->bytesWritten += *written;
2715 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
2716 return res;
2719 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
2721 http_request_t *req = (http_request_t*)workRequest->hdr;
2723 HTTP_ReceiveRequestData(req, FALSE);
2726 static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
2728 http_request_t *req = (http_request_t*)hdr;
2730 TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
2732 if (req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2734 WORKREQUEST workRequest;
2736 /* never wait, if we can't enter the section we queue an async request right away */
2737 if (TryEnterCriticalSection( &req->read_section ))
2739 refill_read_buffer(req, READMODE_NOBLOCK);
2740 if ((*available = get_avail_data( req ))) goto done;
2741 if (end_of_read_data( req )) goto done;
2742 LeaveCriticalSection( &req->read_section );
2745 workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
2746 workRequest.hdr = WININET_AddRef( &req->hdr );
2748 INTERNET_AsyncCall(&workRequest);
2750 return ERROR_IO_PENDING;
2753 EnterCriticalSection( &req->read_section );
2755 if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
2757 refill_read_buffer( req, READMODE_ASYNC );
2758 *available = get_avail_data( req );
2761 done:
2762 LeaveCriticalSection( &req->read_section );
2764 TRACE( "returning %u\n", *available );
2765 return ERROR_SUCCESS;
2768 static const object_vtbl_t HTTPREQVtbl = {
2769 HTTPREQ_Destroy,
2770 HTTPREQ_CloseConnection,
2771 HTTPREQ_QueryOption,
2772 HTTPREQ_SetOption,
2773 HTTPREQ_ReadFile,
2774 HTTPREQ_ReadFileExA,
2775 HTTPREQ_ReadFileExW,
2776 HTTPREQ_WriteFile,
2777 HTTPREQ_QueryDataAvailable,
2778 NULL
2781 /***********************************************************************
2782 * HTTP_HttpOpenRequestW (internal)
2784 * Open a HTTP request handle
2786 * RETURNS
2787 * HINTERNET a HTTP request handle on success
2788 * NULL on failure
2791 static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
2792 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2793 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2794 DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
2796 appinfo_t *hIC = NULL;
2797 http_request_t *request;
2798 LPWSTR lpszHostName = NULL;
2799 static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
2800 DWORD len, res;
2802 TRACE("-->\n");
2804 assert( session->hdr.htype == WH_HHTTPSESSION );
2805 hIC = session->appInfo;
2807 request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
2808 if(!request)
2809 return ERROR_OUTOFMEMORY;
2811 request->hdr.htype = WH_HHTTPREQ;
2812 request->hdr.dwFlags = dwFlags;
2813 request->hdr.dwContext = dwContext;
2814 request->contentLength = ~0u;
2816 request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
2817 request->data_stream = &request->netconn_stream.data_stream;
2819 InitializeCriticalSection( &request->read_section );
2821 WININET_AddRef( &session->hdr );
2822 request->session = session;
2823 list_add_head( &session->hdr.children, &request->hdr.entry );
2825 lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
2826 (strlenW(session->hostName) + 7 /* length of ":65535" + 1 */));
2827 if (NULL == lpszHostName)
2829 res = ERROR_OUTOFMEMORY;
2830 goto lend;
2833 if ((res = NETCON_init(&request->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
2834 goto lend;
2836 if (lpszObjectName && *lpszObjectName) {
2837 HRESULT rc;
2839 len = 0;
2840 rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
2841 if (rc != E_POINTER)
2842 len = strlenW(lpszObjectName)+1;
2843 request->path = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
2844 rc = UrlEscapeW(lpszObjectName, request->path, &len,
2845 URL_ESCAPE_SPACES_ONLY);
2846 if (rc != S_OK)
2848 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
2849 strcpyW(request->path,lpszObjectName);
2851 }else {
2852 static const WCHAR slashW[] = {'/',0};
2854 request->path = heap_strdupW(slashW);
2857 if (lpszReferrer && *lpszReferrer)
2858 HTTP_ProcessHeader(request, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2860 if (lpszAcceptTypes)
2862 int i;
2863 for (i = 0; lpszAcceptTypes[i]; i++)
2865 if (!*lpszAcceptTypes[i]) continue;
2866 HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
2867 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
2868 HTTP_ADDHDR_FLAG_REQ |
2869 (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
2873 request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
2874 request->version = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
2876 if (session->hostPort != INTERNET_INVALID_PORT_NUMBER &&
2877 session->hostPort != INTERNET_DEFAULT_HTTP_PORT &&
2878 session->hostPort != INTERNET_DEFAULT_HTTPS_PORT)
2880 sprintfW(lpszHostName, szHostForm, session->hostName, session->hostPort);
2881 HTTP_ProcessHeader(request, hostW, lpszHostName,
2882 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2884 else
2885 HTTP_ProcessHeader(request, hostW, session->hostName,
2886 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
2888 if (session->serverPort == INTERNET_INVALID_PORT_NUMBER)
2889 session->serverPort = (dwFlags & INTERNET_FLAG_SECURE ?
2890 INTERNET_DEFAULT_HTTPS_PORT :
2891 INTERNET_DEFAULT_HTTP_PORT);
2893 if (session->hostPort == INTERNET_INVALID_PORT_NUMBER)
2894 session->hostPort = (dwFlags & INTERNET_FLAG_SECURE ?
2895 INTERNET_DEFAULT_HTTPS_PORT :
2896 INTERNET_DEFAULT_HTTP_PORT);
2898 if (NULL != hIC->proxy && hIC->proxy[0] != 0)
2899 HTTP_DealWithProxy( hIC, session, request );
2901 INTERNET_SendCallback(&session->hdr, dwContext,
2902 INTERNET_STATUS_HANDLE_CREATED, &request->hdr.hInternet,
2903 sizeof(HINTERNET));
2905 lend:
2906 TRACE("<-- %u (%p)\n", res, request);
2908 HeapFree(GetProcessHeap(), 0, lpszHostName);
2909 if(res != ERROR_SUCCESS) {
2910 WININET_Release( &request->hdr );
2911 *ret = NULL;
2912 return res;
2915 *ret = request->hdr.hInternet;
2916 return ERROR_SUCCESS;
2919 /***********************************************************************
2920 * HttpOpenRequestW (WININET.@)
2922 * Open a HTTP request handle
2924 * RETURNS
2925 * HINTERNET a HTTP request handle on success
2926 * NULL on failure
2929 HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
2930 LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
2931 LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
2932 DWORD dwFlags, DWORD_PTR dwContext)
2934 http_session_t *session;
2935 HINTERNET handle = NULL;
2936 DWORD res;
2938 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
2939 debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
2940 debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
2941 dwFlags, dwContext);
2942 if(lpszAcceptTypes!=NULL)
2944 int i;
2945 for(i=0;lpszAcceptTypes[i]!=NULL;i++)
2946 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
2949 session = (http_session_t*) get_handle_object( hHttpSession );
2950 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
2952 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2953 goto lend;
2957 * My tests seem to show that the windows version does not
2958 * become asynchronous until after this point. And anyhow
2959 * if this call was asynchronous then how would you get the
2960 * necessary HINTERNET pointer returned by this function.
2963 res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
2964 lpszVersion, lpszReferrer, lpszAcceptTypes,
2965 dwFlags, dwContext, &handle);
2966 lend:
2967 if( session )
2968 WININET_Release( &session->hdr );
2969 TRACE("returning %p\n", handle);
2970 if(res != ERROR_SUCCESS)
2971 SetLastError(res);
2972 return handle;
2975 /* read any content returned by the server so that the connection can be
2976 * reused */
2977 static void HTTP_DrainContent(http_request_t *req)
2979 DWORD bytes_read;
2981 if (!NETCON_connected(&req->netConnection)) return;
2983 if (req->contentLength == -1)
2985 NETCON_close(&req->netConnection);
2986 return;
2988 if (!strcmpW(req->verb, szHEAD)) return;
2992 char buffer[2048];
2993 if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
2994 return;
2995 } while (bytes_read);
2998 static const LPCWSTR header_lookup[] = {
2999 szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3000 szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3001 szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3002 szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3003 NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3004 szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3005 szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3006 szAllow, /* HTTP_QUERY_ALLOW = 7 */
3007 szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3008 szDate, /* HTTP_QUERY_DATE = 9 */
3009 szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3010 szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3011 NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3012 szURI, /* HTTP_QUERY_URI = 13 */
3013 szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3014 NULL, /* HTTP_QUERY_COST = 15 */
3015 NULL, /* HTTP_QUERY_LINK = 16 */
3016 szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3017 NULL, /* HTTP_QUERY_VERSION = 18 */
3018 szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3019 NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3020 NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3021 NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3022 szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3023 szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3024 szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3025 szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3026 szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3027 szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3028 szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3029 NULL, /* HTTP_QUERY_FORWARDED = 30 */
3030 NULL, /* HTTP_QUERY_FROM = 31 */
3031 szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3032 szLocation, /* HTTP_QUERY_LOCATION = 33 */
3033 NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3034 szReferer, /* HTTP_QUERY_REFERER = 35 */
3035 szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3036 szServer, /* HTTP_QUERY_SERVER = 37 */
3037 NULL, /* HTTP_TITLE = 38 */
3038 szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3039 szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3040 szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3041 szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3042 szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3043 szCookie, /* HTTP_QUERY_COOKIE = 44 */
3044 NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3045 NULL, /* HTTP_QUERY_REFRESH = 46 */
3046 NULL, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3047 szAge, /* HTTP_QUERY_AGE = 48 */
3048 szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3049 szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3050 szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3051 szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3052 szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3053 szETag, /* HTTP_QUERY_ETAG = 54 */
3054 hostW, /* HTTP_QUERY_HOST = 55 */
3055 szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3056 szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3057 szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3058 szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3059 szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3060 szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3061 szRange, /* HTTP_QUERY_RANGE = 62 */
3062 szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3063 szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3064 szVary, /* HTTP_QUERY_VARY = 65 */
3065 szVia, /* HTTP_QUERY_VIA = 66 */
3066 szWarning, /* HTTP_QUERY_WARNING = 67 */
3067 szExpect, /* HTTP_QUERY_EXPECT = 68 */
3068 szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3069 szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3072 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3074 /***********************************************************************
3075 * HTTP_HttpQueryInfoW (internal)
3077 static DWORD HTTP_HttpQueryInfoW(http_request_t *request, DWORD dwInfoLevel,
3078 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3080 LPHTTPHEADERW lphttpHdr = NULL;
3081 BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
3082 INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3083 DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
3084 INT index = -1;
3086 /* Find requested header structure */
3087 switch (level)
3089 case HTTP_QUERY_CUSTOM:
3090 if (!lpBuffer) return ERROR_INVALID_PARAMETER;
3091 index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3092 break;
3093 case HTTP_QUERY_RAW_HEADERS_CRLF:
3095 LPWSTR headers;
3096 DWORD len = 0;
3097 DWORD res = ERROR_INVALID_PARAMETER;
3099 if (request_only)
3100 headers = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
3101 else
3102 headers = request->rawHeaders;
3104 if (headers)
3105 len = strlenW(headers) * sizeof(WCHAR);
3107 if (len + sizeof(WCHAR) > *lpdwBufferLength)
3109 len += sizeof(WCHAR);
3110 res = ERROR_INSUFFICIENT_BUFFER;
3112 else if (lpBuffer)
3114 if (headers)
3115 memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3116 else
3118 len = strlenW(szCrLf) * sizeof(WCHAR);
3119 memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
3121 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3122 res = ERROR_SUCCESS;
3124 *lpdwBufferLength = len;
3126 if (request_only)
3127 HeapFree(GetProcessHeap(), 0, headers);
3128 return res;
3130 case HTTP_QUERY_RAW_HEADERS:
3132 LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(request->rawHeaders, szCrLf);
3133 DWORD i, size = 0;
3134 LPWSTR pszString = lpBuffer;
3136 for (i = 0; ppszRawHeaderLines[i]; i++)
3137 size += strlenW(ppszRawHeaderLines[i]) + 1;
3139 if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
3141 HTTP_FreeTokens(ppszRawHeaderLines);
3142 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
3143 return ERROR_INSUFFICIENT_BUFFER;
3145 if (pszString)
3147 for (i = 0; ppszRawHeaderLines[i]; i++)
3149 DWORD len = strlenW(ppszRawHeaderLines[i]);
3150 memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
3151 pszString += len+1;
3153 *pszString = '\0';
3154 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
3156 *lpdwBufferLength = size * sizeof(WCHAR);
3157 HTTP_FreeTokens(ppszRawHeaderLines);
3159 return ERROR_SUCCESS;
3161 case HTTP_QUERY_STATUS_TEXT:
3162 if (request->statusText)
3164 DWORD len = strlenW(request->statusText);
3165 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3167 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3168 return ERROR_INSUFFICIENT_BUFFER;
3170 if (lpBuffer)
3172 memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3173 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3175 *lpdwBufferLength = len * sizeof(WCHAR);
3176 return ERROR_SUCCESS;
3178 break;
3179 case HTTP_QUERY_VERSION:
3180 if (request->version)
3182 DWORD len = strlenW(request->version);
3183 if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3185 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3186 return ERROR_INSUFFICIENT_BUFFER;
3188 if (lpBuffer)
3190 memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3191 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3193 *lpdwBufferLength = len * sizeof(WCHAR);
3194 return ERROR_SUCCESS;
3196 break;
3197 case HTTP_QUERY_CONTENT_ENCODING:
3198 index = HTTP_GetCustomHeaderIndex(request, header_lookup[request->read_gzip ? HTTP_QUERY_CONTENT_TYPE : level],
3199 requested_index,request_only);
3200 break;
3201 default:
3202 assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
3204 if (level < LAST_TABLE_HEADER && header_lookup[level])
3205 index = HTTP_GetCustomHeaderIndex(request, header_lookup[level],
3206 requested_index,request_only);
3209 if (index >= 0)
3210 lphttpHdr = &request->custHeaders[index];
3212 /* Ensure header satisfies requested attributes */
3213 if (!lphttpHdr ||
3214 ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
3215 (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3217 return ERROR_HTTP_HEADER_NOT_FOUND;
3220 if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
3222 /* coalesce value to requested type */
3223 if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
3225 *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
3226 TRACE(" returning number: %d\n", *(int *)lpBuffer);
3228 else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
3230 time_t tmpTime;
3231 struct tm tmpTM;
3232 SYSTEMTIME *STHook;
3234 tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3236 tmpTM = *gmtime(&tmpTime);
3237 STHook = (SYSTEMTIME *)lpBuffer;
3238 STHook->wDay = tmpTM.tm_mday;
3239 STHook->wHour = tmpTM.tm_hour;
3240 STHook->wMilliseconds = 0;
3241 STHook->wMinute = tmpTM.tm_min;
3242 STHook->wDayOfWeek = tmpTM.tm_wday;
3243 STHook->wMonth = tmpTM.tm_mon + 1;
3244 STHook->wSecond = tmpTM.tm_sec;
3245 STHook->wYear = tmpTM.tm_year;
3247 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3248 STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3249 STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3251 else if (lphttpHdr->lpszValue)
3253 DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3255 if (len > *lpdwBufferLength)
3257 *lpdwBufferLength = len;
3258 return ERROR_INSUFFICIENT_BUFFER;
3260 if (lpBuffer)
3262 memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3263 TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3265 *lpdwBufferLength = len - sizeof(WCHAR);
3267 return ERROR_SUCCESS;
3270 /***********************************************************************
3271 * HttpQueryInfoW (WININET.@)
3273 * Queries for information about an HTTP request
3275 * RETURNS
3276 * TRUE on success
3277 * FALSE on failure
3280 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3281 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3283 http_request_t *request;
3284 DWORD res;
3286 if (TRACE_ON(wininet)) {
3287 #define FE(x) { x, #x }
3288 static const wininet_flag_info query_flags[] = {
3289 FE(HTTP_QUERY_MIME_VERSION),
3290 FE(HTTP_QUERY_CONTENT_TYPE),
3291 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
3292 FE(HTTP_QUERY_CONTENT_ID),
3293 FE(HTTP_QUERY_CONTENT_DESCRIPTION),
3294 FE(HTTP_QUERY_CONTENT_LENGTH),
3295 FE(HTTP_QUERY_CONTENT_LANGUAGE),
3296 FE(HTTP_QUERY_ALLOW),
3297 FE(HTTP_QUERY_PUBLIC),
3298 FE(HTTP_QUERY_DATE),
3299 FE(HTTP_QUERY_EXPIRES),
3300 FE(HTTP_QUERY_LAST_MODIFIED),
3301 FE(HTTP_QUERY_MESSAGE_ID),
3302 FE(HTTP_QUERY_URI),
3303 FE(HTTP_QUERY_DERIVED_FROM),
3304 FE(HTTP_QUERY_COST),
3305 FE(HTTP_QUERY_LINK),
3306 FE(HTTP_QUERY_PRAGMA),
3307 FE(HTTP_QUERY_VERSION),
3308 FE(HTTP_QUERY_STATUS_CODE),
3309 FE(HTTP_QUERY_STATUS_TEXT),
3310 FE(HTTP_QUERY_RAW_HEADERS),
3311 FE(HTTP_QUERY_RAW_HEADERS_CRLF),
3312 FE(HTTP_QUERY_CONNECTION),
3313 FE(HTTP_QUERY_ACCEPT),
3314 FE(HTTP_QUERY_ACCEPT_CHARSET),
3315 FE(HTTP_QUERY_ACCEPT_ENCODING),
3316 FE(HTTP_QUERY_ACCEPT_LANGUAGE),
3317 FE(HTTP_QUERY_AUTHORIZATION),
3318 FE(HTTP_QUERY_CONTENT_ENCODING),
3319 FE(HTTP_QUERY_FORWARDED),
3320 FE(HTTP_QUERY_FROM),
3321 FE(HTTP_QUERY_IF_MODIFIED_SINCE),
3322 FE(HTTP_QUERY_LOCATION),
3323 FE(HTTP_QUERY_ORIG_URI),
3324 FE(HTTP_QUERY_REFERER),
3325 FE(HTTP_QUERY_RETRY_AFTER),
3326 FE(HTTP_QUERY_SERVER),
3327 FE(HTTP_QUERY_TITLE),
3328 FE(HTTP_QUERY_USER_AGENT),
3329 FE(HTTP_QUERY_WWW_AUTHENTICATE),
3330 FE(HTTP_QUERY_PROXY_AUTHENTICATE),
3331 FE(HTTP_QUERY_ACCEPT_RANGES),
3332 FE(HTTP_QUERY_SET_COOKIE),
3333 FE(HTTP_QUERY_COOKIE),
3334 FE(HTTP_QUERY_REQUEST_METHOD),
3335 FE(HTTP_QUERY_REFRESH),
3336 FE(HTTP_QUERY_CONTENT_DISPOSITION),
3337 FE(HTTP_QUERY_AGE),
3338 FE(HTTP_QUERY_CACHE_CONTROL),
3339 FE(HTTP_QUERY_CONTENT_BASE),
3340 FE(HTTP_QUERY_CONTENT_LOCATION),
3341 FE(HTTP_QUERY_CONTENT_MD5),
3342 FE(HTTP_QUERY_CONTENT_RANGE),
3343 FE(HTTP_QUERY_ETAG),
3344 FE(HTTP_QUERY_HOST),
3345 FE(HTTP_QUERY_IF_MATCH),
3346 FE(HTTP_QUERY_IF_NONE_MATCH),
3347 FE(HTTP_QUERY_IF_RANGE),
3348 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
3349 FE(HTTP_QUERY_MAX_FORWARDS),
3350 FE(HTTP_QUERY_PROXY_AUTHORIZATION),
3351 FE(HTTP_QUERY_RANGE),
3352 FE(HTTP_QUERY_TRANSFER_ENCODING),
3353 FE(HTTP_QUERY_UPGRADE),
3354 FE(HTTP_QUERY_VARY),
3355 FE(HTTP_QUERY_VIA),
3356 FE(HTTP_QUERY_WARNING),
3357 FE(HTTP_QUERY_CUSTOM)
3359 static const wininet_flag_info modifier_flags[] = {
3360 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
3361 FE(HTTP_QUERY_FLAG_SYSTEMTIME),
3362 FE(HTTP_QUERY_FLAG_NUMBER),
3363 FE(HTTP_QUERY_FLAG_COALESCE)
3365 #undef FE
3366 DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
3367 DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
3368 DWORD i;
3370 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
3371 TRACE(" Attribute:");
3372 for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
3373 if (query_flags[i].val == info) {
3374 TRACE(" %s", query_flags[i].name);
3375 break;
3378 if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
3379 TRACE(" Unknown (%08x)", info);
3382 TRACE(" Modifier:");
3383 for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
3384 if (modifier_flags[i].val & info_mod) {
3385 TRACE(" %s", modifier_flags[i].name);
3386 info_mod &= ~ modifier_flags[i].val;
3390 if (info_mod) {
3391 TRACE(" Unknown (%08x)", info_mod);
3393 TRACE("\n");
3396 request = (http_request_t*) get_handle_object( hHttpRequest );
3397 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
3399 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3400 goto lend;
3403 if (lpBuffer == NULL)
3404 *lpdwBufferLength = 0;
3405 res = HTTP_HttpQueryInfoW( request, dwInfoLevel,
3406 lpBuffer, lpdwBufferLength, lpdwIndex);
3408 lend:
3409 if( request )
3410 WININET_Release( &request->hdr );
3412 TRACE("%u <--\n", res);
3413 if(res != ERROR_SUCCESS)
3414 SetLastError(res);
3415 return res == ERROR_SUCCESS;
3418 /***********************************************************************
3419 * HttpQueryInfoA (WININET.@)
3421 * Queries for information about an HTTP request
3423 * RETURNS
3424 * TRUE on success
3425 * FALSE on failure
3428 BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
3429 LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
3431 BOOL result;
3432 DWORD len;
3433 WCHAR* bufferW;
3435 if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
3436 (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
3438 return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
3439 lpdwBufferLength, lpdwIndex );
3442 if (lpBuffer)
3444 DWORD alloclen;
3445 len = (*lpdwBufferLength)*sizeof(WCHAR);
3446 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3448 alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
3449 if (alloclen < len)
3450 alloclen = len;
3452 else
3453 alloclen = len;
3454 bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
3455 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3456 if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
3457 MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
3458 } else
3460 bufferW = NULL;
3461 len = 0;
3464 result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
3465 &len, lpdwIndex );
3466 if( result )
3468 len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
3469 lpBuffer, *lpdwBufferLength, NULL, NULL );
3470 *lpdwBufferLength = len - 1;
3472 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
3474 else
3475 /* since the strings being returned from HttpQueryInfoW should be
3476 * only ASCII characters, it is reasonable to assume that all of
3477 * the Unicode characters can be reduced to a single byte */
3478 *lpdwBufferLength = len / sizeof(WCHAR);
3480 HeapFree(GetProcessHeap(), 0, bufferW );
3482 return result;
3485 /***********************************************************************
3486 * HTTP_GetRedirectURL (internal)
3488 static LPWSTR HTTP_GetRedirectURL(http_request_t *request, LPCWSTR lpszUrl)
3490 static WCHAR szHttp[] = {'h','t','t','p',0};
3491 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3492 http_session_t *session = request->session;
3493 URL_COMPONENTSW urlComponents;
3494 DWORD url_length = 0;
3495 LPWSTR orig_url;
3496 LPWSTR combined_url;
3498 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3499 urlComponents.lpszScheme = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
3500 urlComponents.dwSchemeLength = 0;
3501 urlComponents.lpszHostName = session->hostName;
3502 urlComponents.dwHostNameLength = 0;
3503 urlComponents.nPort = session->hostPort;
3504 urlComponents.lpszUserName = session->userName;
3505 urlComponents.dwUserNameLength = 0;
3506 urlComponents.lpszPassword = NULL;
3507 urlComponents.dwPasswordLength = 0;
3508 urlComponents.lpszUrlPath = request->path;
3509 urlComponents.dwUrlPathLength = 0;
3510 urlComponents.lpszExtraInfo = NULL;
3511 urlComponents.dwExtraInfoLength = 0;
3513 if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
3514 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3515 return NULL;
3517 orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
3519 /* convert from bytes to characters */
3520 url_length = url_length / sizeof(WCHAR) - 1;
3521 if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
3523 HeapFree(GetProcessHeap(), 0, orig_url);
3524 return NULL;
3527 url_length = 0;
3528 if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
3529 (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
3531 HeapFree(GetProcessHeap(), 0, orig_url);
3532 return NULL;
3534 combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
3536 if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
3538 HeapFree(GetProcessHeap(), 0, orig_url);
3539 HeapFree(GetProcessHeap(), 0, combined_url);
3540 return NULL;
3542 HeapFree(GetProcessHeap(), 0, orig_url);
3543 return combined_url;
3547 /***********************************************************************
3548 * HTTP_HandleRedirect (internal)
3550 static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
3552 http_session_t *session = request->session;
3553 appinfo_t *hIC = session->appInfo;
3554 BOOL using_proxy = hIC->proxy && hIC->proxy[0];
3555 WCHAR path[INTERNET_MAX_URL_LENGTH];
3556 int index;
3558 if(lpszUrl[0]=='/')
3560 /* if it's an absolute path, keep the same session info */
3561 lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
3563 else
3565 URL_COMPONENTSW urlComponents;
3566 WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
3567 static WCHAR szHttp[] = {'h','t','t','p',0};
3568 static WCHAR szHttps[] = {'h','t','t','p','s',0};
3570 userName[0] = 0;
3571 hostName[0] = 0;
3572 protocol[0] = 0;
3574 urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
3575 urlComponents.lpszScheme = protocol;
3576 urlComponents.dwSchemeLength = 32;
3577 urlComponents.lpszHostName = hostName;
3578 urlComponents.dwHostNameLength = MAXHOSTNAME;
3579 urlComponents.lpszUserName = userName;
3580 urlComponents.dwUserNameLength = 1024;
3581 urlComponents.lpszPassword = NULL;
3582 urlComponents.dwPasswordLength = 0;
3583 urlComponents.lpszUrlPath = path;
3584 urlComponents.dwUrlPathLength = 2048;
3585 urlComponents.lpszExtraInfo = NULL;
3586 urlComponents.dwExtraInfoLength = 0;
3587 if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3588 return INTERNET_GetLastError();
3590 if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
3591 (request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3593 TRACE("redirect from secure page to non-secure page\n");
3594 /* FIXME: warn about from secure redirect to non-secure page */
3595 request->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
3597 if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
3598 !(request->hdr.dwFlags & INTERNET_FLAG_SECURE))
3600 TRACE("redirect from non-secure page to secure page\n");
3601 /* FIXME: notify about redirect to secure page */
3602 request->hdr.dwFlags |= INTERNET_FLAG_SECURE;
3605 if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
3607 if (lstrlenW(protocol)>4) /*https*/
3608 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
3609 else /*http*/
3610 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
3613 #if 0
3615 * This upsets redirects to binary files on sourceforge.net
3616 * and gives an html page instead of the target file
3617 * Examination of the HTTP request sent by native wininet.dll
3618 * reveals that it doesn't send a referrer in that case.
3619 * Maybe there's a flag that enables this, or maybe a referrer
3620 * shouldn't be added in case of a redirect.
3623 /* consider the current host as the referrer */
3624 if (session->lpszServerName && *session->lpszServerName)
3625 HTTP_ProcessHeader(request, HTTP_REFERER, session->lpszServerName,
3626 HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
3627 HTTP_ADDHDR_FLAG_ADD_IF_NEW);
3628 #endif
3630 HeapFree(GetProcessHeap(), 0, session->hostName);
3631 if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
3632 urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
3634 int len;
3635 static const WCHAR fmt[] = {'%','s',':','%','u',0};
3636 len = lstrlenW(hostName);
3637 len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3638 session->hostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
3639 sprintfW(session->hostName, fmt, hostName, urlComponents.nPort);
3641 else
3642 session->hostName = heap_strdupW(hostName);
3644 HTTP_ProcessHeader(request, hostW, session->hostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
3646 HeapFree(GetProcessHeap(), 0, session->userName);
3647 session->userName = NULL;
3648 if (userName[0])
3649 session->userName = heap_strdupW(userName);
3651 if (!using_proxy)
3653 if (strcmpiW(session->serverName, hostName) || session->serverPort != urlComponents.nPort)
3655 DWORD res;
3657 HeapFree(GetProcessHeap(), 0, session->serverName);
3658 session->serverName = heap_strdupW(hostName);
3659 session->serverPort = urlComponents.nPort;
3661 NETCON_close(&request->netConnection);
3662 if ((res = HTTP_ResolveName(request)) != ERROR_SUCCESS)
3663 return res;
3665 res = NETCON_init(&request->netConnection, request->hdr.dwFlags & INTERNET_FLAG_SECURE);
3666 if (res != ERROR_SUCCESS)
3667 return res;
3669 reset_data_stream(request);
3672 else
3673 TRACE("Redirect through proxy\n");
3676 HeapFree(GetProcessHeap(), 0, request->path);
3677 request->path=NULL;
3678 if (*path)
3680 DWORD needed = 0;
3681 HRESULT rc;
3683 rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
3684 if (rc != E_POINTER)
3685 needed = strlenW(path)+1;
3686 request->path = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
3687 rc = UrlEscapeW(path, request->path, &needed,
3688 URL_ESCAPE_SPACES_ONLY);
3689 if (rc != S_OK)
3691 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
3692 strcpyW(request->path,path);
3696 /* Remove custom content-type/length headers on redirects. */
3697 index = HTTP_GetCustomHeaderIndex(request, szContent_Type, 0, TRUE);
3698 if (0 <= index)
3699 HTTP_DeleteCustomHeader(request, index);
3700 index = HTTP_GetCustomHeaderIndex(request, szContent_Length, 0, TRUE);
3701 if (0 <= index)
3702 HTTP_DeleteCustomHeader(request, index);
3704 return ERROR_SUCCESS;
3707 /***********************************************************************
3708 * HTTP_build_req (internal)
3710 * concatenate all the strings in the request together
3712 static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
3714 LPCWSTR *t;
3715 LPWSTR str;
3717 for( t = list; *t ; t++ )
3718 len += strlenW( *t );
3719 len++;
3721 str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
3722 *str = 0;
3724 for( t = list; *t ; t++ )
3725 strcatW( str, *t );
3727 return str;
3730 static DWORD HTTP_SecureProxyConnect(http_request_t *request)
3732 LPWSTR lpszPath;
3733 LPWSTR requestString;
3734 INT len;
3735 INT cnt;
3736 INT responseLen;
3737 char *ascii_req;
3738 DWORD res;
3739 static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
3740 static const WCHAR szFormat[] = {'%','s',':','%','u',0};
3741 http_session_t *session = request->session;
3743 TRACE("\n");
3745 lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( session->hostName ) + 13)*sizeof(WCHAR) );
3746 sprintfW( lpszPath, szFormat, session->hostName, session->hostPort );
3747 requestString = HTTP_BuildHeaderRequestString( request, szConnect, lpszPath, g_szHttp1_1 );
3748 HeapFree( GetProcessHeap(), 0, lpszPath );
3750 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3751 NULL, 0, NULL, NULL );
3752 len--; /* the nul terminator isn't needed */
3753 ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
3754 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
3755 ascii_req, len, NULL, NULL );
3756 HeapFree( GetProcessHeap(), 0, requestString );
3758 TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
3760 res = NETCON_send( &request->netConnection, ascii_req, len, 0, &cnt );
3761 HeapFree( GetProcessHeap(), 0, ascii_req );
3762 if (res != ERROR_SUCCESS)
3763 return res;
3765 responseLen = HTTP_GetResponseHeaders( request, TRUE );
3766 if (!responseLen)
3767 return ERROR_HTTP_INVALID_HEADER;
3769 return ERROR_SUCCESS;
3772 static void HTTP_InsertCookies(http_request_t *request)
3774 static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
3775 LPWSTR lpszCookies, lpszUrl = NULL;
3776 DWORD nCookieSize, size;
3777 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
3779 size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(request->path)) * sizeof(WCHAR);
3780 if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
3781 sprintfW( lpszUrl, szUrlForm, Host->lpszValue, request->path);
3783 if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
3785 int cnt = 0;
3786 static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
3788 size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
3789 if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
3791 cnt += sprintfW(lpszCookies, szCookie);
3792 InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
3793 strcatW(lpszCookies, szCrLf);
3795 HTTP_HttpAddRequestHeadersW(request, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
3796 HeapFree(GetProcessHeap(), 0, lpszCookies);
3799 HeapFree(GetProcessHeap(), 0, lpszUrl);
3802 static WORD HTTP_ParseDay(LPCWSTR day)
3804 static const WCHAR days[7][4] = {{ 's','u','n',0 },
3805 { 'm','o','n',0 },
3806 { 't','u','e',0 },
3807 { 'w','e','d',0 },
3808 { 't','h','u',0 },
3809 { 'f','r','i',0 },
3810 { 's','a','t',0 }};
3811 int i;
3812 for (i = 0; i < sizeof(days)/sizeof(*days); i++)
3813 if (!strcmpiW(day, days[i]))
3814 return i;
3816 /* Invalid */
3817 return 7;
3820 static WORD HTTP_ParseMonth(LPCWSTR month)
3822 static const WCHAR jan[] = { 'j','a','n',0 };
3823 static const WCHAR feb[] = { 'f','e','b',0 };
3824 static const WCHAR mar[] = { 'm','a','r',0 };
3825 static const WCHAR apr[] = { 'a','p','r',0 };
3826 static const WCHAR may[] = { 'm','a','y',0 };
3827 static const WCHAR jun[] = { 'j','u','n',0 };
3828 static const WCHAR jul[] = { 'j','u','l',0 };
3829 static const WCHAR aug[] = { 'a','u','g',0 };
3830 static const WCHAR sep[] = { 's','e','p',0 };
3831 static const WCHAR oct[] = { 'o','c','t',0 };
3832 static const WCHAR nov[] = { 'n','o','v',0 };
3833 static const WCHAR dec[] = { 'd','e','c',0 };
3835 if (!strcmpiW(month, jan)) return 1;
3836 if (!strcmpiW(month, feb)) return 2;
3837 if (!strcmpiW(month, mar)) return 3;
3838 if (!strcmpiW(month, apr)) return 4;
3839 if (!strcmpiW(month, may)) return 5;
3840 if (!strcmpiW(month, jun)) return 6;
3841 if (!strcmpiW(month, jul)) return 7;
3842 if (!strcmpiW(month, aug)) return 8;
3843 if (!strcmpiW(month, sep)) return 9;
3844 if (!strcmpiW(month, oct)) return 10;
3845 if (!strcmpiW(month, nov)) return 11;
3846 if (!strcmpiW(month, dec)) return 12;
3847 /* Invalid */
3848 return 0;
3851 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
3852 * optionally preceded by whitespace.
3853 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
3854 * st, and sets *str to the first character after the time format.
3856 static BOOL HTTP_ParseTime(SYSTEMTIME *st, LPCWSTR *str)
3858 LPCWSTR ptr = *str;
3859 WCHAR *nextPtr;
3860 unsigned long num;
3862 while (isspaceW(*ptr))
3863 ptr++;
3865 num = strtoulW(ptr, &nextPtr, 10);
3866 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
3868 ERR("unexpected time format %s\n", debugstr_w(ptr));
3869 return FALSE;
3871 if (num > 23)
3873 ERR("unexpected hour in time format %s\n", debugstr_w(ptr));
3874 return FALSE;
3876 ptr = nextPtr + 1;
3877 st->wHour = (WORD)num;
3878 num = strtoulW(ptr, &nextPtr, 10);
3879 if (!nextPtr || nextPtr <= ptr || *nextPtr != ':')
3881 ERR("unexpected time format %s\n", debugstr_w(ptr));
3882 return FALSE;
3884 if (num > 59)
3886 ERR("unexpected minute in time format %s\n", debugstr_w(ptr));
3887 return FALSE;
3889 ptr = nextPtr + 1;
3890 st->wMinute = (WORD)num;
3891 num = strtoulW(ptr, &nextPtr, 10);
3892 if (!nextPtr || nextPtr <= ptr)
3894 ERR("unexpected time format %s\n", debugstr_w(ptr));
3895 return FALSE;
3897 if (num > 59)
3899 ERR("unexpected second in time format %s\n", debugstr_w(ptr));
3900 return FALSE;
3902 ptr = nextPtr + 1;
3903 *str = ptr;
3904 st->wSecond = (WORD)num;
3905 return TRUE;
3908 static BOOL HTTP_ParseDateAsAsctime(LPCWSTR value, FILETIME *ft)
3910 static const WCHAR gmt[]= { 'G','M','T',0 };
3911 WCHAR day[4], *dayPtr, month[4], *monthPtr, *nextPtr;
3912 LPCWSTR ptr;
3913 SYSTEMTIME st = { 0 };
3914 unsigned long num;
3916 for (ptr = value, dayPtr = day; *ptr && !isspaceW(*ptr) &&
3917 dayPtr - day < sizeof(day) / sizeof(day[0]) - 1; ptr++, dayPtr++)
3918 *dayPtr = *ptr;
3919 *dayPtr = 0;
3920 st.wDayOfWeek = HTTP_ParseDay(day);
3921 if (st.wDayOfWeek >= 7)
3923 ERR("unexpected weekday %s\n", debugstr_w(day));
3924 return FALSE;
3927 while (isspaceW(*ptr))
3928 ptr++;
3930 for (monthPtr = month; !isspace(*ptr) &&
3931 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
3932 monthPtr++, ptr++)
3933 *monthPtr = *ptr;
3934 *monthPtr = 0;
3935 st.wMonth = HTTP_ParseMonth(month);
3936 if (!st.wMonth || st.wMonth > 12)
3938 ERR("unexpected month %s\n", debugstr_w(month));
3939 return FALSE;
3942 while (isspaceW(*ptr))
3943 ptr++;
3945 num = strtoulW(ptr, &nextPtr, 10);
3946 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
3948 ERR("unexpected day %s\n", debugstr_w(ptr));
3949 return FALSE;
3951 ptr = nextPtr;
3952 st.wDay = (WORD)num;
3954 while (isspaceW(*ptr))
3955 ptr++;
3957 if (!HTTP_ParseTime(&st, &ptr))
3958 return FALSE;
3960 while (isspaceW(*ptr))
3961 ptr++;
3963 num = strtoulW(ptr, &nextPtr, 10);
3964 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
3966 ERR("unexpected year %s\n", debugstr_w(ptr));
3967 return FALSE;
3969 ptr = nextPtr;
3970 st.wYear = (WORD)num;
3972 while (isspaceW(*ptr))
3973 ptr++;
3975 /* asctime() doesn't report a timezone, but some web servers do, so accept
3976 * with or without GMT.
3978 if (*ptr && strcmpW(ptr, gmt))
3980 ERR("unexpected timezone %s\n", debugstr_w(ptr));
3981 return FALSE;
3983 return SystemTimeToFileTime(&st, ft);
3986 static BOOL HTTP_ParseRfc1123Date(LPCWSTR value, FILETIME *ft)
3988 static const WCHAR gmt[]= { 'G','M','T',0 };
3989 WCHAR *nextPtr, day[4], month[4], *monthPtr;
3990 LPCWSTR ptr;
3991 unsigned long num;
3992 SYSTEMTIME st = { 0 };
3994 ptr = strchrW(value, ',');
3995 if (!ptr)
3996 return FALSE;
3997 if (ptr - value != 3)
3999 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4000 return FALSE;
4002 memcpy(day, value, (ptr - value) * sizeof(WCHAR));
4003 day[3] = 0;
4004 st.wDayOfWeek = HTTP_ParseDay(day);
4005 if (st.wDayOfWeek > 6)
4007 ERR("unexpected weekday %s\n", debugstr_wn(value, ptr - value));
4008 return FALSE;
4010 ptr++;
4012 while (isspaceW(*ptr))
4013 ptr++;
4015 num = strtoulW(ptr, &nextPtr, 10);
4016 if (!nextPtr || nextPtr <= ptr || !num || num > 31)
4018 ERR("unexpected day %s\n", debugstr_w(value));
4019 return FALSE;
4021 ptr = nextPtr;
4022 st.wDay = (WORD)num;
4024 while (isspaceW(*ptr))
4025 ptr++;
4027 for (monthPtr = month; !isspace(*ptr) &&
4028 monthPtr - month < sizeof(month) / sizeof(month[0]) - 1;
4029 monthPtr++, ptr++)
4030 *monthPtr = *ptr;
4031 *monthPtr = 0;
4032 st.wMonth = HTTP_ParseMonth(month);
4033 if (!st.wMonth || st.wMonth > 12)
4035 ERR("unexpected month %s\n", debugstr_w(month));
4036 return FALSE;
4039 while (isspaceW(*ptr))
4040 ptr++;
4042 num = strtoulW(ptr, &nextPtr, 10);
4043 if (!nextPtr || nextPtr <= ptr || num < 1601 || num > 30827)
4045 ERR("unexpected year %s\n", debugstr_w(value));
4046 return FALSE;
4048 ptr = nextPtr;
4049 st.wYear = (WORD)num;
4051 if (!HTTP_ParseTime(&st, &ptr))
4052 return FALSE;
4054 while (isspaceW(*ptr))
4055 ptr++;
4057 if (strcmpW(ptr, gmt))
4059 ERR("unexpected time zone %s\n", debugstr_w(ptr));
4060 return FALSE;
4062 return SystemTimeToFileTime(&st, ft);
4065 /* FIXME: only accepts dates in RFC 1123 format and asctime() format,
4066 * which may not be the only formats actually seen in the wild.
4067 * http://www.hackcraft.net/web/datetime/ suggests at least RFC 850 dates
4068 * should be accepted as well.
4070 static BOOL HTTP_ParseDate(LPCWSTR value, FILETIME *ft)
4072 BOOL ret;
4074 if (strchrW(value, ','))
4075 ret = HTTP_ParseRfc1123Date(value, ft);
4076 else
4078 ret = HTTP_ParseDateAsAsctime(value, ft);
4079 if (!ret)
4080 ERR("unexpected date format %s\n", debugstr_w(value));
4082 return ret;
4085 static void HTTP_ProcessExpires(http_request_t *request)
4087 BOOL expirationFound = FALSE;
4088 int headerIndex;
4090 /* Look for a Cache-Control header with a max-age directive, as it takes
4091 * precedence over the Expires header.
4093 headerIndex = HTTP_GetCustomHeaderIndex(request, szCache_Control, 0, FALSE);
4094 if (headerIndex != -1)
4096 LPHTTPHEADERW ccHeader = &request->custHeaders[headerIndex];
4097 LPWSTR ptr;
4099 for (ptr = ccHeader->lpszValue; ptr && *ptr; )
4101 LPWSTR comma = strchrW(ptr, ','), end, equal;
4103 if (comma)
4104 end = comma;
4105 else
4106 end = ptr + strlenW(ptr);
4107 for (equal = end - 1; equal > ptr && *equal != '='; equal--)
4109 if (*equal == '=')
4111 static const WCHAR max_age[] = {
4112 'm','a','x','-','a','g','e',0 };
4114 if (!strncmpiW(ptr, max_age, equal - ptr - 1))
4116 LPWSTR nextPtr;
4117 unsigned long age;
4119 age = strtoulW(equal + 1, &nextPtr, 10);
4120 if (nextPtr > equal + 1)
4122 LARGE_INTEGER ft;
4124 NtQuerySystemTime( &ft );
4125 /* Age is in seconds, FILETIME resolution is in
4126 * 100 nanosecond intervals.
4128 ft.QuadPart += age * (ULONGLONG)1000000;
4129 request->expires.dwLowDateTime = ft.u.LowPart;
4130 request->expires.dwHighDateTime = ft.u.HighPart;
4131 expirationFound = TRUE;
4135 if (comma)
4137 ptr = comma + 1;
4138 while (isspaceW(*ptr))
4139 ptr++;
4141 else
4142 ptr = NULL;
4145 if (!expirationFound)
4147 headerIndex = HTTP_GetCustomHeaderIndex(request, szExpires, 0, FALSE);
4148 if (headerIndex != -1)
4150 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4151 FILETIME ft;
4153 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4155 expirationFound = TRUE;
4156 request->expires = ft;
4160 if (!expirationFound)
4162 LARGE_INTEGER t;
4164 /* With no known age, default to 10 minutes until expiration. */
4165 NtQuerySystemTime( &t );
4166 t.QuadPart += 10 * 60 * (ULONGLONG)10000000;
4167 request->expires.dwLowDateTime = t.u.LowPart;
4168 request->expires.dwHighDateTime = t.u.HighPart;
4172 static void HTTP_ProcessLastModified(http_request_t *request)
4174 int headerIndex;
4176 headerIndex = HTTP_GetCustomHeaderIndex(request, szLast_Modified, 0, FALSE);
4177 if (headerIndex != -1)
4179 LPHTTPHEADERW expiresHeader = &request->custHeaders[headerIndex];
4180 FILETIME ft;
4182 if (HTTP_ParseDate(expiresHeader->lpszValue, &ft))
4183 request->last_modified = ft;
4187 static void HTTP_CacheRequest(http_request_t *request)
4189 WCHAR url[INTERNET_MAX_URL_LENGTH];
4190 WCHAR cacheFileName[MAX_PATH+1];
4191 BOOL b;
4193 b = HTTP_GetRequestURL(request, url);
4194 if(!b) {
4195 WARN("Could not get URL\n");
4196 return;
4199 b = CreateUrlCacheEntryW(url, request->contentLength, NULL, cacheFileName, 0);
4200 if(b) {
4201 HeapFree(GetProcessHeap(), 0, request->cacheFile);
4202 CloseHandle(request->hCacheFile);
4204 request->cacheFile = heap_strdupW(cacheFileName);
4205 request->hCacheFile = CreateFileW(request->cacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
4206 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
4207 if(request->hCacheFile == INVALID_HANDLE_VALUE) {
4208 WARN("Could not create file: %u\n", GetLastError());
4209 request->hCacheFile = NULL;
4211 }else {
4212 WARN("Could not create cache entry: %08x\n", GetLastError());
4216 /***********************************************************************
4217 * HTTP_HttpSendRequestW (internal)
4219 * Sends the specified request to the HTTP server
4221 * RETURNS
4222 * ERROR_SUCCESS on success
4223 * win32 error code on failure
4226 static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
4227 DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
4228 DWORD dwContentLength, BOOL bEndRequest)
4230 INT cnt;
4231 BOOL redirected = FALSE;
4232 LPWSTR requestString = NULL;
4233 INT responseLen;
4234 BOOL loop_next;
4235 INTERNET_ASYNC_RESULT iar;
4236 static const WCHAR szPost[] = { 'P','O','S','T',0 };
4237 static const WCHAR szContentLength[] =
4238 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4239 WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
4240 DWORD res;
4242 TRACE("--> %p\n", request);
4244 assert(request->hdr.htype == WH_HHTTPREQ);
4246 /* if the verb is NULL default to GET */
4247 if (!request->verb)
4248 request->verb = heap_strdupW(szGET);
4250 if (dwContentLength || strcmpW(request->verb, szGET))
4252 sprintfW(contentLengthStr, szContentLength, dwContentLength);
4253 HTTP_HttpAddRequestHeadersW(request, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
4254 request->bytesToWrite = dwContentLength;
4256 if (request->session->appInfo->agent)
4258 WCHAR *agent_header;
4259 static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4260 int len;
4262 len = strlenW(request->session->appInfo->agent) + strlenW(user_agent);
4263 agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
4264 sprintfW(agent_header, user_agent, request->session->appInfo->agent);
4266 HTTP_HttpAddRequestHeadersW(request, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4267 HeapFree(GetProcessHeap(), 0, agent_header);
4269 if (request->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
4271 static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4272 HTTP_HttpAddRequestHeadersW(request, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4274 if ((request->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(request->verb, szPost))
4276 static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4277 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4278 HTTP_HttpAddRequestHeadersW(request, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
4283 DWORD len;
4284 BOOL reusing_connection;
4285 char *ascii_req;
4287 loop_next = FALSE;
4289 /* like native, just in case the caller forgot to call InternetReadFile
4290 * for all the data */
4291 HTTP_DrainContent(request);
4292 if(redirected) {
4293 request->contentLength = ~0u;
4294 request->bytesToWrite = 0;
4297 if (TRACE_ON(wininet))
4299 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4300 TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(request->path));
4303 HTTP_FixURL(request);
4304 if (request->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
4306 HTTP_ProcessHeader(request, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
4308 HTTP_InsertAuthorization(request, request->authInfo, szAuthorization);
4309 HTTP_InsertAuthorization(request, request->proxyAuthInfo, szProxy_Authorization);
4311 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
4312 HTTP_InsertCookies(request);
4314 /* add the headers the caller supplied */
4315 if( lpszHeaders && dwHeaderLength )
4317 HTTP_HttpAddRequestHeadersW(request, lpszHeaders, dwHeaderLength,
4318 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
4321 if (request->session->appInfo->proxy && request->session->appInfo->proxy[0])
4323 WCHAR *url = HTTP_BuildProxyRequestUrl(request);
4324 requestString = HTTP_BuildHeaderRequestString(request, request->verb, url, request->version);
4325 HeapFree(GetProcessHeap(), 0, url);
4327 else
4328 requestString = HTTP_BuildHeaderRequestString(request, request->verb, request->path, request->version);
4331 TRACE("Request header -> %s\n", debugstr_w(requestString) );
4333 /* Send the request and store the results */
4334 if(NETCON_connected(&request->netConnection))
4335 reusing_connection = TRUE;
4336 else
4337 reusing_connection = FALSE;
4339 if ((res = HTTP_OpenConnection(request)) != ERROR_SUCCESS)
4340 goto lend;
4342 /* send the request as ASCII, tack on the optional data */
4343 if (!lpOptional || redirected)
4344 dwOptionalLength = 0;
4345 len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4346 NULL, 0, NULL, NULL );
4347 ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
4348 WideCharToMultiByte( CP_ACP, 0, requestString, -1,
4349 ascii_req, len, NULL, NULL );
4350 if( lpOptional )
4351 memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
4352 len = (len + dwOptionalLength - 1);
4353 ascii_req[len] = 0;
4354 TRACE("full request -> %s\n", debugstr_a(ascii_req) );
4356 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4357 INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
4359 res = NETCON_send(&request->netConnection, ascii_req, len, 0, &cnt);
4360 HeapFree( GetProcessHeap(), 0, ascii_req );
4362 request->bytesWritten = dwOptionalLength;
4364 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4365 INTERNET_STATUS_REQUEST_SENT,
4366 &len, sizeof(DWORD));
4368 if (bEndRequest)
4370 DWORD dwBufferSize;
4371 DWORD dwStatusCode;
4373 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4374 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4376 if (res != ERROR_SUCCESS)
4377 goto lend;
4379 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4380 /* FIXME: We should know that connection is closed before sending
4381 * headers. Otherwise wrong callbacks are executed */
4382 if(!responseLen && reusing_connection) {
4383 TRACE("Connection closed by server, reconnecting\n");
4384 loop_next = TRUE;
4385 continue;
4388 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4389 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
4390 sizeof(DWORD));
4392 HTTP_ProcessCookies(request);
4393 HTTP_ProcessExpires(request);
4394 HTTP_ProcessLastModified(request);
4396 res = set_content_length(request);
4397 if(res != ERROR_SUCCESS)
4398 goto lend;
4399 if(!request->contentLength)
4400 HTTP_FinishedReading(request);
4402 dwBufferSize = sizeof(dwStatusCode);
4403 if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
4404 &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
4405 dwStatusCode = 0;
4407 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
4409 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4410 dwBufferSize=sizeof(szNewLocation);
4411 if ((dwStatusCode == HTTP_STATUS_REDIRECT ||
4412 dwStatusCode == HTTP_STATUS_MOVED ||
4413 dwStatusCode == HTTP_STATUS_REDIRECT_METHOD) &&
4414 HTTP_HttpQueryInfoW(request,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
4416 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD))
4418 HeapFree(GetProcessHeap(), 0, request->verb);
4419 request->verb = heap_strdupW(szGET);
4421 HTTP_DrainContent(request);
4422 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4424 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4425 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4426 res = HTTP_HandleRedirect(request, new_url);
4427 if (res == ERROR_SUCCESS)
4429 HeapFree(GetProcessHeap(), 0, requestString);
4430 loop_next = TRUE;
4432 HeapFree( GetProcessHeap(), 0, new_url );
4434 redirected = TRUE;
4437 if (!(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
4439 WCHAR szAuthValue[2048];
4440 dwBufferSize=2048;
4441 if (dwStatusCode == HTTP_STATUS_DENIED)
4443 LPHTTPHEADERW Host = HTTP_GetHeader(request, hostW);
4444 DWORD dwIndex = 0;
4445 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4447 if (HTTP_DoAuthorization(request, szAuthValue,
4448 &request->authInfo,
4449 request->session->userName,
4450 request->session->password,
4451 Host->lpszValue))
4453 HeapFree(GetProcessHeap(), 0, requestString);
4454 loop_next = TRUE;
4455 break;
4459 if(!loop_next) {
4460 TRACE("Cleaning wrong authorization data\n");
4461 destroy_authinfo(request->authInfo);
4462 request->authInfo = NULL;
4465 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
4467 DWORD dwIndex = 0;
4468 while (HTTP_HttpQueryInfoW(request,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
4470 if (HTTP_DoAuthorization(request, szAuthValue,
4471 &request->proxyAuthInfo,
4472 request->session->appInfo->proxyUsername,
4473 request->session->appInfo->proxyPassword,
4474 NULL))
4476 loop_next = TRUE;
4477 break;
4481 if(!loop_next) {
4482 TRACE("Cleaning wrong proxy authorization data\n");
4483 destroy_authinfo(request->proxyAuthInfo);
4484 request->proxyAuthInfo = NULL;
4489 else
4490 res = ERROR_SUCCESS;
4492 while (loop_next);
4494 if(res == ERROR_SUCCESS)
4495 HTTP_CacheRequest(request);
4497 lend:
4499 HeapFree(GetProcessHeap(), 0, requestString);
4501 /* TODO: send notification for P3P header */
4503 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4505 if (res == ERROR_SUCCESS && request->bytesWritten == request->bytesToWrite)
4506 HTTP_ReceiveRequestData(request, TRUE);
4507 else
4509 iar.dwResult = (res==ERROR_SUCCESS ? (DWORD_PTR)request->hdr.hInternet : 0);
4510 iar.dwError = res;
4512 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4513 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4514 sizeof(INTERNET_ASYNC_RESULT));
4518 TRACE("<--\n");
4519 return res;
4522 /***********************************************************************
4524 * Helper functions for the HttpSendRequest(Ex) functions
4527 static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
4529 struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
4530 http_request_t *request = (http_request_t*) workRequest->hdr;
4532 TRACE("%p\n", request);
4534 HTTP_HttpSendRequestW(request, req->lpszHeader,
4535 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
4536 req->dwContentLength, req->bEndRequest);
4538 HeapFree(GetProcessHeap(), 0, req->lpszHeader);
4542 static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_PTR dwContext)
4544 INT responseLen;
4545 DWORD dwBufferSize;
4546 DWORD res = ERROR_SUCCESS;
4548 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4549 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
4551 responseLen = HTTP_GetResponseHeaders(request, TRUE);
4552 if (!responseLen)
4553 res = ERROR_HTTP_HEADER_NOT_FOUND;
4555 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4556 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
4558 /* process cookies here. Is this right? */
4559 HTTP_ProcessCookies(request);
4560 HTTP_ProcessExpires(request);
4561 HTTP_ProcessLastModified(request);
4563 if ((res = set_content_length( request )) == ERROR_SUCCESS) {
4564 if(!request->contentLength)
4565 HTTP_FinishedReading(request);
4568 if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
4570 DWORD dwCode,dwCodeLength = sizeof(DWORD);
4571 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
4572 && (dwCode == 302 || dwCode == 301 || dwCode == 303))
4574 WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
4575 dwBufferSize=sizeof(szNewLocation);
4576 if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
4578 if (strcmpW(request->verb, szGET) && strcmpW(request->verb, szHEAD))
4580 HeapFree(GetProcessHeap(), 0, request->verb);
4581 request->verb = heap_strdupW(szGET);
4583 HTTP_DrainContent(request);
4584 if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
4586 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
4587 new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
4588 res = HTTP_HandleRedirect(request, new_url);
4589 if (res == ERROR_SUCCESS)
4590 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, TRUE);
4591 HeapFree( GetProcessHeap(), 0, new_url );
4597 if (res == ERROR_SUCCESS) {
4598 HTTP_ReceiveRequestData(request, TRUE);
4599 }else {
4600 INTERNET_ASYNC_RESULT iar = {0, res};
4602 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
4603 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
4604 sizeof(INTERNET_ASYNC_RESULT));
4607 return res;
4610 /***********************************************************************
4611 * HttpEndRequestA (WININET.@)
4613 * Ends an HTTP request that was started by HttpSendRequestEx
4615 * RETURNS
4616 * TRUE if successful
4617 * FALSE on failure
4620 BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
4621 LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4623 TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
4625 if (lpBuffersOut)
4627 SetLastError(ERROR_INVALID_PARAMETER);
4628 return FALSE;
4631 return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
4634 static void AsyncHttpEndRequestProc(WORKREQUEST *work)
4636 struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
4637 http_request_t *request = (http_request_t*)work->hdr;
4639 TRACE("%p\n", request);
4641 HTTP_HttpEndRequestW(request, req->dwFlags, req->dwContext);
4644 /***********************************************************************
4645 * HttpEndRequestW (WININET.@)
4647 * Ends an HTTP request that was started by HttpSendRequestEx
4649 * RETURNS
4650 * TRUE if successful
4651 * FALSE on failure
4654 BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
4655 LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
4657 http_request_t *request;
4658 DWORD res;
4660 TRACE("-->\n");
4662 if (lpBuffersOut)
4664 SetLastError(ERROR_INVALID_PARAMETER);
4665 return FALSE;
4668 request = (http_request_t*) get_handle_object( hRequest );
4670 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
4672 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
4673 if (request)
4674 WININET_Release( &request->hdr );
4675 return FALSE;
4677 request->hdr.dwFlags |= dwFlags;
4679 if (request->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4681 WORKREQUEST work;
4682 struct WORKREQ_HTTPENDREQUESTW *work_endrequest;
4684 work.asyncproc = AsyncHttpEndRequestProc;
4685 work.hdr = WININET_AddRef( &request->hdr );
4687 work_endrequest = &work.u.HttpEndRequestW;
4688 work_endrequest->dwFlags = dwFlags;
4689 work_endrequest->dwContext = dwContext;
4691 INTERNET_AsyncCall(&work);
4692 res = ERROR_IO_PENDING;
4694 else
4695 res = HTTP_HttpEndRequestW(request, dwFlags, dwContext);
4697 WININET_Release( &request->hdr );
4698 TRACE("%u <--\n", res);
4699 if(res != ERROR_SUCCESS)
4700 SetLastError(res);
4701 return res == ERROR_SUCCESS;
4704 /***********************************************************************
4705 * HttpSendRequestExA (WININET.@)
4707 * Sends the specified request to the HTTP server and allows chunked
4708 * transfers.
4710 * RETURNS
4711 * Success: TRUE
4712 * Failure: FALSE, call GetLastError() for more information.
4714 BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
4715 LPINTERNET_BUFFERSA lpBuffersIn,
4716 LPINTERNET_BUFFERSA lpBuffersOut,
4717 DWORD dwFlags, DWORD_PTR dwContext)
4719 INTERNET_BUFFERSW BuffersInW;
4720 BOOL rc = FALSE;
4721 DWORD headerlen;
4722 LPWSTR header = NULL;
4724 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4725 lpBuffersOut, dwFlags, dwContext);
4727 if (lpBuffersIn)
4729 BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
4730 if (lpBuffersIn->lpcszHeader)
4732 headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
4733 lpBuffersIn->dwHeadersLength,0,0);
4734 header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
4735 if (!(BuffersInW.lpcszHeader = header))
4737 SetLastError(ERROR_OUTOFMEMORY);
4738 return FALSE;
4740 BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
4741 lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4742 header, headerlen);
4744 else
4745 BuffersInW.lpcszHeader = NULL;
4746 BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
4747 BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
4748 BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
4749 BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
4750 BuffersInW.Next = NULL;
4753 rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
4755 HeapFree(GetProcessHeap(),0,header);
4757 return rc;
4760 /***********************************************************************
4761 * HttpSendRequestExW (WININET.@)
4763 * Sends the specified request to the HTTP server and allows chunked
4764 * transfers
4766 * RETURNS
4767 * Success: TRUE
4768 * Failure: FALSE, call GetLastError() for more information.
4770 BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
4771 LPINTERNET_BUFFERSW lpBuffersIn,
4772 LPINTERNET_BUFFERSW lpBuffersOut,
4773 DWORD dwFlags, DWORD_PTR dwContext)
4775 http_request_t *request;
4776 http_session_t *session;
4777 appinfo_t *hIC;
4778 DWORD res;
4780 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
4781 lpBuffersOut, dwFlags, dwContext);
4783 request = (http_request_t*) get_handle_object( hRequest );
4785 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
4787 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4788 goto lend;
4791 session = request->session;
4792 assert(session->hdr.htype == WH_HHTTPSESSION);
4793 hIC = session->appInfo;
4794 assert(hIC->hdr.htype == WH_HINIT);
4796 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4798 WORKREQUEST workRequest;
4799 struct WORKREQ_HTTPSENDREQUESTW *req;
4801 workRequest.asyncproc = AsyncHttpSendRequestProc;
4802 workRequest.hdr = WININET_AddRef( &request->hdr );
4803 req = &workRequest.u.HttpSendRequestW;
4804 if (lpBuffersIn)
4806 DWORD size = 0;
4808 if (lpBuffersIn->lpcszHeader)
4810 if (lpBuffersIn->dwHeadersLength == ~0u)
4811 size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
4812 else
4813 size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
4815 req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
4816 memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
4818 else req->lpszHeader = NULL;
4820 req->dwHeaderLength = size / sizeof(WCHAR);
4821 req->lpOptional = lpBuffersIn->lpvBuffer;
4822 req->dwOptionalLength = lpBuffersIn->dwBufferLength;
4823 req->dwContentLength = lpBuffersIn->dwBufferTotal;
4825 else
4827 req->lpszHeader = NULL;
4828 req->dwHeaderLength = 0;
4829 req->lpOptional = NULL;
4830 req->dwOptionalLength = 0;
4831 req->dwContentLength = 0;
4834 req->bEndRequest = FALSE;
4836 INTERNET_AsyncCall(&workRequest);
4838 * This is from windows.
4840 res = ERROR_IO_PENDING;
4842 else
4844 if (lpBuffersIn)
4845 res = HTTP_HttpSendRequestW(request, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
4846 lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
4847 lpBuffersIn->dwBufferTotal, FALSE);
4848 else
4849 res = HTTP_HttpSendRequestW(request, NULL, 0, NULL, 0, 0, FALSE);
4852 lend:
4853 if ( request )
4854 WININET_Release( &request->hdr );
4856 TRACE("<---\n");
4857 SetLastError(res);
4858 return res == ERROR_SUCCESS;
4861 /***********************************************************************
4862 * HttpSendRequestW (WININET.@)
4864 * Sends the specified request to the HTTP server
4866 * RETURNS
4867 * TRUE on success
4868 * FALSE on failure
4871 BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
4872 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4874 http_request_t *request;
4875 http_session_t *session = NULL;
4876 appinfo_t *hIC = NULL;
4877 DWORD res = ERROR_SUCCESS;
4879 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
4880 debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
4882 request = (http_request_t*) get_handle_object( hHttpRequest );
4883 if (NULL == request || request->hdr.htype != WH_HHTTPREQ)
4885 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4886 goto lend;
4889 session = request->session;
4890 if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
4892 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4893 goto lend;
4896 hIC = session->appInfo;
4897 if (NULL == hIC || hIC->hdr.htype != WH_HINIT)
4899 res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4900 goto lend;
4903 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
4905 WORKREQUEST workRequest;
4906 struct WORKREQ_HTTPSENDREQUESTW *req;
4908 workRequest.asyncproc = AsyncHttpSendRequestProc;
4909 workRequest.hdr = WININET_AddRef( &request->hdr );
4910 req = &workRequest.u.HttpSendRequestW;
4911 if (lpszHeaders)
4913 DWORD size;
4915 if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
4916 else size = dwHeaderLength * sizeof(WCHAR);
4918 req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
4919 memcpy(req->lpszHeader, lpszHeaders, size);
4921 else
4922 req->lpszHeader = 0;
4923 req->dwHeaderLength = dwHeaderLength;
4924 req->lpOptional = lpOptional;
4925 req->dwOptionalLength = dwOptionalLength;
4926 req->dwContentLength = dwOptionalLength;
4927 req->bEndRequest = TRUE;
4929 INTERNET_AsyncCall(&workRequest);
4931 * This is from windows.
4933 res = ERROR_IO_PENDING;
4935 else
4937 res = HTTP_HttpSendRequestW(request, lpszHeaders,
4938 dwHeaderLength, lpOptional, dwOptionalLength,
4939 dwOptionalLength, TRUE);
4941 lend:
4942 if( request )
4943 WININET_Release( &request->hdr );
4945 SetLastError(res);
4946 return res == ERROR_SUCCESS;
4949 /***********************************************************************
4950 * HttpSendRequestA (WININET.@)
4952 * Sends the specified request to the HTTP server
4954 * RETURNS
4955 * TRUE on success
4956 * FALSE on failure
4959 BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
4960 DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
4962 BOOL result;
4963 LPWSTR szHeaders=NULL;
4964 DWORD nLen=dwHeaderLength;
4965 if(lpszHeaders!=NULL)
4967 nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
4968 szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
4969 MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
4971 result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
4972 HeapFree(GetProcessHeap(),0,szHeaders);
4973 return result;
4976 /***********************************************************************
4977 * HTTPSESSION_Destroy (internal)
4979 * Deallocate session handle
4982 static void HTTPSESSION_Destroy(object_header_t *hdr)
4984 http_session_t *session = (http_session_t*) hdr;
4986 TRACE("%p\n", session);
4988 WININET_Release(&session->appInfo->hdr);
4990 HeapFree(GetProcessHeap(), 0, session->hostName);
4991 HeapFree(GetProcessHeap(), 0, session->serverName);
4992 HeapFree(GetProcessHeap(), 0, session->password);
4993 HeapFree(GetProcessHeap(), 0, session->userName);
4996 static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
4998 switch(option) {
4999 case INTERNET_OPTION_HANDLE_TYPE:
5000 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5002 if (*size < sizeof(ULONG))
5003 return ERROR_INSUFFICIENT_BUFFER;
5005 *size = sizeof(DWORD);
5006 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
5007 return ERROR_SUCCESS;
5010 return INET_QueryOption(hdr, option, buffer, size, unicode);
5013 static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
5015 http_session_t *ses = (http_session_t*)hdr;
5017 switch(option) {
5018 case INTERNET_OPTION_USERNAME:
5020 HeapFree(GetProcessHeap(), 0, ses->userName);
5021 if (!(ses->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5022 return ERROR_SUCCESS;
5024 case INTERNET_OPTION_PASSWORD:
5026 HeapFree(GetProcessHeap(), 0, ses->password);
5027 if (!(ses->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
5028 return ERROR_SUCCESS;
5030 default: break;
5033 return ERROR_INTERNET_INVALID_OPTION;
5036 static const object_vtbl_t HTTPSESSIONVtbl = {
5037 HTTPSESSION_Destroy,
5038 NULL,
5039 HTTPSESSION_QueryOption,
5040 HTTPSESSION_SetOption,
5041 NULL,
5042 NULL,
5043 NULL,
5044 NULL,
5045 NULL
5049 /***********************************************************************
5050 * HTTP_Connect (internal)
5052 * Create http session handle
5054 * RETURNS
5055 * HINTERNET a session handle on success
5056 * NULL on failure
5059 DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
5060 INTERNET_PORT serverPort, LPCWSTR lpszUserName,
5061 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
5062 DWORD dwInternalFlags, HINTERNET *ret)
5064 http_session_t *session = NULL;
5066 TRACE("-->\n");
5068 if (!lpszServerName || !lpszServerName[0])
5069 return ERROR_INVALID_PARAMETER;
5071 assert( hIC->hdr.htype == WH_HINIT );
5073 session = alloc_object(&hIC->hdr, &HTTPSESSIONVtbl, sizeof(http_session_t));
5074 if (!session)
5075 return ERROR_OUTOFMEMORY;
5078 * According to my tests. The name is not resolved until a request is sent
5081 session->hdr.htype = WH_HHTTPSESSION;
5082 session->hdr.dwFlags = dwFlags;
5083 session->hdr.dwContext = dwContext;
5084 session->hdr.dwInternalFlags |= dwInternalFlags;
5086 WININET_AddRef( &hIC->hdr );
5087 session->appInfo = hIC;
5088 list_add_head( &hIC->hdr.children, &session->hdr.entry );
5090 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
5091 if(hIC->proxyBypass)
5092 FIXME("Proxy bypass is ignored.\n");
5094 session->serverName = heap_strdupW(lpszServerName);
5095 session->hostName = heap_strdupW(lpszServerName);
5096 if (lpszUserName && lpszUserName[0])
5097 session->userName = heap_strdupW(lpszUserName);
5098 if (lpszPassword && lpszPassword[0])
5099 session->password = heap_strdupW(lpszPassword);
5100 session->serverPort = serverPort;
5101 session->hostPort = serverPort;
5103 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5104 if (!(session->hdr.dwInternalFlags & INET_OPENURL))
5106 INTERNET_SendCallback(&hIC->hdr, dwContext,
5107 INTERNET_STATUS_HANDLE_CREATED, &session->hdr.hInternet,
5108 sizeof(HINTERNET));
5112 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5113 * windows
5116 TRACE("%p --> %p\n", hIC, session);
5118 *ret = session->hdr.hInternet;
5119 return ERROR_SUCCESS;
5123 /***********************************************************************
5124 * HTTP_OpenConnection (internal)
5126 * Connect to a web server
5128 * RETURNS
5130 * TRUE on success
5131 * FALSE on failure
5133 static DWORD HTTP_OpenConnection(http_request_t *request)
5135 http_session_t *session;
5136 appinfo_t *hIC = NULL;
5137 char szaddr[INET6_ADDRSTRLEN];
5138 const void *addr;
5139 DWORD res = ERROR_SUCCESS;
5141 TRACE("-->\n");
5144 if (request->hdr.htype != WH_HHTTPREQ)
5146 res = ERROR_INVALID_PARAMETER;
5147 goto lend;
5150 if (NETCON_connected(&request->netConnection))
5151 goto lend;
5152 if ((res = HTTP_ResolveName(request)) != ERROR_SUCCESS) goto lend;
5154 session = request->session;
5156 hIC = session->appInfo;
5157 switch (session->socketAddress.ss_family)
5159 case AF_INET:
5160 addr = &((struct sockaddr_in *)&session->socketAddress)->sin_addr;
5161 break;
5162 case AF_INET6:
5163 addr = &((struct sockaddr_in6 *)&session->socketAddress)->sin6_addr;
5164 break;
5165 default:
5166 WARN("unsupported family %d\n", session->socketAddress.ss_family);
5167 return ERROR_INTERNET_NAME_NOT_RESOLVED;
5169 inet_ntop(session->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
5170 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5171 INTERNET_STATUS_CONNECTING_TO_SERVER,
5172 szaddr,
5173 strlen(szaddr)+1);
5175 res = NETCON_create(&request->netConnection, session->socketAddress.ss_family, SOCK_STREAM, 0);
5176 if (res != ERROR_SUCCESS)
5178 WARN("Socket creation failed: %u\n", res);
5179 goto lend;
5182 res = NETCON_connect(&request->netConnection, (struct sockaddr *)&session->socketAddress,
5183 session->sa_len);
5184 if(res != ERROR_SUCCESS)
5185 goto lend;
5187 INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
5188 INTERNET_STATUS_CONNECTED_TO_SERVER,
5189 szaddr, strlen(szaddr)+1);
5191 if (request->hdr.dwFlags & INTERNET_FLAG_SECURE)
5193 /* Note: we differ from Microsoft's WinINet here. they seem to have
5194 * a bug that causes no status callbacks to be sent when starting
5195 * a tunnel to a proxy server using the CONNECT verb. i believe our
5196 * behaviour to be more correct and to not cause any incompatibilities
5197 * because using a secure connection through a proxy server is a rare
5198 * case that would be hard for anyone to depend on */
5199 if (hIC->proxy && (res = HTTP_SecureProxyConnect(request)) != ERROR_SUCCESS) {
5200 HTTPREQ_CloseConnection(&request->hdr);
5201 goto lend;
5204 res = NETCON_secure_connect(&request->netConnection, session->hostName);
5205 if(res != ERROR_SUCCESS)
5207 WARN("Couldn't connect securely to host\n");
5209 if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
5210 res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
5211 || res == ERROR_INTERNET_INVALID_CA
5212 || res == ERROR_INTERNET_SEC_CERT_NO_REV
5213 || res == ERROR_INTERNET_SEC_CERT_REV_FAILED
5214 || res == ERROR_INTERNET_SEC_CERT_REVOKED
5215 || res == ERROR_INTERNET_SEC_INVALID_CERT
5216 || res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
5217 res = ERROR_INTERNET_SEC_CERT_ERRORS;
5219 HTTPREQ_CloseConnection(&request->hdr);
5220 goto lend;
5225 lend:
5226 reset_data_stream(request);
5228 TRACE("%d <--\n", res);
5229 return res;
5233 /***********************************************************************
5234 * HTTP_clear_response_headers (internal)
5236 * clear out any old response headers
5238 static void HTTP_clear_response_headers( http_request_t *request )
5240 DWORD i;
5242 for( i=0; i<request->nCustHeaders; i++)
5244 if( !request->custHeaders[i].lpszField )
5245 continue;
5246 if( !request->custHeaders[i].lpszValue )
5247 continue;
5248 if ( request->custHeaders[i].wFlags & HDR_ISREQUEST )
5249 continue;
5250 HTTP_DeleteCustomHeader( request, i );
5251 i--;
5255 /***********************************************************************
5256 * HTTP_GetResponseHeaders (internal)
5258 * Read server response
5260 * RETURNS
5262 * TRUE on success
5263 * FALSE on error
5265 static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
5267 INT cbreaks = 0;
5268 WCHAR buffer[MAX_REPLY_LEN];
5269 DWORD buflen = MAX_REPLY_LEN;
5270 BOOL bSuccess = FALSE;
5271 INT rc = 0;
5272 char bufferA[MAX_REPLY_LEN];
5273 LPWSTR status_code = NULL, status_text = NULL;
5274 DWORD cchMaxRawHeaders = 1024;
5275 LPWSTR lpszRawHeaders = NULL;
5276 LPWSTR temp;
5277 DWORD cchRawHeaders = 0;
5278 BOOL codeHundred = FALSE;
5280 TRACE("-->\n");
5282 if (!NETCON_connected(&request->netConnection))
5283 goto lend;
5285 do {
5286 static const WCHAR szHundred[] = {'1','0','0',0};
5288 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5290 buflen = MAX_REPLY_LEN;
5291 if (!read_line(request, bufferA, &buflen))
5292 goto lend;
5294 /* clear old response headers (eg. from a redirect response) */
5295 if (clear) {
5296 HTTP_clear_response_headers( request );
5297 clear = FALSE;
5300 rc += buflen;
5301 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5302 /* check is this a status code line? */
5303 if (!strncmpW(buffer, g_szHttp1_0, 4))
5305 /* split the version from the status code */
5306 status_code = strchrW( buffer, ' ' );
5307 if( !status_code )
5308 goto lend;
5309 *status_code++=0;
5311 /* split the status code from the status text */
5312 status_text = strchrW( status_code, ' ' );
5313 if( !status_text )
5314 goto lend;
5315 *status_text++=0;
5317 TRACE("version [%s] status code [%s] status text [%s]\n",
5318 debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
5320 codeHundred = (!strcmpW(status_code, szHundred));
5322 else if (!codeHundred)
5324 WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
5326 HeapFree(GetProcessHeap(), 0, request->version);
5327 HeapFree(GetProcessHeap(), 0, request->statusText);
5329 request->version = heap_strdupW(g_szHttp1_0);
5330 request->statusText = heap_strdupW(szOK);
5332 HeapFree(GetProcessHeap(), 0, request->rawHeaders);
5333 request->rawHeaders = heap_strdupW(szDefaultHeader);
5335 bSuccess = TRUE;
5336 goto lend;
5338 } while (codeHundred);
5340 /* Add status code */
5341 HTTP_ProcessHeader(request, szStatus, status_code,
5342 HTTP_ADDHDR_FLAG_REPLACE);
5344 HeapFree(GetProcessHeap(),0,request->version);
5345 HeapFree(GetProcessHeap(),0,request->statusText);
5347 request->version = heap_strdupW(buffer);
5348 request->statusText = heap_strdupW(status_text);
5350 /* Restore the spaces */
5351 *(status_code-1) = ' ';
5352 *(status_text-1) = ' ';
5354 /* regenerate raw headers */
5355 lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5356 if (!lpszRawHeaders) goto lend;
5358 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5359 cchMaxRawHeaders *= 2;
5360 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5361 if (temp == NULL) goto lend;
5362 lpszRawHeaders = temp;
5363 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5364 cchRawHeaders += (buflen-1);
5365 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5366 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5367 lpszRawHeaders[cchRawHeaders] = '\0';
5369 /* Parse each response line */
5372 buflen = MAX_REPLY_LEN;
5373 if (read_line(request, bufferA, &buflen))
5375 LPWSTR * pFieldAndValue;
5377 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
5379 if (!bufferA[0]) break;
5380 MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
5382 pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
5383 if (pFieldAndValue)
5385 while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
5386 cchMaxRawHeaders *= 2;
5387 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
5388 if (temp == NULL) goto lend;
5389 lpszRawHeaders = temp;
5390 memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
5391 cchRawHeaders += (buflen-1);
5392 memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
5393 cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
5394 lpszRawHeaders[cchRawHeaders] = '\0';
5396 HTTP_ProcessHeader(request, pFieldAndValue[0], pFieldAndValue[1],
5397 HTTP_ADDREQ_FLAG_ADD );
5399 HTTP_FreeTokens(pFieldAndValue);
5402 else
5404 cbreaks++;
5405 if (cbreaks >= 2)
5406 break;
5408 }while(1);
5410 /* make sure the response header is terminated with an empty line. Some apps really
5411 truly care about that empty line being there for some reason. Just add it to the
5412 header. */
5413 if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
5415 cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
5416 temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
5417 if (temp == NULL) goto lend;
5418 lpszRawHeaders = temp;
5421 memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
5423 HeapFree(GetProcessHeap(), 0, request->rawHeaders);
5424 request->rawHeaders = lpszRawHeaders;
5425 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
5426 bSuccess = TRUE;
5428 lend:
5430 TRACE("<--\n");
5431 if (bSuccess)
5432 return rc;
5433 else
5435 HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
5436 return 0;
5440 /***********************************************************************
5441 * HTTP_InterpretHttpHeader (internal)
5443 * Parse server response
5445 * RETURNS
5447 * Pointer to array of field, value, NULL on success.
5448 * NULL on error.
5450 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
5452 LPWSTR * pTokenPair;
5453 LPWSTR pszColon;
5454 INT len;
5456 pTokenPair = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pTokenPair)*3);
5458 pszColon = strchrW(buffer, ':');
5459 /* must have two tokens */
5460 if (!pszColon)
5462 HTTP_FreeTokens(pTokenPair);
5463 if (buffer[0])
5464 TRACE("No ':' in line: %s\n", debugstr_w(buffer));
5465 return NULL;
5468 pTokenPair[0] = HeapAlloc(GetProcessHeap(), 0, (pszColon - buffer + 1) * sizeof(WCHAR));
5469 if (!pTokenPair[0])
5471 HTTP_FreeTokens(pTokenPair);
5472 return NULL;
5474 memcpy(pTokenPair[0], buffer, (pszColon - buffer) * sizeof(WCHAR));
5475 pTokenPair[0][pszColon - buffer] = '\0';
5477 /* skip colon */
5478 pszColon++;
5479 len = strlenW(pszColon);
5480 pTokenPair[1] = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
5481 if (!pTokenPair[1])
5483 HTTP_FreeTokens(pTokenPair);
5484 return NULL;
5486 memcpy(pTokenPair[1], pszColon, (len + 1) * sizeof(WCHAR));
5488 strip_spaces(pTokenPair[0]);
5489 strip_spaces(pTokenPair[1]);
5491 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair[0]), debugstr_w(pTokenPair[1]));
5492 return pTokenPair;
5495 /***********************************************************************
5496 * HTTP_ProcessHeader (internal)
5498 * Stuff header into header tables according to <dwModifier>
5502 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5504 static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
5506 LPHTTPHEADERW lphttpHdr = NULL;
5507 INT index = -1;
5508 BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
5509 DWORD res = ERROR_HTTP_INVALID_HEADER;
5511 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
5513 /* REPLACE wins out over ADD */
5514 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5515 dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
5517 if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
5518 index = -1;
5519 else
5520 index = HTTP_GetCustomHeaderIndex(request, field, 0, request_only);
5522 if (index >= 0)
5524 if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
5525 return ERROR_HTTP_INVALID_HEADER;
5526 lphttpHdr = &request->custHeaders[index];
5528 else if (value)
5530 HTTPHEADERW hdr;
5532 hdr.lpszField = (LPWSTR)field;
5533 hdr.lpszValue = (LPWSTR)value;
5534 hdr.wFlags = hdr.wCount = 0;
5536 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5537 hdr.wFlags |= HDR_ISREQUEST;
5539 return HTTP_InsertCustomHeader(request, &hdr);
5541 /* no value to delete */
5542 else return ERROR_SUCCESS;
5544 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5545 lphttpHdr->wFlags |= HDR_ISREQUEST;
5546 else
5547 lphttpHdr->wFlags &= ~HDR_ISREQUEST;
5549 if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
5551 HTTP_DeleteCustomHeader( request, index );
5553 if (value)
5555 HTTPHEADERW hdr;
5557 hdr.lpszField = (LPWSTR)field;
5558 hdr.lpszValue = (LPWSTR)value;
5559 hdr.wFlags = hdr.wCount = 0;
5561 if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
5562 hdr.wFlags |= HDR_ISREQUEST;
5564 return HTTP_InsertCustomHeader(request, &hdr);
5567 return ERROR_SUCCESS;
5569 else if (dwModifier & COALESCEFLAGS)
5571 LPWSTR lpsztmp;
5572 WCHAR ch = 0;
5573 INT len = 0;
5574 INT origlen = strlenW(lphttpHdr->lpszValue);
5575 INT valuelen = strlenW(value);
5577 if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
5579 ch = ',';
5580 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5582 else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5584 ch = ';';
5585 lphttpHdr->wFlags |= HDR_COMMADELIMITED;
5588 len = origlen + valuelen + ((ch > 0) ? 2 : 0);
5590 lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
5591 if (lpsztmp)
5593 lphttpHdr->lpszValue = lpsztmp;
5594 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5595 if (ch > 0)
5597 lphttpHdr->lpszValue[origlen] = ch;
5598 origlen++;
5599 lphttpHdr->lpszValue[origlen] = ' ';
5600 origlen++;
5603 memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
5604 lphttpHdr->lpszValue[len] = '\0';
5605 res = ERROR_SUCCESS;
5607 else
5609 WARN("HeapReAlloc (%d bytes) failed\n",len+1);
5610 res = ERROR_OUTOFMEMORY;
5613 TRACE("<-- %d\n", res);
5614 return res;
5618 /***********************************************************************
5619 * HTTP_FinishedReading (internal)
5621 * Called when all content from server has been read by client.
5624 static BOOL HTTP_FinishedReading(http_request_t *request)
5626 BOOL keepalive = HTTP_KeepAlive(request);
5628 TRACE("\n");
5631 if (!keepalive)
5633 HTTPREQ_CloseConnection(&request->hdr);
5636 /* FIXME: store data in the URL cache here */
5638 return TRUE;
5642 /***********************************************************************
5643 * HTTP_GetCustomHeaderIndex (internal)
5645 * Return index of custom header from header array
5648 static INT HTTP_GetCustomHeaderIndex(http_request_t *request, LPCWSTR lpszField,
5649 int requested_index, BOOL request_only)
5651 DWORD index;
5653 TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
5655 for (index = 0; index < request->nCustHeaders; index++)
5657 if (strcmpiW(request->custHeaders[index].lpszField, lpszField))
5658 continue;
5660 if (request_only && !(request->custHeaders[index].wFlags & HDR_ISREQUEST))
5661 continue;
5663 if (!request_only && (request->custHeaders[index].wFlags & HDR_ISREQUEST))
5664 continue;
5666 if (requested_index == 0)
5667 break;
5668 requested_index --;
5671 if (index >= request->nCustHeaders)
5672 index = -1;
5674 TRACE("Return: %d\n", index);
5675 return index;
5679 /***********************************************************************
5680 * HTTP_InsertCustomHeader (internal)
5682 * Insert header into array
5685 static DWORD HTTP_InsertCustomHeader(http_request_t *request, LPHTTPHEADERW lpHdr)
5687 INT count;
5688 LPHTTPHEADERW lph = NULL;
5690 TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
5691 count = request->nCustHeaders + 1;
5692 if (count > 1)
5693 lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, request->custHeaders, sizeof(HTTPHEADERW) * count);
5694 else
5695 lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
5697 if (!lph)
5698 return ERROR_OUTOFMEMORY;
5700 request->custHeaders = lph;
5701 request->custHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
5702 request->custHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
5703 request->custHeaders[count-1].wFlags = lpHdr->wFlags;
5704 request->custHeaders[count-1].wCount= lpHdr->wCount;
5705 request->nCustHeaders++;
5707 return ERROR_SUCCESS;
5711 /***********************************************************************
5712 * HTTP_DeleteCustomHeader (internal)
5714 * Delete header from array
5715 * If this function is called, the indexs may change.
5717 static BOOL HTTP_DeleteCustomHeader(http_request_t *request, DWORD index)
5719 if( request->nCustHeaders <= 0 )
5720 return FALSE;
5721 if( index >= request->nCustHeaders )
5722 return FALSE;
5723 request->nCustHeaders--;
5725 HeapFree(GetProcessHeap(), 0, request->custHeaders[index].lpszField);
5726 HeapFree(GetProcessHeap(), 0, request->custHeaders[index].lpszValue);
5728 memmove( &request->custHeaders[index], &request->custHeaders[index+1],
5729 (request->nCustHeaders - index)* sizeof(HTTPHEADERW) );
5730 memset( &request->custHeaders[request->nCustHeaders], 0, sizeof(HTTPHEADERW) );
5732 return TRUE;
5736 /***********************************************************************
5737 * HTTP_VerifyValidHeader (internal)
5739 * Verify the given header is not invalid for the given http request
5742 static BOOL HTTP_VerifyValidHeader(http_request_t *request, LPCWSTR field)
5744 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
5745 if (!strcmpW(request->version, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
5746 return ERROR_HTTP_INVALID_HEADER;
5748 return ERROR_SUCCESS;
5751 /***********************************************************************
5752 * IsHostInProxyBypassList (@)
5754 * Undocumented
5757 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
5759 FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
5760 return FALSE;
5763 /***********************************************************************
5764 * InternetShowSecurityInfoByURLA (@)
5766 BOOL WINAPI InternetShowSecurityInfoByURLA(LPCSTR url, HWND window)
5768 FIXME("stub: %s %p\n", url, window);
5769 return FALSE;
5772 /***********************************************************************
5773 * InternetShowSecurityInfoByURLW (@)
5775 BOOL WINAPI InternetShowSecurityInfoByURLW(LPCWSTR url, HWND window)
5777 FIXME("stub: %s %p\n", debugstr_w(url), window);
5778 return FALSE;