ReactOS  0.4.14-dev-606-g14ebc0b
http.c
Go to the documentation of this file.
1 /*
2  * Wininet - HTTP Implementation
3  *
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
11  *
12  * Ulrich Czekalla
13  * David Hammerton
14  *
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.
19  *
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.
24  *
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
28  */
29 
30 #include "config.h"
31 
32 #include <stdlib.h>
33 
34 #ifdef HAVE_ZLIB
35 # include <zlib.h>
36 #endif
37 
38 #include "winsock2.h"
39 #include "ws2ipdef.h"
40 
41 #include <stdarg.h>
42 #include <stdio.h>
43 #include <time.h>
44 #include <assert.h>
45 #include <errno.h>
46 #include <limits.h>
47 
48 #include "windef.h"
49 #include "winbase.h"
50 #include "wininet.h"
51 #include "winerror.h"
52 #include "winternl.h"
53 #define NO_SHLWAPI_STREAM
54 #define NO_SHLWAPI_REG
55 #define NO_SHLWAPI_GDI
56 #include "shlwapi.h"
57 #include "sspi.h"
58 #include "wincrypt.h"
59 #include "winuser.h"
60 
61 #include "internet.h"
62 #include "wine/debug.h"
63 #include "wine/exception.h"
64 #include "wine/unicode.h"
65 
67 
68 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
69 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
70 static const WCHAR szOK[] = {'O','K',0};
71 static const WCHAR hostW[] = { 'H','o','s','t',0 };
72 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
73 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
74 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
75 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
76 static const WCHAR szGET[] = { 'G','E','T', 0 };
77 static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
78 
79 static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
80 static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
81 static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
82 static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
83 static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
84 static const WCHAR szAge[] = { 'A','g','e',0 };
85 static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
86 static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
87 static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
88 static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
89 static const WCHAR szContent_Disposition[] = { 'C','o','n','t','e','n','t','-','D','i','s','p','o','s','i','t','i','o','n',0 };
90 static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
91 static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
92 static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
93 static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
94 static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
95 static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
96 static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
97 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 };
98 static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
99 static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
100 static const WCHAR szDate[] = { 'D','a','t','e',0 };
101 static const WCHAR szFrom[] = { 'F','r','o','m',0 };
102 static const WCHAR szETag[] = { 'E','T','a','g',0 };
103 static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
104 static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
105 static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
106 static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
107 static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
108 static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
109 static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
110 static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
111 static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
112 static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
113 static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
114 static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
115 static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
116 static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
117 static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
118 static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
119 static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
120 static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
121 static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
122 static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
123 static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
124 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 };
125 static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
126 static const WCHAR szURI[] = { 'U','R','I',0 };
127 static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
128 static const WCHAR szVary[] = { 'V','a','r','y',0 };
129 static const WCHAR szVia[] = { 'V','i','a',0 };
130 static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
131 static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
132 
133 static const WCHAR emptyW[] = {0};
134 
135 #define HTTP_REFERER szReferer
136 #define HTTP_ACCEPT szAccept
137 #define HTTP_USERAGENT szUser_Agent
138 
139 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
140 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
141 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
142 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
143 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
144 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
145 
146 #define COLLECT_TIME 60000
147 
149 {
156  void *auth_data;
157  unsigned int auth_data_len;
158  BOOL finished; /* finished authenticating */
159 };
160 
161 
163 {
164  struct list entry;
165 
171 
172 typedef struct _authorizationData
173 {
174  struct list entry;
175 
185 
188 
191 {
192  0, 0, &authcache_cs,
194  0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
195 };
196 static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
197 
204 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
206 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
208 
211 {
212  0, 0, &connection_pool_cs,
214  0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
215 };
216 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
217 
220 
222 {
224 }
225 
227 {
228  if(InterlockedDecrement(&server->ref))
229  return;
230 
231 #ifdef __REACTOS__
233 #endif
234  list_remove(&server->entry);
235 #ifdef __REACTOS__
237 #endif
238 
239  if(server->cert_chain)
240  CertFreeCertificateChain(server->cert_chain);
241  heap_free(server->name);
242  heap_free(server->scheme_host_port);
243  heap_free(server);
244 }
245 
247 {
248  BOOL default_port;
249  size_t name_len;
250  WCHAR *buf;
251 
252  static const WCHAR httpW[] = {'h','t','t','p',0};
253  static const WCHAR httpsW[] = {'h','t','t','p','s',0};
254  static const WCHAR formatW[] = {'%','s',':','/','/','%','s',':','%','u',0};
255 
256  name_len = strlenW(server->name);
257  buf = heap_alloc((name_len + 10 /* strlen("://:<port>") */)*sizeof(WCHAR) + sizeof(httpsW));
258  if(!buf)
259  return FALSE;
260 
261  sprintfW(buf, formatW, server->is_https ? httpsW : httpW, server->name, server->port);
262  server->scheme_host_port = buf;
263 
264  server->host_port = server->scheme_host_port + 7 /* strlen("http://") */;
265  if(server->is_https)
266  server->host_port++;
267 
268  default_port = server->port == (server->is_https ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT);
269  server->canon_host_port = default_port ? server->name : server->host_port;
270  return TRUE;
271 }
272 
274 {
275  server_t *iter, *server = NULL;
276 
278 
280  if(iter->port == port && name.len == strlenW(iter->name) && !strncmpiW(iter->name, name.str, name.len)
281  && iter->is_https == is_https) {
282  server = iter;
284  break;
285  }
286  }
287 
288  if(!server && do_create) {
289  server = heap_alloc_zero(sizeof(*server));
290  if(server) {
291  server->ref = 2; /* list reference and return */
292  server->port = port;
293  server->is_https = is_https;
294  list_init(&server->conn_pool);
295  server->name = heap_strndupW(name.str, name.len);
296  if(server->name && process_host_port(server)) {
298  }else {
299  heap_free(server);
300  server = NULL;
301  }
302  }
303  }
304 
306 
307  return server;
308 }
309 
311 {
312  netconn_t *netconn, *netconn_safe;
313  server_t *server, *server_safe;
314  BOOL remaining = FALSE;
315  DWORD64 now;
316 
317 #ifdef __REACTOS__
318  now = GetTickCount();
319 #else
320  now = GetTickCount64();
321 #endif
322 
324  LIST_FOR_EACH_ENTRY_SAFE(netconn, netconn_safe, &server->conn_pool, netconn_t, pool_entry) {
325  if(collect_type > COLLECT_TIMEOUT || netconn->keep_until < now) {
326  TRACE("freeing %p\n", netconn);
327  list_remove(&netconn->pool_entry);
329  }else {
330  remaining = TRUE;
331  }
332  }
333 
334  if(collect_type == COLLECT_CLEANUP) {
335  list_remove(&server->entry);
336  list_init(&server->entry);
338  }
339  }
340 
341  return remaining;
342 }
343 
345 {
346  BOOL remaining_conns;
347 
348  do {
349  /* FIXME: Use more sophisticated method */
350  Sleep(5000);
351 
353 
354  remaining_conns = collect_connections(COLLECT_TIMEOUT);
355  if(!remaining_conns)
357 
359  }while(remaining_conns);
360 
362 }
363 
364 /***********************************************************************
365  * HTTP_GetHeader (internal)
366  *
367  * Headers section must be held
368  */
370 {
371  int HeaderIndex = 0;
372  HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
373  if (HeaderIndex == -1)
374  return NULL;
375  else
376  return &req->custHeaders[HeaderIndex];
377 }
378 
380 {
382  WCHAR *ret = NULL;
383 
385  if ((header = HTTP_GetHeader( req, hostW ))) ret = heap_strdupW( header->lpszValue );
386  else ret = heap_strdupW( req->server->canon_host_port );
388  return ret;
389 }
390 
396 };
397 
398 typedef struct {
400 
405 
406  enum {
413  CHUNKED_STREAM_STATE_ERROR
414  } state;
416 
418 {
419  stream->vtbl->destroy(stream);
420 }
421 
423 {
426  req->read_pos = req->read_size = req->netconn_stream.content_read = 0;
427  req->read_gzip = FALSE;
428 }
429 
430 static void remove_header( http_request_t *request, const WCHAR *str, BOOL from_request )
431 {
432  int index;
433  EnterCriticalSection( &request->headers_section );
434  index = HTTP_GetCustomHeaderIndex( request, str, 0, from_request );
436  LeaveCriticalSection( &request->headers_section );
437 }
438 
439 #ifdef HAVE_ZLIB
440 
441 typedef struct {
443  data_stream_t *parent_stream;
444  z_stream zstream;
446  DWORD buf_size;
447  DWORD buf_pos;
448  BOOL end_of_data;
449 } gzip_stream_t;
450 
451 static BOOL gzip_end_of_data(data_stream_t *stream, http_request_t *req)
452 {
453  gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
454  return gzip_stream->end_of_data
455  || (!gzip_stream->buf_size && gzip_stream->parent_stream->vtbl->end_of_data(gzip_stream->parent_stream, req));
456 }
457 
458 static DWORD gzip_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
459  DWORD *read, BOOL allow_blocking)
460 {
461  gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
462  z_stream *zstream = &gzip_stream->zstream;
463  DWORD current_read, ret_read = 0;
464  int zres;
466 
467  TRACE("(%d %x)\n", size, allow_blocking);
468 
469  while(size && !gzip_stream->end_of_data) {
470  if(!gzip_stream->buf_size) {
471  if(gzip_stream->buf_pos) {
472  if(gzip_stream->buf_size)
473  memmove(gzip_stream->buf, gzip_stream->buf+gzip_stream->buf_pos, gzip_stream->buf_size);
474  gzip_stream->buf_pos = 0;
475  }
476  res = gzip_stream->parent_stream->vtbl->read(gzip_stream->parent_stream, req, gzip_stream->buf+gzip_stream->buf_size,
477  sizeof(gzip_stream->buf)-gzip_stream->buf_size, &current_read, allow_blocking);
478  if(res != ERROR_SUCCESS)
479  break;
480 
481  gzip_stream->buf_size += current_read;
482  if(!current_read) {
483  WARN("unexpected end of data\n");
484  gzip_stream->end_of_data = TRUE;
485  break;
486  }
487  }
488 
489  zstream->next_in = gzip_stream->buf+gzip_stream->buf_pos;
490  zstream->avail_in = gzip_stream->buf_size;
491  zstream->next_out = buf+ret_read;
492  zstream->avail_out = size;
493  zres = inflate(&gzip_stream->zstream, 0);
494  current_read = size - zstream->avail_out;
495  size -= current_read;
496  ret_read += current_read;
497  gzip_stream->buf_size -= zstream->next_in - (gzip_stream->buf+gzip_stream->buf_pos);
498  gzip_stream->buf_pos = zstream->next_in-gzip_stream->buf;
499  if(zres == Z_STREAM_END) {
500  TRACE("end of data\n");
501  gzip_stream->end_of_data = TRUE;
502  inflateEnd(zstream);
503  }else if(zres != Z_OK) {
504  WARN("inflate failed %d: %s\n", zres, debugstr_a(zstream->msg));
505  if(!ret_read)
507  break;
508  }
509 
510  if(ret_read)
511  allow_blocking = FALSE;
512  }
513 
514  TRACE("read %u bytes\n", ret_read);
515  if(ret_read)
516  res = ERROR_SUCCESS;
517  *read = ret_read;
518  return res;
519 }
520 
521 static DWORD gzip_drain_content(data_stream_t *stream, http_request_t *req, BOOL allow_blocking)
522 {
523  gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
524  return gzip_stream->parent_stream->vtbl->drain_content(gzip_stream->parent_stream, req, allow_blocking);
525 }
526 
527 static void gzip_destroy(data_stream_t *stream)
528 {
529  gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
530 
531  destroy_data_stream(gzip_stream->parent_stream);
532 
533  if(!gzip_stream->end_of_data)
534  inflateEnd(&gzip_stream->zstream);
535  heap_free(gzip_stream);
536 }
537 
538 static const data_stream_vtbl_t gzip_stream_vtbl = {
539  gzip_end_of_data,
540  gzip_read,
541  gzip_drain_content,
542  gzip_destroy
543 };
544 
545 static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
546 {
547  return heap_alloc(items*size);
548 }
549 
550 static void wininet_zfree(voidpf opaque, voidpf address)
551 {
553 }
554 
555 static DWORD init_gzip_stream(http_request_t *req, BOOL is_gzip)
556 {
557  gzip_stream_t *gzip_stream;
558  int zres;
559 
560  gzip_stream = heap_alloc_zero(sizeof(gzip_stream_t));
561  if(!gzip_stream)
562  return ERROR_OUTOFMEMORY;
563 
564  gzip_stream->stream.vtbl = &gzip_stream_vtbl;
565  gzip_stream->zstream.zalloc = wininet_zalloc;
566  gzip_stream->zstream.zfree = wininet_zfree;
567 
568  zres = inflateInit2(&gzip_stream->zstream, is_gzip ? 0x1f : -15);
569  if(zres != Z_OK) {
570  ERR("inflateInit failed: %d\n", zres);
571  heap_free(gzip_stream);
572  return ERROR_OUTOFMEMORY;
573  }
574 
576 
577  if(req->read_size) {
578  memcpy(gzip_stream->buf, req->read_buf+req->read_pos, req->read_size);
579  gzip_stream->buf_size = req->read_size;
580  req->read_pos = req->read_size = 0;
581  }
582 
583  req->read_gzip = TRUE;
584  gzip_stream->parent_stream = req->data_stream;
585  req->data_stream = &gzip_stream->stream;
586  return ERROR_SUCCESS;
587 }
588 
589 #else
590 
592 {
593  ERR("gzip stream not supported, missing zlib.\n");
594  return ERROR_SUCCESS;
595 }
596 
597 #endif
598 
599 /***********************************************************************
600  * HTTP_FreeTokens (internal)
601  *
602  * Frees table of pointers.
603  */
604 static void HTTP_FreeTokens(LPWSTR * token_array)
605 {
606  int i;
607  for (i = 0; token_array[i]; i++) heap_free(token_array[i]);
608  heap_free(token_array);
609 }
610 
612 {
613  static const WCHAR szSlash[] = { '/',0 };
614  static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
615 
616  /* If we don't have a path we set it to root */
617  if (NULL == request->path)
618  request->path = heap_strdupW(szSlash);
619  else /* remove \r and \n*/
620  {
621  int nLen = strlenW(request->path);
622  while ((nLen >0 ) && ((request->path[nLen-1] == '\r')||(request->path[nLen-1] == '\n')))
623  {
624  nLen--;
625  request->path[nLen]='\0';
626  }
627  /* Replace '\' with '/' */
628  while (nLen>0) {
629  nLen--;
630  if (request->path[nLen] == '\\') request->path[nLen]='/';
631  }
632  }
633 
635  request->path, strlenW(request->path), szHttp, strlenW(szHttp) )
636  && request->path[0] != '/') /* not an absolute path ?? --> fix it !! */
637  {
638  WCHAR *fixurl = heap_alloc((strlenW(request->path) + 2)*sizeof(WCHAR));
639  *fixurl = '/';
640  strcpyW(fixurl + 1, request->path);
641  heap_free( request->path );
642  request->path = fixurl;
643  }
644 }
645 
647  const WCHAR *path, const WCHAR *version, BOOL use_cr)
648 {
649  static const WCHAR szSpace[] = {' ',0};
650  static const WCHAR szColon[] = {':',' ',0};
651  static const WCHAR szCr[] = {'\r',0};
652  static const WCHAR szLf[] = {'\n',0};
653  LPWSTR requestString;
654  DWORD len, n;
655  LPCWSTR *req;
656  UINT i;
657 
658  EnterCriticalSection( &request->headers_section );
659 
660  /* allocate space for an array of all the string pointers to be added */
661  len = request->nCustHeaders * 5 + 10;
662  if (!(req = heap_alloc( len * sizeof(const WCHAR *) )))
663  {
664  LeaveCriticalSection( &request->headers_section );
665  return NULL;
666  }
667 
668  /* add the verb, path and HTTP version string */
669  n = 0;
670  req[n++] = verb;
671  req[n++] = szSpace;
672  req[n++] = path;
673  req[n++] = szSpace;
674  req[n++] = version;
675  if (use_cr)
676  req[n++] = szCr;
677  req[n++] = szLf;
678 
679  /* Append custom request headers */
680  for (i = 0; i < request->nCustHeaders; i++)
681  {
682  if (request->custHeaders[i].wFlags & HDR_ISREQUEST)
683  {
684  req[n++] = request->custHeaders[i].lpszField;
685  req[n++] = szColon;
686  req[n++] = request->custHeaders[i].lpszValue;
687  if (use_cr)
688  req[n++] = szCr;
689  req[n++] = szLf;
690 
691  TRACE("Adding custom header %s (%s)\n",
692  debugstr_w(request->custHeaders[i].lpszField),
693  debugstr_w(request->custHeaders[i].lpszValue));
694  }
695  }
696  if (use_cr)
697  req[n++] = szCr;
698  req[n++] = szLf;
699  req[n] = NULL;
700 
701  requestString = HTTP_build_req( req, 4 );
702  heap_free( req );
703  LeaveCriticalSection( &request->headers_section );
704  return requestString;
705 }
706 
708 {
709  static const WCHAR colonW[] = { ':',' ',0 };
710  static const WCHAR crW[] = { '\r',0 };
711  static const WCHAR lfW[] = { '\n',0 };
712  static const WCHAR status_fmt[] = { ' ','%','u',' ',0 };
713  const WCHAR **req;
714  WCHAR *ret, buf[14];
715  DWORD i, n = 0;
716 
717  EnterCriticalSection( &request->headers_section );
718 
719  if (!(req = heap_alloc( (request->nCustHeaders * 5 + 8) * sizeof(WCHAR *) )))
720  {
721  LeaveCriticalSection( &request->headers_section );
722  return NULL;
723  }
724 
725  if (request->status_code)
726  {
727  req[n++] = request->version;
728  sprintfW(buf, status_fmt, request->status_code);
729  req[n++] = buf;
730  req[n++] = request->statusText;
731  if (use_cr)
732  req[n++] = crW;
733  req[n++] = lfW;
734  }
735 
736  for(i = 0; i < request->nCustHeaders; i++)
737  {
738  if(!(request->custHeaders[i].wFlags & HDR_ISREQUEST)
739  && strcmpW(request->custHeaders[i].lpszField, szStatus))
740  {
741  req[n++] = request->custHeaders[i].lpszField;
742  req[n++] = colonW;
743  req[n++] = request->custHeaders[i].lpszValue;
744  if(use_cr)
745  req[n++] = crW;
746  req[n++] = lfW;
747 
748  TRACE("Adding custom header %s (%s)\n",
749  debugstr_w(request->custHeaders[i].lpszField),
750  debugstr_w(request->custHeaders[i].lpszValue));
751  }
752  }
753  if(use_cr)
754  req[n++] = crW;
755  req[n++] = lfW;
756  req[n] = NULL;
757 
758  ret = HTTP_build_req(req, 0);
759  heap_free(req);
760  LeaveCriticalSection( &request->headers_section );
761  return ret;
762 }
763 
765 {
766  int HeaderIndex;
767  int numCookies = 0;
768  LPHTTPHEADERW setCookieHeader;
769  WCHAR *path, *tmp;
770 
771  if(request->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES)
772  return;
773 
775  if (!path)
776  return;
777 
778  tmp = strrchrW(path, '/');
779  if (tmp && tmp[1]) tmp[1] = 0;
780 
781  EnterCriticalSection( &request->headers_section );
782 
783  while((HeaderIndex = HTTP_GetCustomHeaderIndex(request, szSet_Cookie, numCookies++, FALSE)) != -1)
784  {
785  const WCHAR *data;
786  substr_t name;
787 
788  setCookieHeader = &request->custHeaders[HeaderIndex];
789 
790  if (!setCookieHeader->lpszValue)
791  continue;
792 
793  data = strchrW(setCookieHeader->lpszValue, '=');
794  if(!data)
795  continue;
796 
797  name = substr(setCookieHeader->lpszValue, data - setCookieHeader->lpszValue);
798  data++;
800  }
801 
802  LeaveCriticalSection( &request->headers_section );
803  heap_free(path);
804 }
805 
807 {
808  LPWSTR str = start;
809  LPWSTR end;
810 
811  while (*str == ' ')
812  str++;
813 
814  if (str != start)
815  memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
816 
817  end = start + strlenW(start) - 1;
818  while (end >= start && *end == ' ')
819  {
820  *end = '\0';
821  end--;
822  }
823 }
824 
825 static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
826 {
827  static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
828  static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
829  BOOL is_basic;
830  is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAY_SIZE(szBasic)) &&
831  ((pszAuthValue[ARRAY_SIZE(szBasic)] == ' ') || !pszAuthValue[ARRAY_SIZE(szBasic)]);
832  if (is_basic && pszRealm)
833  {
834  LPCWSTR token;
835  LPCWSTR ptr = &pszAuthValue[ARRAY_SIZE(szBasic)];
836  LPCWSTR realm;
837  ptr++;
838  *pszRealm=NULL;
839  token = strchrW(ptr,'=');
840  if (!token)
841  return TRUE;
842  realm = ptr;
843  while (*realm == ' ')
844  realm++;
845  if(!strncmpiW(realm, szRealm, ARRAY_SIZE(szRealm)) &&
846  (realm[ARRAY_SIZE(szRealm)] == ' ' || realm[ARRAY_SIZE(szRealm)] == '='))
847  {
848  token++;
849  while (*token == ' ')
850  token++;
851  if (*token == '\0')
852  return TRUE;
853  *pszRealm = heap_strdupW(token);
854  strip_spaces(*pszRealm);
855  }
856  }
857 
858  return is_basic;
859 }
860 
861 static void destroy_authinfo( struct HttpAuthInfo *authinfo )
862 {
863  if (!authinfo) return;
864 
869 
870  heap_free(authinfo->auth_data);
873 }
874 
875 static UINT retrieve_cached_basic_authorization(http_request_t *req, const WCHAR *host, const WCHAR *realm, char **auth_data)
876 {
878  UINT rc = 0;
879 
880  TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
881 
884  {
885  if (!strcmpiW(host, ad->host) && (!realm || !strcmpW(realm, ad->realm)))
886  {
887  char *colon;
888  DWORD length;
889 
890  TRACE("Authorization found in cache\n");
891  *auth_data = heap_alloc(ad->authorizationLen);
892  memcpy(*auth_data,ad->authorization,ad->authorizationLen);
893  rc = ad->authorizationLen;
894 
895  /* update session username and password to reflect current credentials */
896  colon = strchr(ad->authorization, ':');
897  length = colon - ad->authorization;
898 
899  heap_free(req->session->userName);
900  heap_free(req->session->password);
901 
903  length++;
905  break;
906  }
907  }
909  return rc;
910 }
911 
912 static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
913 {
914  struct list *cursor;
916 
917  TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
918 
921  {
923  if (!strcmpiW(host,check->host) && !strcmpW(realm,check->realm))
924  {
925  ad = check;
926  break;
927  }
928  }
929 
930  if (ad)
931  {
932  TRACE("Found match in cache, replacing\n");
934  ad->authorization = heap_alloc(auth_data_len);
935  memcpy(ad->authorization, auth_data, auth_data_len);
936  ad->authorizationLen = auth_data_len;
937  }
938  else
939  {
940  ad = heap_alloc(sizeof(basicAuthorizationData));
941  ad->host = heap_strdupW(host);
942  ad->realm = heap_strdupW(realm);
943  ad->authorization = heap_alloc(auth_data_len);
944  memcpy(ad->authorization, auth_data, auth_data_len);
945  ad->authorizationLen = auth_data_len;
947  TRACE("authorization cached\n");
948  }
950 }
951 
953  SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
954 {
955  authorizationData *ad;
956 
957  TRACE("Looking for authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
958 
961  if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
962  TRACE("Authorization found in cache\n");
963 
964  nt_auth_identity->User = heap_strdupW(ad->user);
965  nt_auth_identity->Password = heap_strdupW(ad->password);
966  nt_auth_identity->Domain = heap_alloc(sizeof(WCHAR)*ad->domain_len);
967  if(!nt_auth_identity->User || !nt_auth_identity->Password ||
968  (!nt_auth_identity->Domain && ad->domain_len)) {
969  heap_free(nt_auth_identity->User);
970  heap_free(nt_auth_identity->Password);
971  heap_free(nt_auth_identity->Domain);
972  break;
973  }
974 
975  nt_auth_identity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
976  nt_auth_identity->UserLength = ad->user_len;
977  nt_auth_identity->PasswordLength = ad->password_len;
978  memcpy(nt_auth_identity->Domain, ad->domain, sizeof(WCHAR)*ad->domain_len);
979  nt_auth_identity->DomainLength = ad->domain_len;
981  return TRUE;
982  }
983  }
985 
986  return FALSE;
987 }
988 
990  SEC_WINNT_AUTH_IDENTITY_W *nt_auth_identity)
991 {
992  authorizationData *ad;
993  BOOL found = FALSE;
994 
995  TRACE("Caching authorization for %s:%s\n", debugstr_w(host), debugstr_w(scheme));
996 
999  if(!strcmpiW(host, ad->host) && !strcmpiW(scheme, ad->scheme)) {
1000  found = TRUE;
1001  break;
1002  }
1003 
1004  if(found) {
1005  heap_free(ad->user);
1006  heap_free(ad->password);
1007  heap_free(ad->domain);
1008  } else {
1009  ad = heap_alloc(sizeof(authorizationData));
1010  if(!ad) {
1012  return;
1013  }
1014 
1015  ad->host = heap_strdupW(host);
1016  ad->scheme = heap_strdupW(scheme);
1018  }
1019 
1020  ad->user = heap_strndupW(nt_auth_identity->User, nt_auth_identity->UserLength);
1021  ad->password = heap_strndupW(nt_auth_identity->Password, nt_auth_identity->PasswordLength);
1022  ad->domain = heap_strndupW(nt_auth_identity->Domain, nt_auth_identity->DomainLength);
1023  ad->user_len = nt_auth_identity->UserLength;
1024  ad->password_len = nt_auth_identity->PasswordLength;
1025  ad->domain_len = nt_auth_identity->DomainLength;
1026 
1027  if(!ad->host || !ad->scheme || !ad->user || !ad->password
1028  || (nt_auth_identity->Domain && !ad->domain)) {
1029  heap_free(ad->host);
1030  heap_free(ad->scheme);
1031  heap_free(ad->user);
1032  heap_free(ad->password);
1033  heap_free(ad->domain);
1034  list_remove(&ad->entry);
1035  heap_free(ad);
1036  }
1037 
1039 }
1040 
1042 {
1043  authorizationData *ad, *sa_safe;
1044  basicAuthorizationData *basic, *basic_safe;
1045 
1047 
1049  {
1050  heap_free(basic->host);
1051  heap_free(basic->realm);
1052  heap_free(basic->authorization);
1053 
1054  list_remove(&basic->entry);
1055  heap_free(basic);
1056  }
1057 
1059  {
1060  heap_free(ad->host);
1061  heap_free(ad->scheme);
1062  heap_free(ad->user);
1063  heap_free(ad->password);
1064  heap_free(ad->domain);
1065  list_remove(&ad->entry);
1066  heap_free(ad);
1067  }
1068 
1070 }
1071 
1073  struct HttpAuthInfo **ppAuthInfo,
1074  LPWSTR domain_and_username, LPWSTR password,
1075  LPWSTR host )
1076 {
1077  SECURITY_STATUS sec_status;
1078  struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
1079  BOOL first = FALSE;
1080  LPWSTR szRealm = NULL;
1081 
1082  TRACE("%s\n", debugstr_w(pszAuthValue));
1083 
1084  if (!pAuthInfo)
1085  {
1086  TimeStamp exp;
1087 
1088  first = TRUE;
1089  pAuthInfo = heap_alloc(sizeof(*pAuthInfo));
1090  if (!pAuthInfo)
1091  return FALSE;
1092 
1093  SecInvalidateHandle(&pAuthInfo->cred);
1094  SecInvalidateHandle(&pAuthInfo->ctx);
1095  memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
1096  pAuthInfo->attr = 0;
1097  pAuthInfo->auth_data = NULL;
1098  pAuthInfo->auth_data_len = 0;
1099  pAuthInfo->finished = FALSE;
1100 
1101  if (is_basic_auth_value(pszAuthValue,NULL))
1102  {
1103  static const WCHAR szBasic[] = {'B','a','s','i','c',0};
1104  pAuthInfo->scheme = heap_strdupW(szBasic);
1105  if (!pAuthInfo->scheme)
1106  {
1107  heap_free(pAuthInfo);
1108  return FALSE;
1109  }
1110  }
1111  else
1112  {
1113  PVOID pAuthData;
1114  SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
1115 
1116  pAuthInfo->scheme = heap_strdupW(pszAuthValue);
1117  if (!pAuthInfo->scheme)
1118  {
1119  heap_free(pAuthInfo);
1120  return FALSE;
1121  }
1122 
1123  if (domain_and_username)
1124  {
1125  WCHAR *user = strchrW(domain_and_username, '\\');
1126  WCHAR *domain = domain_and_username;
1127 
1128  /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1129 
1130  pAuthData = &nt_auth_identity;
1131 
1132  if (user) user++;
1133  else
1134  {
1135  user = domain_and_username;
1136  domain = NULL;
1137  }
1138 
1139  nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1140  nt_auth_identity.User = user;
1141  nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
1142  nt_auth_identity.Domain = domain;
1143  nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
1144  nt_auth_identity.Password = password;
1145  nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
1146 
1147  cache_authorization(host, pAuthInfo->scheme, &nt_auth_identity);
1148  }
1149  else if(retrieve_cached_authorization(host, pAuthInfo->scheme, &nt_auth_identity))
1150  pAuthData = &nt_auth_identity;
1151  else
1152  /* use default credentials */
1153  pAuthData = NULL;
1154 
1155  sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
1157  pAuthData, NULL,
1158  NULL, &pAuthInfo->cred,
1159  &exp);
1160 
1161  if(pAuthData && !domain_and_username) {
1162  heap_free(nt_auth_identity.User);
1163  heap_free(nt_auth_identity.Domain);
1164  heap_free(nt_auth_identity.Password);
1165  }
1166 
1167  if (sec_status == SEC_E_OK)
1168  {
1169  PSecPkgInfoW sec_pkg_info;
1170  sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
1171  if (sec_status == SEC_E_OK)
1172  {
1173  pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
1174  FreeContextBuffer(sec_pkg_info);
1175  }
1176  }
1177  if (sec_status != SEC_E_OK)
1178  {
1179  WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1180  debugstr_w(pAuthInfo->scheme), sec_status);
1181  heap_free(pAuthInfo->scheme);
1182  heap_free(pAuthInfo);
1183  return FALSE;
1184  }
1185  }
1186  *ppAuthInfo = pAuthInfo;
1187  }
1188  else if (pAuthInfo->finished)
1189  return FALSE;
1190 
1191  if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
1192  strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
1193  {
1194  ERR("authentication scheme changed from %s to %s\n",
1195  debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
1196  return FALSE;
1197  }
1198 
1199  if (is_basic_auth_value(pszAuthValue,&szRealm))
1200  {
1201  int userlen;
1202  int passlen;
1203  char *auth_data = NULL;
1204  UINT auth_data_len = 0;
1205 
1206  TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
1207 
1208  if (!domain_and_username)
1209  {
1210  if (host && szRealm)
1212  if (auth_data_len == 0)
1213  {
1214  heap_free(szRealm);
1215  return FALSE;
1216  }
1217  }
1218  else
1219  {
1220  userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
1222 
1223  /* length includes a nul terminator, which will be re-used for the ':' */
1224  auth_data = heap_alloc(userlen + 1 + passlen);
1225  if (!auth_data)
1226  {
1227  heap_free(szRealm);
1228  return FALSE;
1229  }
1230 
1231  WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
1232  auth_data[userlen] = ':';
1233  WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
1234  auth_data_len = userlen + 1 + passlen;
1235  if (host && szRealm)
1237  }
1238 
1239  pAuthInfo->auth_data = auth_data;
1240  pAuthInfo->auth_data_len = auth_data_len;
1241  pAuthInfo->finished = TRUE;
1242  heap_free(szRealm);
1243  return TRUE;
1244  }
1245  else
1246  {
1247  LPCWSTR pszAuthData;
1248  SecBufferDesc out_desc, in_desc;
1249  SecBuffer out, in;
1250  unsigned char *buffer;
1253 
1254  in.BufferType = SECBUFFER_TOKEN;
1255  in.cbBuffer = 0;
1256  in.pvBuffer = NULL;
1257 
1258  in_desc.ulVersion = 0;
1259  in_desc.cBuffers = 1;
1260  in_desc.pBuffers = &in;
1261 
1262  pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
1263  if (*pszAuthData == ' ')
1264  {
1265  pszAuthData++;
1266  in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
1267  in.pvBuffer = heap_alloc(in.cbBuffer);
1268  HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
1269  }
1270 
1271  buffer = heap_alloc(pAuthInfo->max_token);
1272 
1273  out.BufferType = SECBUFFER_TOKEN;
1274  out.cbBuffer = pAuthInfo->max_token;
1275  out.pvBuffer = buffer;
1276 
1277  out_desc.ulVersion = 0;
1278  out_desc.cBuffers = 1;
1279  out_desc.pBuffers = &out;
1280 
1281  sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
1282  first ? NULL : &pAuthInfo->ctx,
1283  first ? request->server->name : NULL,
1284  context_req, 0, SECURITY_NETWORK_DREP,
1285  in.pvBuffer ? &in_desc : NULL,
1286  0, &pAuthInfo->ctx, &out_desc,
1287  &pAuthInfo->attr, &pAuthInfo->exp);
1288  if (sec_status == SEC_E_OK)
1289  {
1290  pAuthInfo->finished = TRUE;
1291  pAuthInfo->auth_data = out.pvBuffer;
1292  pAuthInfo->auth_data_len = out.cbBuffer;
1293  TRACE("sending last auth packet\n");
1294  }
1295  else if (sec_status == SEC_I_CONTINUE_NEEDED)
1296  {
1297  pAuthInfo->auth_data = out.pvBuffer;
1298  pAuthInfo->auth_data_len = out.cbBuffer;
1299  TRACE("sending next auth packet\n");
1300  }
1301  else
1302  {
1303  ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
1304  heap_free(out.pvBuffer);
1305  destroy_authinfo(pAuthInfo);
1306  *ppAuthInfo = NULL;
1307  return FALSE;
1308  }
1309  }
1310 
1311  return TRUE;
1312 }
1313 
1314 /***********************************************************************
1315  * HTTP_HttpAddRequestHeadersW (internal)
1316  */
1318  LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1319 {
1320  LPWSTR lpszStart;
1321  LPWSTR lpszEnd;
1322  LPWSTR buffer;
1324 
1325  TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
1326 
1327  if( dwHeaderLength == ~0U )
1328  len = strlenW(lpszHeader);
1329  else
1330  len = dwHeaderLength;
1331  buffer = heap_alloc(sizeof(WCHAR)*(len+1));
1332  lstrcpynW( buffer, lpszHeader, len + 1);
1333 
1334  lpszStart = buffer;
1335 
1336  do
1337  {
1338  LPWSTR * pFieldAndValue;
1339 
1340  lpszEnd = lpszStart;
1341 
1342  while (*lpszEnd != '\0')
1343  {
1344  if (*lpszEnd == '\r' || *lpszEnd == '\n')
1345  break;
1346  lpszEnd++;
1347  }
1348 
1349  if (*lpszStart == '\0')
1350  break;
1351 
1352  if (*lpszEnd == '\r' || *lpszEnd == '\n')
1353  {
1354  *lpszEnd = '\0';
1355  lpszEnd++; /* Jump over newline */
1356  }
1357  TRACE("interpreting header %s\n", debugstr_w(lpszStart));
1358  if (*lpszStart == '\0')
1359  {
1360  /* Skip 0-length headers */
1361  lpszStart = lpszEnd;
1362  res = ERROR_SUCCESS;
1363  continue;
1364  }
1365  pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
1366  if (pFieldAndValue)
1367  {
1368  res = HTTP_ProcessHeader(request, pFieldAndValue[0],
1369  pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
1370  HTTP_FreeTokens(pFieldAndValue);
1371  }
1372 
1373  lpszStart = lpszEnd;
1374  } while (res == ERROR_SUCCESS);
1375 
1376  heap_free(buffer);
1377  return res;
1378 }
1379 
1380 /***********************************************************************
1381  * HttpAddRequestHeadersW (WININET.@)
1382  *
1383  * Adds one or more HTTP header to the request handler
1384  *
1385  * NOTE
1386  * On Windows if dwHeaderLength includes the trailing '\0', then
1387  * HttpAddRequestHeadersW() adds it too. However this results in an
1388  * invalid HTTP header which is rejected by some servers so we probably
1389  * don't need to match Windows on that point.
1390  *
1391  * RETURNS
1392  * TRUE on success
1393  * FALSE on failure
1394  *
1395  */
1397  LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1398 {
1401 
1402  TRACE("%p, %s, %u, %08x\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1403 
1404  if (!lpszHeader)
1405  return TRUE;
1406 
1407  request = (http_request_t*) get_handle_object( hHttpRequest );
1408  if (request && request->hdr.htype == WH_HHTTPREQ)
1409  res = HTTP_HttpAddRequestHeadersW( request, lpszHeader, dwHeaderLength, dwModifier );
1410  if( request )
1412 
1413  if(res != ERROR_SUCCESS)
1414  SetLastError(res);
1415  return res == ERROR_SUCCESS;
1416 }
1417 
1418 /***********************************************************************
1419  * HttpAddRequestHeadersA (WININET.@)
1420  *
1421  * Adds one or more HTTP header to the request handler
1422  *
1423  * RETURNS
1424  * TRUE on success
1425  * FALSE on failure
1426  *
1427  */
1429  LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
1430 {
1431  WCHAR *headers = NULL;
1432  BOOL r;
1433 
1434  TRACE("%p, %s, %u, %08x\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
1435 
1436  if(lpszHeader)
1437  headers = heap_strndupAtoW(lpszHeader, dwHeaderLength, &dwHeaderLength);
1438 
1439  r = HttpAddRequestHeadersW(hHttpRequest, headers, dwHeaderLength, dwModifier);
1440 
1441  heap_free(headers);
1442  return r;
1443 }
1444 
1445 static void free_accept_types( WCHAR **accept_types )
1446 {
1447  WCHAR *ptr, **types = accept_types;
1448 
1449  if (!types) return;
1450  while ((ptr = *types))
1451  {
1452  heap_free( ptr );
1453  types++;
1454  }
1455  heap_free( accept_types );
1456 }
1457 
1458 static WCHAR **convert_accept_types( const char **accept_types )
1459 {
1460  unsigned int count;
1461  const char **types = accept_types;
1462  WCHAR **typesW;
1463  BOOL invalid_pointer = FALSE;
1464 
1465  if (!types) return NULL;
1466  count = 0;
1467  while (*types)
1468  {
1469  __TRY
1470  {
1471  /* find out how many there are */
1472  if (*types && **types)
1473  {
1474  TRACE("accept type: %s\n", debugstr_a(*types));
1475  count++;
1476  }
1477  }
1479  {
1480  WARN("invalid accept type pointer\n");
1481  invalid_pointer = TRUE;
1482  }
1483  __ENDTRY;
1484  types++;
1485  }
1486  if (invalid_pointer) return NULL;
1487  if (!(typesW = heap_alloc( sizeof(WCHAR *) * (count + 1) ))) return NULL;
1488  count = 0;
1489  types = accept_types;
1490  while (*types)
1491  {
1492  if (*types && **types) typesW[count++] = heap_strdupAtoW( *types );
1493  types++;
1494  }
1495  typesW[count] = NULL;
1496  return typesW;
1497 }
1498 
1499 /***********************************************************************
1500  * HttpOpenRequestA (WININET.@)
1501  *
1502  * Open a HTTP request handle
1503  *
1504  * RETURNS
1505  * HINTERNET a HTTP request handle on success
1506  * NULL on failure
1507  *
1508  */
1510  LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
1511  LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
1512  DWORD dwFlags, DWORD_PTR dwContext)
1513 {
1514  LPWSTR szVerb = NULL, szObjectName = NULL;
1515  LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
1516  HINTERNET rc = NULL;
1517 
1518  TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
1519  debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
1520  debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
1521  dwFlags, dwContext);
1522 
1523  if (lpszVerb)
1524  {
1525  szVerb = heap_strdupAtoW(lpszVerb);
1526  if ( !szVerb )
1527  goto end;
1528  }
1529 
1530  if (lpszObjectName)
1531  {
1532  szObjectName = heap_strdupAtoW(lpszObjectName);
1533  if ( !szObjectName )
1534  goto end;
1535  }
1536 
1537  if (lpszVersion)
1538  {
1539  szVersion = heap_strdupAtoW(lpszVersion);
1540  if ( !szVersion )
1541  goto end;
1542  }
1543 
1544  if (lpszReferrer)
1545  {
1546  szReferrer = heap_strdupAtoW(lpszReferrer);
1547  if ( !szReferrer )
1548  goto end;
1549  }
1550 
1551  szAcceptTypes = convert_accept_types( lpszAcceptTypes );
1552  rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName, szVersion, szReferrer,
1553  (const WCHAR **)szAcceptTypes, dwFlags, dwContext);
1554 
1555 end:
1556  free_accept_types(szAcceptTypes);
1557  heap_free(szReferrer);
1559  heap_free(szObjectName);
1560  heap_free(szVerb);
1561  return rc;
1562 }
1563 
1564 /***********************************************************************
1565  * HTTP_EncodeBase64
1566  */
1567 static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
1568 {
1569  UINT n = 0, x;
1570  static const CHAR HTTP_Base64Enc[] =
1571  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1572 
1573  while( len > 0 )
1574  {
1575  /* first 6 bits, all from bin[0] */
1576  base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
1577  x = (bin[0] & 3) << 4;
1578 
1579  /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1580  if( len == 1 )
1581  {
1582  base64[n++] = HTTP_Base64Enc[x];
1583  base64[n++] = '=';
1584  base64[n++] = '=';
1585  break;
1586  }
1587  base64[n++] = HTTP_Base64Enc[ x | ( (bin[1]&0xf0) >> 4 ) ];
1588  x = ( bin[1] & 0x0f ) << 2;
1589 
1590  /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1591  if( len == 2 )
1592  {
1593  base64[n++] = HTTP_Base64Enc[x];
1594  base64[n++] = '=';
1595  break;
1596  }
1597  base64[n++] = HTTP_Base64Enc[ x | ( (bin[2]&0xc0 ) >> 6 ) ];
1598 
1599  /* last 6 bits, all from bin [2] */
1600  base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
1601  bin += 3;
1602  len -= 3;
1603  }
1604  base64[n] = 0;
1605  return n;
1606 }
1607 
1608 static const signed char HTTP_Base64Dec[] =
1609 {
1610  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00 */
1611  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10 */
1612  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20 */
1613  52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30 */
1614  -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40 */
1615  15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50 */
1616  -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60 */
1617  41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70 */
1618 };
1619 
1620 /***********************************************************************
1621  * HTTP_DecodeBase64
1622  */
1624 {
1625  unsigned int n = 0;
1626 
1627  while(*base64)
1628  {
1629  signed char in[4];
1630 
1631  if (base64[0] >= ARRAY_SIZE(HTTP_Base64Dec) ||
1632  ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
1633  base64[1] >= ARRAY_SIZE(HTTP_Base64Dec) ||
1634  ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
1635  {
1636  WARN("invalid base64: %s\n", debugstr_w(base64));
1637  return 0;
1638  }
1639  if (bin)
1640  bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
1641  n++;
1642 
1643  if ((base64[2] == '=') && (base64[3] == '='))
1644  break;
1645  if (base64[2] > ARRAY_SIZE(HTTP_Base64Dec) ||
1646  ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
1647  {
1648  WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
1649  return 0;
1650  }
1651  if (bin)
1652  bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
1653  n++;
1654 
1655  if (base64[3] == '=')
1656  break;
1657  if (base64[3] > ARRAY_SIZE(HTTP_Base64Dec) ||
1658  ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
1659  {
1660  WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
1661  return 0;
1662  }
1663  if (bin)
1664  bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
1665  n++;
1666 
1667  base64 += 4;
1668  }
1669 
1670  return n;
1671 }
1672 
1673 static WCHAR *encode_auth_data( const WCHAR *scheme, const char *data, UINT data_len )
1674 {
1675  WCHAR *ret;
1676  UINT len, scheme_len = strlenW( scheme );
1677 
1678  /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1679  len = scheme_len + 1 + ((data_len + 2) * 4) / 3;
1680  if (!(ret = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return NULL;
1681  memcpy( ret, scheme, scheme_len * sizeof(WCHAR) );
1682  ret[scheme_len] = ' ';
1683  HTTP_EncodeBase64( data, data_len, ret + scheme_len + 1 );
1684  return ret;
1685 }
1686 
1687 
1688 /***********************************************************************
1689  * HTTP_InsertAuthorization
1690  *
1691  * Insert or delete the authorization field in the request header.
1692  */
1694 {
1695  static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
1696  WCHAR *host, *authorization = NULL;
1697 
1698  if (pAuthInfo)
1699  {
1700  if (pAuthInfo->auth_data_len)
1701  {
1702  if (!(authorization = encode_auth_data(pAuthInfo->scheme, pAuthInfo->auth_data, pAuthInfo->auth_data_len)))
1703  return FALSE;
1704 
1705  /* clear the data as it isn't valid now that it has been sent to the
1706  * server, unless it's Basic authentication which doesn't do
1707  * connection tracking */
1708  if (strcmpiW(pAuthInfo->scheme, wszBasic))
1709  {
1710  heap_free(pAuthInfo->auth_data);
1711  pAuthInfo->auth_data = NULL;
1712  pAuthInfo->auth_data_len = 0;
1713  }
1714  }
1715 
1716  TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1717 
1718  HTTP_ProcessHeader(request, header, authorization,
1720  heap_free(authorization);
1721  }
1722  else
1723  {
1724  UINT data_len;
1725  char *data;
1726 
1727  /* Don't use cached credentials when a username or Authorization was specified */
1728  if ((request->session->userName && request->session->userName[0]) || strcmpW(header, szAuthorization))
1729  return TRUE;
1730 
1731  if (!(host = get_host_header(request)))
1732  return TRUE;
1733 
1735  {
1736  TRACE("Found cached basic authorization for %s\n", debugstr_w(host));
1737 
1738  if (!(authorization = encode_auth_data(wszBasic, data, data_len)))
1739  {
1740  heap_free(data);
1741  heap_free(host);
1742  return FALSE;
1743  }
1744 
1745  TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
1746 
1747  HTTP_ProcessHeader(request, header, authorization,
1749  heap_free(data);
1750  heap_free(authorization);
1751  }
1752  heap_free(host);
1753  }
1754  return TRUE;
1755 }
1756 
1758 {
1759  DWORD size, len;
1760  WCHAR *url;
1761 
1763  size = len + strlenW(req->path) + 1;
1764  if(*req->path != '/')
1765  size++;
1766  url = heap_alloc(size * sizeof(WCHAR));
1767  if(!url)
1768  return NULL;
1769 
1770  memcpy(url, req->server->scheme_host_port, len*sizeof(WCHAR));
1771  if(*req->path != '/')
1772  url[len++] = '/';
1773 
1774  strcpyW(url+len, req->path);
1775 
1776  TRACE("url=%s\n", debugstr_w(url));
1777  return url;
1778 }
1779 
1781 {
1782  static const WCHAR localW[] = { '<','l','o','c','a','l','>',0 };
1783  const WCHAR *dot, *ptr;
1784  int len;
1785 
1786  if(domain.len == ARRAY_SIZE(localW)-1 && !strncmpiW(domain.str, localW, domain.len) && !strchrW(server, '.' ))
1787  return TRUE;
1788 
1789  if(domain.len && *domain.str != '*')
1790  return domain.len == strlenW(server) && !strncmpiW(server, domain.str, domain.len);
1791 
1792  if(domain.len < 2 || domain.str[1] != '.')
1793  return FALSE;
1794 
1795  /* For a hostname to match a wildcard, the last domain must match
1796  * the wildcard exactly. E.g. if the wildcard is *.a.b, and the
1797  * hostname is www.foo.a.b, it matches, but a.b does not.
1798  */
1799  dot = strchrW(server, '.');
1800  if(!dot)
1801  return FALSE;
1802 
1803  len = strlenW(dot + 1);
1804  if(len < domain.len - 2)
1805  return FALSE;
1806 
1807  /* The server's domain is longer than the wildcard, so it
1808  * could be a subdomain. Compare the last portion of the
1809  * server's domain.
1810  */
1811  ptr = dot + 1 + len - domain.len + 2;
1812  if(!strncmpiW(ptr, domain.str+2, domain.len-2))
1813  /* This is only a match if the preceding character is
1814  * a '.', i.e. that it is a matching domain. E.g.
1815  * if domain is '*.b.c' and server is 'www.ab.c' they
1816  * do not match.
1817  */
1818  return *(ptr - 1) == '.';
1819 
1820  return len == domain.len-2 && !strncmpiW(dot + 1, domain.str + 2, len);
1821 }
1822 
1824 {
1825  LPCWSTR ptr;
1826  BOOL ret = FALSE;
1827 
1828  if (!lpwai->proxyBypass) return FALSE;
1829  ptr = lpwai->proxyBypass;
1830  while(1) {
1831  LPCWSTR tmp = ptr;
1832 
1833  ptr = strchrW( ptr, ';' );
1834  if (!ptr)
1835  ptr = strchrW( tmp, ' ' );
1836  if (!ptr)
1837  ptr = tmp + strlenW(tmp);
1838  ret = HTTP_DomainMatches( server, substr(tmp, ptr-tmp) );
1839  if (ret || !*ptr)
1840  break;
1841  ptr++;
1842  }
1843  return ret;
1844 }
1845 
1846 /***********************************************************************
1847  * HTTP_DealWithProxy
1848  */
1850 {
1851  static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
1852  static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
1853  static WCHAR szNul[] = { 0 };
1854  URL_COMPONENTSW UrlComponents = { sizeof(UrlComponents) };
1855  server_t *new_server = NULL;
1856  WCHAR *proxy;
1857 
1858  proxy = INTERNET_FindProxyForProtocol(hIC->proxy, protoHttp);
1859  if(!proxy)
1860  return FALSE;
1862  proxy, strlenW(szHttp), szHttp, strlenW(szHttp))) {
1863  WCHAR *proxy_url = heap_alloc(strlenW(proxy)*sizeof(WCHAR) + sizeof(szHttp));
1864  if(!proxy_url) {
1865  heap_free(proxy);
1866  return FALSE;
1867  }
1868  strcpyW(proxy_url, szHttp);
1869  strcatW(proxy_url, proxy);
1870  heap_free(proxy);
1871  proxy = proxy_url;
1872  }
1873 
1874  UrlComponents.dwHostNameLength = 1;
1875  if(InternetCrackUrlW(proxy, 0, 0, &UrlComponents) && UrlComponents.dwHostNameLength) {
1876  if( !request->path )
1877  request->path = szNul;
1878 
1879  new_server = get_server(substr(UrlComponents.lpszHostName, UrlComponents.dwHostNameLength),
1880  UrlComponents.nPort, UrlComponents.nScheme == INTERNET_SCHEME_HTTPS, TRUE);
1881  }
1882  heap_free(proxy);
1883  if(!new_server)
1884  return FALSE;
1885 
1886  request->proxy = new_server;
1887 
1888  TRACE("proxy server=%s port=%d\n", debugstr_w(new_server->name), new_server->port);
1889  return TRUE;
1890 }
1891 
1893 {
1894  server_t *server = request->proxy ? request->proxy : request->server;
1895  int addr_len;
1896 
1897  if(server->addr_len)
1898  return ERROR_SUCCESS;
1899 
1902  server->name,
1903  (strlenW(server->name)+1) * sizeof(WCHAR));
1904 
1905  addr_len = sizeof(server->addr);
1906  if (!GetAddress(server->name, server->port, (SOCKADDR*)&server->addr, &addr_len, server->addr_str))
1908 
1909  server->addr_len = addr_len;
1912  server->addr_str, strlen(server->addr_str)+1);
1913 
1914  TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str);
1915  return ERROR_SUCCESS;
1916 }
1917 
1919 {
1920  static const WCHAR http[] = { 'h','t','t','p',':','/','/',0 };
1921  static const WCHAR https[] = { 'h','t','t','p','s',':','/','/',0 };
1922  const WCHAR *host, *scheme;
1923  WCHAR *buf, *ptr;
1924  size_t len;
1925 
1926  host = req->server->canon_host_port;
1927 
1928  if (req->server->is_https)
1929  scheme = https;
1930  else
1931  scheme = http;
1932 
1933  len = strlenW(scheme) + strlenW(host) + (req->path[0] != '/' ? 1 : 0) + strlenW(req->path);
1934  ptr = buf = heap_alloc((len+1) * sizeof(WCHAR));
1935  if(buf) {
1936  strcpyW(ptr, scheme);
1937  ptr += strlenW(ptr);
1938 
1939  strcpyW(ptr, host);
1940  ptr += strlenW(ptr);
1941 
1942  if(req->path[0] != '/')
1943  *ptr++ = '/';
1944 
1945  strcpyW(ptr, req->path);
1946  ptr += strlenW(ptr);
1947  *ptr = 0;
1948  }
1949 
1950  return buf;
1951 }
1952 
1953 
1954 /***********************************************************************
1955  * HTTPREQ_Destroy (internal)
1956  *
1957  * Deallocate request handle
1958  *
1959  */
1961 {
1963  DWORD i;
1964 
1965  TRACE("\n");
1966 
1967  if(request->hCacheFile)
1968  CloseHandle(request->hCacheFile);
1969  if(request->req_file)
1970  req_file_release(request->req_file);
1971 
1972  request->headers_section.DebugInfo->Spare[0] = 0;
1973  DeleteCriticalSection( &request->headers_section );
1974  request->read_section.DebugInfo->Spare[0] = 0;
1975  DeleteCriticalSection( &request->read_section );
1976  WININET_Release(&request->session->hdr);
1977 
1978  destroy_authinfo(request->authInfo);
1979  destroy_authinfo(request->proxyAuthInfo);
1980 
1981  if(request->server)
1982  server_release(request->server);
1983  if(request->proxy)
1984  server_release(request->proxy);
1985 
1989  heap_free(request->statusText);
1990 
1991  for (i = 0; i < request->nCustHeaders; i++)
1992  {
1993  heap_free(request->custHeaders[i].lpszField);
1994  heap_free(request->custHeaders[i].lpszValue);
1995  }
1996  destroy_data_stream(request->data_stream);
1997  heap_free(request->custHeaders);
1998 }
1999 
2000 static void http_release_netconn(http_request_t *req, BOOL reuse)
2001 {
2002  TRACE("%p %p %x\n",req, req->netconn, reuse);
2003 
2004  if(!is_valid_netconn(req->netconn))
2005  return;
2006 
2007 #ifndef __REACTOS__
2008  if(reuse && req->netconn->keep_alive) {
2009  BOOL run_collector;
2010 
2012 
2015  req->netconn = NULL;
2016 
2017  run_collector = !collector_running;
2019 
2021 
2022  if(run_collector) {
2023  HANDLE thread = NULL;
2024  HMODULE module;
2025 
2026  GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
2027  if(module)
2029  if(!thread) {
2033 
2034  if(module)
2036  }
2037  else
2039  }
2040  return;
2041  }
2042 #else
2043  /* Silence unused function warning */
2045 #endif
2046 
2047  INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2049 
2050  close_netconn(req->netconn);
2051 
2052  INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
2054 }
2055 
2057 {
2058  WCHAR szVersion[10];
2059  WCHAR szConnectionResponse[20];
2060  DWORD dwBufferSize = sizeof(szVersion);
2061  BOOL keepalive = FALSE;
2062 
2063  /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
2064  * the connection is keep-alive by default */
2067  {
2068  keepalive = TRUE;
2069  }
2070 
2071  dwBufferSize = sizeof(szConnectionResponse);
2072  if (HTTP_HttpQueryInfoW(request, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
2073  || HTTP_HttpQueryInfoW(request, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
2074  {
2075  keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
2076  }
2077 
2078  return keepalive;
2079 }
2080 
2082 {
2084 
2086 }
2087 
2088 static DWORD str_to_buffer(const WCHAR *str, void *buffer, DWORD *size, BOOL unicode)
2089 {
2090  int len;
2091  if (unicode)
2092  {
2093  WCHAR *buf = buffer;
2094 
2095  if (str) len = strlenW(str);
2096  else len = 0;
2097  if (*size < (len + 1) * sizeof(WCHAR))
2098  {
2099  *size = (len + 1) * sizeof(WCHAR);
2101  }
2102  if (str) strcpyW(buf, str);
2103  else buf[0] = 0;
2104 
2105  *size = len;
2106  return ERROR_SUCCESS;
2107  }
2108  else
2109  {
2110  char *buf = buffer;
2111 
2112  if (str) len = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
2113  else len = 1;
2114  if (*size < len)
2115  {
2116  *size = len;
2118  }
2119  if (str) WideCharToMultiByte(CP_ACP, 0, str, -1, buf, *size, NULL, NULL);
2120  else buf[0] = 0;
2121 
2122  *size = len - 1;
2123  return ERROR_SUCCESS;
2124  }
2125 }
2126 
2128 {
2130 
2131  switch(option) {
2133  {
2135 
2136  FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
2137 
2138  if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
2141  /* FIXME: can't get a SOCKET from our connection since we don't use
2142  * winsock
2143  */
2144  info->Socket = 0;
2145  /* FIXME: get source port from req->netConnection */
2146  info->SourcePort = 0;
2147  info->DestPort = req->server->port;
2148  info->Flags = 0;
2149  if (HTTP_KeepAlive(req))
2150  info->Flags |= IDSI_FLAG_KEEP_ALIVE;
2151  if (req->proxy)
2152  info->Flags |= IDSI_FLAG_PROXY;
2153  if (is_valid_netconn(req->netconn) && req->netconn->secure)
2154  info->Flags |= IDSI_FLAG_SECURE;
2155 
2156  return ERROR_SUCCESS;
2157  }
2158 
2159  case 98:
2160  TRACE("Queried undocumented option 98, forwarding to INTERNET_OPTION_SECURITY_FLAGS\n");
2161  /* fall through */
2163  {
2164  DWORD flags;
2165 
2166  if (*size < sizeof(ULONG))
2168 
2169  *size = sizeof(DWORD);
2171  *(DWORD *)buffer = flags;
2172 
2173  TRACE("INTERNET_OPTION_SECURITY_FLAGS %x\n", flags);
2174  return ERROR_SUCCESS;
2175  }
2176 
2178  TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2179 
2180  if (*size < sizeof(ULONG))
2182 
2183  *size = sizeof(DWORD);
2185  return ERROR_SUCCESS;
2186 
2187  case INTERNET_OPTION_URL: {
2188  WCHAR *url;
2189  DWORD res;
2190 
2191  TRACE("INTERNET_OPTION_URL\n");
2192 
2193  url = compose_request_url(req);
2194  if(!url)
2195  return ERROR_OUTOFMEMORY;
2196 
2197  res = str_to_buffer(url, buffer, size, unicode);
2198  heap_free(url);
2199  return res;
2200  }
2202  return str_to_buffer(req->session->appInfo->agent, buffer, size, unicode);
2204  return str_to_buffer(req->session->userName, buffer, size, unicode);
2206  return str_to_buffer(req->session->password, buffer, size, unicode);
2208  return str_to_buffer(req->session->appInfo->proxyUsername, buffer, size, unicode);
2210  return str_to_buffer(req->session->appInfo->proxyPassword, buffer, size, unicode);
2211 
2215  DWORD nbytes, error;
2216  BOOL ret;
2217 
2218  TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2219 
2220  if(!req->req_file)
2221  return ERROR_FILE_NOT_FOUND;
2222 
2223  if (*size < sizeof(*ts))
2224  {
2225  *size = sizeof(*ts);
2227  }
2228 
2229  nbytes = 0;
2230  ret = GetUrlCacheEntryInfoW(req->req_file->url, NULL, &nbytes);
2231  error = GetLastError();
2232  if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
2233  {
2234  if (!(info = heap_alloc(nbytes)))
2235  return ERROR_OUTOFMEMORY;
2236 
2237  GetUrlCacheEntryInfoW(req->req_file->url, info, &nbytes);
2238 
2239  ts->ftExpires = info->ExpireTime;
2240  ts->ftLastModified = info->LastModifiedTime;
2241 
2242  heap_free(info);
2243  *size = sizeof(*ts);
2244  return ERROR_SUCCESS;
2245  }
2246  return error;
2247  }
2248 
2250  DWORD req_size;
2251 
2252  TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2253 
2254  if(!req->req_file) {
2255  *size = 0;
2257  }
2258 
2259  if(unicode) {
2260  req_size = (lstrlenW(req->req_file->file_name)+1) * sizeof(WCHAR);
2261  if(*size < req_size)
2263 
2264  *size = req_size;
2265  memcpy(buffer, req->req_file->file_name, *size);
2266  return ERROR_SUCCESS;
2267  }else {
2268  req_size = WideCharToMultiByte(CP_ACP, 0, req->req_file->file_name, -1, NULL, 0, NULL, NULL);
2269  if (req_size > *size)
2271 
2273  -1, buffer, *size, NULL, NULL);
2274  return ERROR_SUCCESS;
2275  }
2276  }
2277 
2280 
2281  if(!req->netconn)
2283 
2284  if(*size < sizeof(INTERNET_CERTIFICATE_INFOA)) {
2285  *size = sizeof(INTERNET_CERTIFICATE_INFOA);
2287  }
2288 
2290  if(context) {
2292  DWORD len;
2293 
2294  memset(info, 0, sizeof(*info));
2295  info->ftExpiry = context->pCertInfo->NotAfter;
2296  info->ftStart = context->pCertInfo->NotBefore;
2297  len = CertNameToStrA(context->dwCertEncodingType,
2298  &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2299  info->lpszSubjectInfo = LocalAlloc(0, len);
2300  if(info->lpszSubjectInfo)
2301  CertNameToStrA(context->dwCertEncodingType,
2303  info->lpszSubjectInfo, len);
2304  len = CertNameToStrA(context->dwCertEncodingType,
2305  &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR|CERT_NAME_STR_CRLF_FLAG, NULL, 0);
2306  info->lpszIssuerInfo = LocalAlloc(0, len);
2307  if(info->lpszIssuerInfo)
2308  CertNameToStrA(context->dwCertEncodingType,
2310  info->lpszIssuerInfo, len);
2311  info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
2313  return ERROR_SUCCESS;
2314  }
2315  return ERROR_NOT_SUPPORTED;
2316  }
2318  if (*size < sizeof(DWORD))
2320 
2321  *size = sizeof(DWORD);
2322  *(DWORD *)buffer = req->connect_timeout;
2323  return ERROR_SUCCESS;
2325  DWORD flags = 0;
2326 
2327  if (*size < sizeof(DWORD))
2329 
2330  /* FIXME: Add support for:
2331  * INTERNET_REQFLAG_FROM_CACHE
2332  * INTERNET_REQFLAG_CACHE_WRITE_DISABLED
2333  */
2334 
2335  if(req->proxy)
2337  if(!req->status_code)
2339 
2340  TRACE("INTERNET_OPTION_REQUEST_FLAGS returning %x\n", flags);
2341 
2342  *size = sizeof(DWORD);
2343  *(DWORD*)buffer = flags;
2344  return ERROR_SUCCESS;
2345  }
2347  TRACE("INTERNET_OPTION_ERROR_MASK\n");
2348 
2349  if (*size < sizeof(ULONG))
2351 
2352  *(ULONG*)buffer = hdr->ErrorMask;
2353  *size = sizeof(ULONG);
2354  return ERROR_SUCCESS;
2355  }
2356 
2357  return INET_QueryOption(hdr, option, buffer, size, unicode);
2358 }
2359 
2361 {
2363 
2364  switch(option) {
2365  case 99: /* Undocumented, seems to be INTERNET_OPTION_SECURITY_FLAGS with argument validation */
2366  TRACE("Undocumented option 99\n");
2367 
2368  if (!buffer || size != sizeof(DWORD))
2369  return ERROR_INVALID_PARAMETER;
2370  if(*(DWORD*)buffer & ~SECURITY_SET_MASK)
2372 
2373  /* fall through */
2375  {
2376  DWORD flags;
2377 
2378  if (!buffer || size != sizeof(DWORD))
2379  return ERROR_INVALID_PARAMETER;
2380  flags = *(DWORD *)buffer;
2381  TRACE("INTERNET_OPTION_SECURITY_FLAGS %08x\n", flags);
2383  req->security_flags |= flags;
2384  if(is_valid_netconn(req->netconn))
2385  req->netconn->security_flags |= flags;
2386  return ERROR_SUCCESS;
2387  }
2389  if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2390  req->connect_timeout = *(DWORD *)buffer;
2391  return ERROR_SUCCESS;
2392 
2394  if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2395  req->send_timeout = *(DWORD *)buffer;
2396  return ERROR_SUCCESS;
2397 
2399  if (!buffer || size != sizeof(DWORD)) return ERROR_INVALID_PARAMETER;
2400  req->receive_timeout = *(DWORD *)buffer;
2401  return ERROR_SUCCESS;
2402 
2404  heap_free(req->session->userName);
2405  if (!(req->session->userName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2406  return ERROR_SUCCESS;
2407 
2409  heap_free(req->session->password);
2410  if (!(req->session->password = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
2411  return ERROR_SUCCESS;
2412 
2416  return ERROR_SUCCESS;
2417 
2421  return ERROR_SUCCESS;
2422 
2423  }
2424 
2425  return INET_SetOption(hdr, option, buffer, size);
2426 }
2427 
2429 {
2430  WCHAR *header;
2431  DWORD header_len;
2432  BOOL res;
2433 
2434  TRACE("%p\n", req);
2435 
2436  CloseHandle(req->hCacheFile);
2437  req->hCacheFile = NULL;
2438 
2440  header_len = (header ? strlenW(header) : 0);
2443  header, header_len, NULL, 0);
2444  if(res)
2445  req->req_file->is_committed = TRUE;
2446  else
2447  WARN("CommitUrlCacheEntry failed: %u\n", GetLastError());
2448  heap_free(header);
2449 }
2450 
2452 {
2453  static const WCHAR no_cacheW[] = {'n','o','-','c','a','c','h','e',0};
2454  static const WCHAR no_storeW[] = {'n','o','-','s','t','o','r','e',0};
2455 
2457  WCHAR *url;
2458  BOOL b = TRUE;
2459 
2460  /* FIXME: We should free previous cache file earlier */
2461  if(req->req_file) {
2462  req_file_release(req->req_file);
2463  req->req_file = NULL;
2464  }
2465  if(req->hCacheFile) {
2466  CloseHandle(req->hCacheFile);
2467  req->hCacheFile = NULL;
2468  }
2469 
2471  b = FALSE;
2472 
2473  if(b) {
2474  int header_idx;
2475 
2477 
2478  header_idx = HTTP_GetCustomHeaderIndex(req, szCache_Control, 0, FALSE);
2479  if(header_idx != -1) {
2480  WCHAR *ptr;
2481 
2482  for(ptr=req->custHeaders[header_idx].lpszValue; *ptr; ) {
2483  WCHAR *end;
2484 
2485  while(*ptr==' ' || *ptr=='\t')
2486  ptr++;
2487 
2488  end = strchrW(ptr, ',');
2489  if(!end)
2490  end = ptr + strlenW(ptr);
2491 
2492  if(!strncmpiW(ptr, no_cacheW, ARRAY_SIZE(no_cacheW)-1)
2493  || !strncmpiW(ptr, no_storeW, ARRAY_SIZE(no_storeW)-1)) {
2494  b = FALSE;
2495  break;
2496  }
2497 
2498  ptr = end;
2499  if(*ptr == ',')
2500  ptr++;
2501  }
2502  }
2503 
2505  }
2506 
2507  if(!b) {
2508  if(!(req->hdr.dwFlags & INTERNET_FLAG_NEED_FILE))
2509  return;
2510 
2511  FIXME("INTERNET_FLAG_NEED_FILE is not supported correctly\n");
2512  }
2513 
2514  url = compose_request_url(req);
2515  if(!url) {
2516  WARN("Could not get URL\n");
2517  return;
2518  }
2519 
2520  b = CreateUrlCacheEntryW(url, req->contentLength == ~0 ? 0 : req->contentLength, NULL, file_name, 0);
2521  if(!b) {
2522  WARN("Could not create cache entry: %08x\n", GetLastError());
2523  return;
2524  }
2525 
2527  req->req_file->url = url;
2528 
2531  if(req->hCacheFile == INVALID_HANDLE_VALUE) {
2532  WARN("Could not create file: %u\n", GetLastError());
2533  req->hCacheFile = NULL;
2534  return;
2535  }
2536 
2537  if(req->read_size) {
2538  DWORD written;
2539 
2540  b = WriteFile(req->hCacheFile, req->read_buf+req->read_pos, req->read_size, &written, NULL);
2541  if(!b)
2542  FIXME("WriteFile failed: %u\n", GetLastError());
2543 
2544  if(req->data_stream->vtbl->end_of_data(req->data_stream, req))
2545  commit_cache_entry(req);
2546  }
2547 }
2548 
2549 /* read some more data into the read buffer (the read section must be held) */
2550 static DWORD read_more_data( http_request_t *req, int maxlen )
2551 {
2552  DWORD res;
2553  int len;
2554 
2555  if (req->read_pos)
2556  {
2557  /* move existing data to the start of the buffer */
2558  if(req->read_size)
2559  memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
2560  req->read_pos = 0;
2561  }
2562 
2563  if (maxlen == -1) maxlen = sizeof(req->read_buf);
2564 
2565  res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
2566  maxlen - req->read_size, TRUE, &len );
2567  if(res == ERROR_SUCCESS)
2568  req->read_size += len;
2569 
2570  return res;
2571 }
2572 
2573 /* remove some amount of data from the read buffer (the read section must be held) */
2574 static void remove_data( http_request_t *req, int count )
2575 {
2576  if (!(req->read_size -= count)) req->read_pos = 0;
2577  else req->read_pos += count;
2578 }
2579 
2581 {
2582  int count, bytes_read, pos = 0;
2583  DWORD res;
2584 
2586  for (;;)
2587  {
2588  BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
2589 
2590  if (eol)
2591  {
2592  count = eol - (req->read_buf + req->read_pos);
2593  bytes_read = count + 1;
2594  }
2595  else count = bytes_read = req->read_size;
2596 
2597  count = min( count, *len - pos );
2598  memcpy( buffer + pos, req->read_buf + req->read_pos, count );
2599  pos += count;
2600  remove_data( req, bytes_read );
2601  if (eol) break;
2602 
2603  if ((res = read_more_data( req, -1 )))
2604  {
2605  WARN( "read failed %u\n", res );
2607  return res;
2608  }
2609  if (!req->read_size)
2610  {
2611  *len = 0;
2612  TRACE( "returning empty string\n" );
2614  return ERROR_SUCCESS;
2615  }
2616  }
2618 
2619  if (pos < *len)
2620  {
2621  if (pos && buffer[pos - 1] == '\r') pos--;
2622  *len = pos + 1;
2623  }
2624  buffer[*len - 1] = 0;
2625  TRACE( "returning %s\n", debugstr_a(buffer));
2626  return ERROR_SUCCESS;
2627 }
2628 
2629 /* check if we have reached the end of the data to read (the read section must be held) */
2631 {
2632  return !req->read_size && req->data_stream->vtbl->end_of_data(req->data_stream, req);
2633 }
2634 
2636 {
2637  DWORD res;
2638 
2639  res = req->data_stream->vtbl->read(req->data_stream, req, buf, size, read, allow_blocking);
2640  if(res != ERROR_SUCCESS)
2641  *read = 0;
2642  assert(*read <= size);
2643 
2644  if(req->hCacheFile) {
2645  if(*read) {
2646  BOOL bres;
2647  DWORD written;
2648 
2649  bres = WriteFile(req->hCacheFile, buf, *read, &written, NULL);
2650  if(!bres)
2651  FIXME("WriteFile failed: %u\n", GetLastError());
2652  }
2653 
2654  if((res == ERROR_SUCCESS && !*read) || req->data_stream->vtbl->end_of_data(req->data_stream, req))
2655  commit_cache_entry(req);
2656  }
2657 
2658  return res;
2659 }
2660 
2661 /* fetch some more data into the read buffer (the read section must be held) */
2663 {
2664  DWORD res, read=0;
2665 
2666  if(req->read_size == sizeof(req->read_buf))
2667  return ERROR_SUCCESS;
2668 
2669  if(req->read_pos) {
2670  if(req->read_size)
2671  memmove(req->read_buf, req->read_buf+req->read_pos, req->read_size);
2672  req->read_pos = 0;
2673  }
2674 
2675  res = read_http_stream(req, req->read_buf+req->read_size, sizeof(req->read_buf) - req->read_size,
2676  &read, allow_blocking);
2677  if(res != ERROR_SUCCESS)
2678  return res;
2679 
2680  req->read_size += read;
2681 
2682  TRACE("read %u bytes, read_size %u\n", read, req->read_size);
2683  if(read_bytes)
2684  *read_bytes = read;
2685  return res;
2686 }
2687 
2689 {
2690  netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2691  return netconn_stream->content_read == netconn_stream->content_length || !is_valid_netconn(req->netconn);
2692 }
2693 
2695  DWORD *read, BOOL allow_blocking)
2696 {
2697  netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2699  int ret = 0;
2700 
2701  size = min(size, netconn_stream->content_length-netconn_stream->content_read);
2702 
2703  if(size && is_valid_netconn(req->netconn)) {
2704  res = NETCON_recv(req->netconn, buf, size, allow_blocking, &ret);
2705  if(res == ERROR_SUCCESS) {
2706  if(!ret)
2707  netconn_stream->content_length = netconn_stream->content_read;
2708  netconn_stream->content_read += ret;
2709  }
2710  }
2711 
2712  TRACE("res %u read %u bytes\n", res, ret);
2713  *read = ret;
2714  return res;
2715 }
2716 
2718 {
2719  netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
2720  BYTE buf[1024];
2721  int len, res;
2722  size_t size;
2723 
2724  if(netconn_stream->content_length == ~0)
2725  return WSAEISCONN;
2726 
2727  while(netconn_stream->content_read < netconn_stream->content_length) {
2728  size = min(sizeof(buf), netconn_stream->content_length-netconn_stream->content_read);
2729  res = NETCON_recv(req->netconn, buf, size, allow_blocking, &len);
2730  if(res)
2731  return res;
2732  if(!len)
2733  return WSAECONNABORTED;
2734 
2735  netconn_stream->content_read += len;
2736  }
2737 
2738  return ERROR_SUCCESS;
2739 }
2740 
2742 {
2743 }
2744 
2747  netconn_read,
2750 };
2751 
2753 {
2754  assert(stream->buf_size);
2755 
2756  stream->buf_size--;
2757  return stream->buf[stream->buf_pos++];
2758 }
2759 
2761 {
2762  chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2763  switch(chunked_stream->state) {
2764  case CHUNKED_STREAM_STATE_DISCARD_EOL_AT_END:
2765  case CHUNKED_STREAM_STATE_END_OF_STREAM:
2766  case CHUNKED_STREAM_STATE_ERROR:
2767  return TRUE;
2768  default:
2769  return FALSE;
2770  }
2771 }
2772 
2774  DWORD *read, BOOL allow_blocking)
2775 {
2776  chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2777  DWORD ret_read = 0, res = ERROR_SUCCESS;
2778  BOOL continue_read = TRUE;
2779  int read_bytes;
2780  char ch;
2781 
2782  do {
2783  TRACE("state %d\n", chunked_stream->state);
2784 
2785  /* Ensure that we have data in the buffer for states that need it. */
2786  if(!chunked_stream->buf_size) {
2787  BOOL blocking_read = allow_blocking;
2788 
2789  switch(chunked_stream->state) {
2790  case CHUNKED_STREAM_STATE_DISCARD_EOL_AT_END:
2791  case CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_SIZE:
2792  /* never allow blocking after 0 chunk size */
2793  if(!chunked_stream->chunk_size)
2794  blocking_read = FALSE;
2795  /* fall through */
2796  case CHUNKED_STREAM_STATE_READING_CHUNK_SIZE:
2797  case CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_DATA:
2798  chunked_stream->buf_pos = 0;
2799  res = NETCON_recv(req->netconn, chunked_stream->buf, sizeof(chunked_stream->buf), blocking_read, &read_bytes);
2800  if(res == ERROR_SUCCESS && read_bytes) {
2801  chunked_stream->buf_size += read_bytes;
2802  }else if(res == WSAEWOULDBLOCK) {
2803  if(ret_read || allow_blocking)
2804  res = ERROR_SUCCESS;
2805  continue_read = FALSE;
2806  continue;
2807  }else {
2808  chunked_stream->state = CHUNKED_STREAM_STATE_ERROR;
2809  }
2810  break;
2811  default:
2812  break;
2813  }
2814  }
2815 
2816  switch(chunked_stream->state) {
2817  case CHUNKED_STREAM_STATE_READING_CHUNK_SIZE:
2818  ch = next_chunked_data_char(chunked_stream);
2819 
2820  if(ch >= '0' && ch <= '9') {
2821  chunked_stream->chunk_size = chunked_stream->chunk_size * 16 + ch - '0';
2822  }else if(ch >= 'a' && ch <= 'f') {
2823  chunked_stream->chunk_size = chunked_stream->chunk_size * 16 + ch - 'a' + 10;
2824  }else if (ch >= 'A' && ch <= 'F') {
2825  chunked_stream->chunk_size = chunked_stream->chunk_size * 16 + ch - 'A' + 10;
2826  }else if (ch == ';' || ch == '\r' || ch == '\n') {
2827  TRACE("reading %u byte chunk\n", chunked_stream->chunk_size);
2828  chunked_stream->buf_size++;
2829  chunked_stream->buf_pos--;
2830  if(req->contentLength == ~0) req->contentLength = chunked_stream->chunk_size;
2831  else req->contentLength += chunked_stream->chunk_size;
2832  chunked_stream->state = CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_SIZE;
2833  }
2834  break;
2835 
2836  case CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_SIZE:
2837  ch = next_chunked_data_char(chunked_stream);
2838  if(ch == '\n')
2839  chunked_stream->state = chunked_stream->chunk_size
2840  ? CHUNKED_STREAM_STATE_READING_CHUNK
2841  : CHUNKED_STREAM_STATE_DISCARD_EOL_AT_END;
2842  else if(ch != '\r')
2843  WARN("unexpected char '%c'\n", ch);
2844  break;
2845 
2846  case CHUNKED_STREAM_STATE_READING_CHUNK:
2847  assert(chunked_stream->chunk_size);
2848  if(!size) {
2849  continue_read = FALSE;
2850  break;
2851  }
2852  read_bytes = min(size, chunked_stream->chunk_size);
2853 
2854  if(chunked_stream->buf_size) {
2855  if(read_bytes > chunked_stream->buf_size)
2856  read_bytes = chunked_stream->buf_size;
2857 
2858  memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
2859  chunked_stream->buf_pos += read_bytes;
2860  chunked_stream->buf_size -= read_bytes;
2861  }else {
2862  res = NETCON_recv(req->netconn, (char*)buf+ret_read, read_bytes,
2863  allow_blocking, (int*)&read_bytes);
2864  if(res != ERROR_SUCCESS) {
2865  continue_read = FALSE;
2866  break;
2867  }
2868 
2869  if(!read_bytes) {
2870  chunked_stream->state = CHUNKED_STREAM_STATE_ERROR;
2871  continue;
2872  }
2873  }
2874 
2875  chunked_stream->chunk_size -= read_bytes;
2876  size -= read_bytes;
2877  ret_read += read_bytes;
2878  if(!chunked_stream->chunk_size)
2879  chunked_stream->state = CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_DATA;
2880  allow_blocking = FALSE;
2881  break;
2882 
2883  case CHUNKED_STREAM_STATE_DISCARD_EOL_AFTER_DATA:
2884  ch = next_chunked_data_char(chunked_stream);
2885  if(ch == '\n')
2886  chunked_stream->state = CHUNKED_STREAM_STATE_READING_CHUNK_SIZE;
2887  else if(ch != '\r')
2888  WARN("unexpected char '%c'\n", ch);
2889  break;
2890 
2891  case CHUNKED_STREAM_STATE_DISCARD_EOL_AT_END:
2892  ch = next_chunked_data_char(chunked_stream);
2893  if(ch == '\n')
2894  chunked_stream->state = CHUNKED_STREAM_STATE_END_OF_STREAM;
2895  else if(ch != '\r')
2896  WARN("unexpected char '%c'\n", ch);
2897  break;
2898 
2899  case CHUNKED_STREAM_STATE_END_OF_STREAM:
2900  case CHUNKED_STREAM_STATE_ERROR:
2901  continue_read = FALSE;
2902  break;
2903  }
2904  } while(continue_read);
2905 
2906  if(ret_read)
2907  res = ERROR_SUCCESS;
2908  if(res != ERROR_SUCCESS)
2909  return res;
2910 
2911  TRACE("read %d bytes\n", ret_read);
2912  *read = ret_read;
2913  return ERROR_SUCCESS;
2914 }
2915 
2917 {
2918  chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2919  BYTE buf[1024];
2920  DWORD size, res;
2921 
2922  while(chunked_stream->state != CHUNKED_STREAM_STATE_END_OF_STREAM
2923  && chunked_stream->state != CHUNKED_STREAM_STATE_ERROR) {
2924  res = chunked_read(stream, req, buf, sizeof(buf), &size, allow_blocking);
2925  if(res != ERROR_SUCCESS)
2926  return res;
2927  }
2928 
2929  if(chunked_stream->state != CHUNKED_STREAM_STATE_END_OF_STREAM)
2930  return ERROR_NO_DATA;
2931  return ERROR_SUCCESS;
2932 }
2933 
2935 {
2936  chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
2937  heap_free(chunked_stream);
2938 }
2939 
2942  chunked_read,
2945 };
2946 
2947 /* set the request content length based on the headers */
2949 {
2950  static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
2951  static const WCHAR headW[] = {'H','E','A','D',0};
2952  WCHAR contentLength[32];
2953  WCHAR encoding[20];
2954  DWORD size;
2955 
2956  if(request->status_code == HTTP_STATUS_NO_CONTENT || !strcmpW(request->verb, headW)) {
2957  request->contentLength = request->netconn_stream.content_length = 0;
2958  return ERROR_SUCCESS;
2959  }
2960 
2961  size = sizeof(contentLength);
2963  contentLength, &size, NULL) != ERROR_SUCCESS ||
2964  !StrToInt64ExW(contentLength, STIF_DEFAULT, (LONGLONG*)&request->contentLength)) {
2965  request->contentLength = ~0;
2966  }
2967 
2968  request->netconn_stream.content_length = request->contentLength;
2969  request->netconn_stream.content_read = request->read_size;
2970 
2971  size = sizeof(encoding);
2973  !strcmpiW(encoding, szChunked))
2974  {
2975  chunked_stream_t *chunked_stream;
2976 
2977  chunked_stream = heap_alloc(sizeof(*chunked_stream));
2978  if(!chunked_stream)
2979  return ERROR_OUTOFMEMORY;
2980 
2981  chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
2982  chunked_stream->buf_size = chunked_stream->buf_pos = 0;
2983  chunked_stream->chunk_size = 0;
2984  chunked_stream->state = CHUNKED_STREAM_STATE_READING_CHUNK_SIZE;
2985 
2986  if(request->read_size) {
2987  memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
2988  chunked_stream->buf_size = request->read_size;
2990  }
2991 
2992  request->data_stream = &chunked_stream->data_stream;
2993  request->contentLength = ~0;
2994  }
2995 
2996  if(request->hdr.decoding) {
2997  int encoding_idx;
2998 
2999  static const WCHAR deflateW[] = {'d','e','f','l','a','t','e',0};
3000  static const WCHAR gzipW[] = {'g','z','i','p',0};
3001 
3002  EnterCriticalSection( &request->headers_section );
3003 
3005  if(encoding_idx != -1) {
3006  if(!strcmpiW(request->custHeaders[encoding_idx].lpszValue, gzipW)) {
3007  HTTP_DeleteCustomHeader(request, encoding_idx);
3008  LeaveCriticalSection( &request->headers_section );
3009  return init_gzip_stream(request, TRUE);
3010  }
3011  if(!strcmpiW(request->custHeaders[encoding_idx].lpszValue, deflateW)) {
3012  HTTP_DeleteCustomHeader(request, encoding_idx);
3013  LeaveCriticalSection( &request->headers_section );
3014  return init_gzip_stream(request, FALSE);
3015  }
3016  }
3017 
3018  LeaveCriticalSection( &request->headers_section );
3019  }
3020 
3021  return ERROR_SUCCESS;
3022 }
3023 
3025 {
3027 
3028  iar.dwResult = result;
3029  iar.dwError = error;
3030 
3032  sizeof(INTERNET_ASYNC_RESULT));
3033 }
3034 
3036 {
3037  DWORD res, read = 0;
3038 
3039  TRACE("%p\n", req);
3040 
3042 
3043  res = refill_read_buffer(req, FALSE, &read);
3044  if(res == ERROR_SUCCESS)
3045  read += req->read_size;
3046 
3048 
3049  if(res != WSAEWOULDBLOCK && (res != ERROR_SUCCESS || !read)) {
3050  WARN("res %u read %u, closing connection\n", res, read);
3052  }
3053 
3054  if(res != ERROR_SUCCESS && res != WSAEWOULDBLOCK) {
3055  send_request_complete(req, 0, res);
3056  return;
3057  }
3058 
3060 }
3061 
3062 /* read data from the http connection (the read section must be held) */
3063 static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL allow_blocking)
3064 {
3065  DWORD current_read = 0, ret_read = 0;
3067 
3069 
3070  if(req->read_size) {
3071  ret_read = min(size, req->read_size);
3072  memcpy(buffer, req->read_buf+req->read_pos, ret_read);
3073  req->read_size -= ret_read;
3074  req->read_pos += ret_read;
3075  allow_blocking = FALSE;
3076  }
3077 
3078  if(ret_read < size) {
3079  res = read_http_stream(req, (BYTE*)buffer+ret_read, size-ret_read, &current_read, allow_blocking);
3080  if(res == ERROR_SUCCESS)
3081  ret_read += current_read;
3082  else if(res == WSAEWOULDBLOCK && ret_read)
3083  res = ERROR_SUCCESS;
3084  }
3085 
3087 
3088  *read = ret_read;
3089  TRACE( "retrieved %u bytes (res %u)\n", ret_read, res );
3090 
3091  if(res != WSAEWOULDBLOCK) {
3092  if(res != ERROR_SUCCESS)
3094  else if(!ret_read && drain_content(req, FALSE) == ERROR_SUCCESS)
3095  http_release_netconn(req, TRUE);
3096  }
3097 
3098  return res;
3099 }
3100 
3101 static DWORD drain_content(http_request_t *req, BOOL blocking)
3102 {
3103  DWORD res;
3104 
3105  TRACE("%p\n", req->netconn);
3106 
3107  if(!is_valid_netconn(req->netconn))
3108  return ERROR_NO_DATA;
3109 
3110  if(!strcmpW(req->verb, szHEAD))
3111  return ERROR_SUCCESS;
3112 
3114  res = req->data_stream->vtbl->drain_content(req->data_stream, req, blocking);
3116  return res;
3117 }
3118 
3119 typedef struct {
3121  void *buf;
3126 
3128 {
3130  http_request_t *req = (http_request_t*)task->hdr.hdr;
3131  DWORD res = ERROR_SUCCESS, read = task->read_pos, complete_arg = 0;
3132 
3133  TRACE("req %p buf %p size %u read_pos %u ret_read %p\n", req, task->buf, task->size, task->read_pos, task->ret_read);
3134 
3135  if(task->buf) {
3136  DWORD read_bytes;
3137  while (read < task->size) {
3138  res = HTTPREQ_Read(req, (char*)task->buf + read, task->size - read, &read_bytes, TRUE);
3139  if (res != ERROR_SUCCESS || !read_bytes)
3140  break;
3141  read += read_bytes;
3142  }
3143  }else {
3145  res = refill_read_buffer(req, TRUE, &read);
3147 
3148  if(task->ret_read)
3149  complete_arg = read; /* QueryDataAvailable reports read bytes in request complete notification */
3150  if(res != ERROR_SUCCESS || !read)
3152  }
3153 
3154  TRACE("res %u read %u\n", res, read);
3155 
3156  if(task->ret_read)
3157  *task->ret_read = read;
3158 
3159  /* FIXME: We should report bytes transferred before decoding content. */
3161 
3162  if(res != ERROR_SUCCESS)
3163  complete_arg = res;
3164  send_request_complete(req, res == ERROR_SUCCESS, complete_arg);
3165 }
3166 
3167 static DWORD async_read(http_request_t *req, void *buf, DWORD size, DWORD read_pos, DWORD *ret_read)
3168 {
3169  read_file_task_t *task;
3170 
3171  task = alloc_async_task(&req->hdr, async_read_file_proc, sizeof(*task));
3172  if(!task)
3173  return ERROR_OUTOFMEMORY;
3174 
3175  task->buf = buf;
3176  task->size = size;
3177  task->read_pos = read_pos;
3178  task->ret_read = ret_read;
3179 
3180  INTERNET_AsyncCall(&task->hdr);
3181  return ERROR_IO_PENDING;
3182 }
3183 
3186 {
3188  DWORD res = ERROR_SUCCESS, read = 0, cread, error = ERROR_SUCCESS;
3189  BOOL allow_blocking, notify_received = FALSE;
3190 
3191  TRACE("(%p %p %u %x)\n", req, buf, size, flags);
3192 
3193  if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
3194  FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
3195 
3196  allow_blocking = !(req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC);
3197 
3198  if(allow_blocking || TryEnterCriticalSection(&req->read_section)) {
3199  if(allow_blocking)
3201  if(hdr->dwError == ERROR_SUCCESS)
3202  hdr->dwError = INTERNET_HANDLE_IN_USE;
3203  else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3205 
3206  if(req->read_size) {
3207  read = min(size, req->read_size);
3208  memcpy(buf, req->read_buf + req->read_pos, read);
3209  req->read_size -= read;
3210  req->read_pos += read;
3211  }
3212 
3213  if(read < size && (!read || !(flags & IRF_NO_WAIT)) && !end_of_read_data(req)) {
3217  notify_received = TRUE;
3218 
3219  while(read < size) {
3220  res = HTTPREQ_Read(req, (char*)buf+read, size-read, &cread, allow_blocking);
3221  read += cread;
3222  if (res != ERROR_SUCCESS || !cread)
3223  break;
3224  }
3225  }
3226 
3227  if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3228  hdr->dwError = ERROR_SUCCESS;
3229  else
3230  error = hdr->dwError;
3231 
3233  }else {
3234  res = WSAEWOULDBLOCK;
3235  }
3236 
3237  if(res == WSAEWOULDBLOCK) {
3238  if(!(flags & IRF_NO_WAIT))
3239  return async_read(req, buf, size, read, ret_read);
3240  if(!read)
3241  return async_read(req, NULL, 0, 0, NULL);
3242  res = ERROR_SUCCESS;
3243  }
3244 
3245  *ret_read = read;
3246  if (res != ERROR_SUCCESS)
3247  return res;
3248 
3249  if(notify_received)
3251  &read, sizeof(read));
3252  return error;
3253 }
3254 
3256 {
3257  DWORD res;
3259 
3261 
3262  *written = 0;
3263  res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
3264  if (res == ERROR_SUCCESS)
3265  request->bytesWritten += *written;
3266 
3268  return res;
3269 }
3270 
3272 {
3275  BOOL allow_blocking, notify_received = FALSE;
3276 
3277  TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
3278 
3279  if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
3280  FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
3281 
3282  *available = 0;
3283  allow_blocking = !(req->session->appInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC);
3284 
3285  if(allow_blocking || TryEnterCriticalSection(&req->read_section)) {
3286  if(allow_blocking)
3288  if(hdr->dwError == ERROR_SUCCESS)
3289  hdr->dwError = INTERNET_HANDLE_IN_USE;
3290  else if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3292 
3293  avail = req->read_size;
3294 
3295  if(!avail && !end_of_read_data(req)) {
3299  notify_received = TRUE;
3300 
3301  res = refill_read_buffer(req, allow_blocking, &avail);
3302  }
3303 
3304  if(hdr->dwError == INTERNET_HANDLE_IN_USE)
3305  hdr->dwError = ERROR_SUCCESS;
3306  else
3307  error = hdr->dwError;
3308 
3310  }else {
3311  res = WSAEWOULDBLOCK;
3312  }
3313 
3314  if(res == WSAEWOULDBLOCK)
3315  return async_read(req, NULL, 0, 0, available);
3316 
3317  if (res != ERROR_SUCCESS)
3318  return res;
3319 
3320  *available = avail;
3321  if(notify_received)
3323  &avail, sizeof(avail));
3324  return error;
3325 }
3326 
3328 {
3330 
3331  TRACE("(%p)\n", req);
3332 
3333  if(!req->req_file) {
3334  WARN("No cache file name available\n");
3335  return ERROR_FILE_NOT_FOUND;
3336  }
3337 
3338  *ret = req_file_addref(req->req_file);
3339  return ERROR_SUCCESS;
3340 }
3341 
3342 static const object_vtbl_t HTTPREQVtbl = {
3350  NULL,
3352 };
3353 
3354 /***********************************************************************
3355  * HTTP_HttpOpenRequestW (internal)
3356  *
3357  * Open a HTTP request handle
3358  *
3359  * RETURNS
3360  * HINTERNET a HTTP request handle on success
3361  * NULL on failure
3362  *
3363  */
3365  LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3366  LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3367  DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
3368 {
3369  appinfo_t *hIC = session->appInfo;
3371  DWORD port, len;
3372 
3373  TRACE("-->\n");
3374 
3376  if(!request)
3377  return ERROR_OUTOFMEMORY;
3378 
3379  request->hdr.htype = WH_HHTTPREQ;
3380  request->hdr.dwFlags = dwFlags;
3381  request->hdr.dwContext = dwContext;
3382  request->hdr.decoding = session->hdr.decoding;
3383  request->contentLength = ~0;
3384 
3385  request->netconn_stream.data_stream.vtbl = &netconn_stream_vtbl;
3386  request->data_stream = &request->netconn_stream.data_stream;
3390 
3391  InitializeCriticalSection( &request->headers_section );
3392  request->headers_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.headers_section");
3393 
3394  InitializeCriticalSection( &request->read_section );
3395  request->read_section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": http_request_t.read_section");
3396 
3397  WININET_AddRef( &session->hdr );
3398  request->session = session;
3399  list_add_head( &session->hdr.children, &request->hdr.entry );
3400 
3401  port = session->hostPort;
3403  port = (session->hdr.dwFlags & INTERNET_FLAG_SECURE) ?
3405 
3406  request->server = get_server(substrz(session->hostName), port, (dwFlags & INTERNET_FLAG_SECURE) != 0, TRUE);
3407  if(!request->server) {
3409  return ERROR_OUTOFMEMORY;
3410  }
3411 
3416 
3417  if (lpszObjectName && *lpszObjectName) {
3418  HRESULT rc;
3419  WCHAR dummy;
3420 
3421  len = 1;
3422  rc = UrlCanonicalizeW(lpszObjectName, &dummy, &len, URL_ESCAPE_SPACES_ONLY);
3423  if (rc != E_POINTER)
3424  len = strlenW(lpszObjectName)+1;
3425  request->path = heap_alloc(len*sizeof(WCHAR));
3426  rc = UrlCanonicalizeW(lpszObjectName, request->path, &len,
3428  if (rc != S_OK)
3429  {
3430  ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
3431  strcpyW(request->path,lpszObjectName);
3432  }
3433  }else {
3434  static const WCHAR slashW[] = {'/',0};
3435 
3437  }
3438 
3439  if (lpszReferrer && *lpszReferrer)
3441 
3442  if (lpszAcceptTypes)
3443  {
3444  int i;
3445  for (i = 0; lpszAcceptTypes[i]; i++)
3446  {
3447  if (!*lpszAcceptTypes[i]) continue;
3448  HTTP_ProcessHeader(request, HTTP_ACCEPT, lpszAcceptTypes[i],
3452  }
3453  }
3454 
3455  request->verb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
3456  request->version = heap_strdupW(lpszVersion && *lpszVersion ? lpszVersion : g_szHttp1_1);
3457 
3458  if (hIC->proxy && hIC->proxy[0] && !HTTP_ShouldBypassProxy(hIC, session->hostName))
3460 
3461  INTERNET_SendCallback(&session->hdr, dwContext,
3463  sizeof(HINTERNET));
3464 
3465  TRACE("<-- (%p)\n", request);
3466 
3467  *ret = request->hdr.hInternet;
3468  return ERROR_SUCCESS;
3469 }
3470 
3471 /***********************************************************************
3472  * HttpOpenRequestW (WININET.@)
3473  *
3474  * Open a HTTP request handle
3475  *
3476  * RETURNS
3477  * HINTERNET a HTTP request handle on success
3478  * NULL on failure
3479  *
3480  */
3482  LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
3483  LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
3484  DWORD dwFlags, DWORD_PTR dwContext)
3485 {
3487  HINTERNET handle = NULL;
3488  DWORD res;
3489 
3490  TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
3491  debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
3492  debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
3493  dwFlags, dwContext);
3494  if(lpszAcceptTypes!=NULL)
3495  {
3496  int i;
3497  for(i=0;lpszAcceptTypes[i]!=NULL;i++)
3498  TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
3499  }
3500 
3501  session = (http_session_t*) get_handle_object( hHttpSession );
3502  if (NULL == session || session->hdr.htype != WH_HHTTPSESSION)
3503  {
3505  goto lend;
3506  }
3507 
3508  /*
3509  * My tests seem to show that the windows version does not
3510  * become asynchronous until after this point. And anyhow
3511  * if this call was asynchronous then how would you get the
3512  * necessary HINTERNET pointer returned by this function.
3513  *
3514  */
3515  res = HTTP_HttpOpenRequestW(session, lpszVerb, lpszObjectName,
3516  lpszVersion, lpszReferrer, lpszAcceptTypes,
3517  dwFlags, dwContext, &handle);
3518 lend:
3519  if( session )
3521  TRACE("returning %p\n", handle);
3522  if(res != ERROR_SUCCESS)
3523  SetLastError(res);
3524  return handle;
3525 }
3526 
3527 static const LPCWSTR header_lookup[] = {
3528  szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
3529  szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3530  szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3531  szContent_ID, /* HTTP_QUERY_CONTENT_ID = 3 */
3532  NULL, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3533  szContent_Length, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3534  szContent_Language, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3535  szAllow, /* HTTP_QUERY_ALLOW = 7 */
3536  szPublic, /* HTTP_QUERY_PUBLIC = 8 */
3537  szDate, /* HTTP_QUERY_DATE = 9 */
3538  szExpires, /* HTTP_QUERY_EXPIRES = 10 */
3539  szLast_Modified, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3540  NULL, /* HTTP_QUERY_MESSAGE_ID = 12 */
3541  szURI, /* HTTP_QUERY_URI = 13 */
3542  szFrom, /* HTTP_QUERY_DERIVED_FROM = 14 */
3543  NULL, /* HTTP_QUERY_COST = 15 */
3544  NULL, /* HTTP_QUERY_LINK = 16 */
3545  szPragma, /* HTTP_QUERY_PRAGMA = 17 */
3546  NULL, /* HTTP_QUERY_VERSION = 18 */
3547  szStatus, /* HTTP_QUERY_STATUS_CODE = 19 */
3548  NULL, /* HTTP_QUERY_STATUS_TEXT = 20 */
3549  NULL, /* HTTP_QUERY_RAW_HEADERS = 21 */
3550  NULL, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3551  szConnection, /* HTTP_QUERY_CONNECTION = 23 */
3552  szAccept, /* HTTP_QUERY_ACCEPT = 24 */
3553  szAccept_Charset, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3554  szAccept_Encoding, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3555  szAccept_Language, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3556  szAuthorization, /* HTTP_QUERY_AUTHORIZATION = 28 */
3557  szContent_Encoding, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3558  NULL, /* HTTP_QUERY_FORWARDED = 30 */
3559  NULL, /* HTTP_QUERY_FROM = 31 */
3560  szIf_Modified_Since, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3561  szLocation, /* HTTP_QUERY_LOCATION = 33 */
3562  NULL, /* HTTP_QUERY_ORIG_URI = 34 */
3563  szReferer, /* HTTP_QUERY_REFERER = 35 */
3564  szRetry_After, /* HTTP_QUERY_RETRY_AFTER = 36 */
3565  szServer, /* HTTP_QUERY_SERVER = 37 */
3566  NULL, /* HTTP_TITLE = 38 */
3567  szUser_Agent, /* HTTP_QUERY_USER_AGENT = 39 */
3568  szWWW_Authenticate, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3569  szProxy_Authenticate, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3570  szAccept_Ranges, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3571  szSet_Cookie, /* HTTP_QUERY_SET_COOKIE = 43 */
3572  szCookie, /* HTTP_QUERY_COOKIE = 44 */
3573  NULL, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3574  NULL, /* HTTP_QUERY_REFRESH = 46 */
3575  szContent_Disposition, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3576  szAge, /* HTTP_QUERY_AGE = 48 */
3577  szCache_Control, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3578  szContent_Base, /* HTTP_QUERY_CONTENT_BASE = 50 */
3579  szContent_Location, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3580  szContent_MD5, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3581  szContent_Range, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3582  szETag, /* HTTP_QUERY_ETAG = 54 */
3583  hostW, /* HTTP_QUERY_HOST = 55 */
3584  szIf_Match, /* HTTP_QUERY_IF_MATCH = 56 */
3585  szIf_None_Match, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3586  szIf_Range, /* HTTP_QUERY_IF_RANGE = 58 */
3587  szIf_Unmodified_Since, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3588  szMax_Forwards, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3589  szProxy_Authorization, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3590  szRange, /* HTTP_QUERY_RANGE = 62 */
3591  szTransfer_Encoding, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3592  szUpgrade, /* HTTP_QUERY_UPGRADE = 64 */
3593  szVary, /* HTTP_QUERY_VARY = 65 */
3594  szVia, /* HTTP_QUERY_VIA = 66 */
3595  szWarning, /* HTTP_QUERY_WARNING = 67 */
3596  szExpect, /* HTTP_QUERY_EXPECT = 68 */
3597  szProxy_Connection, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3598  szUnless_Modified_Since, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3599 };
3600 
3601 /***********************************************************************
3602  * HTTP_HttpQueryInfoW (internal)
3603  */
3606 {
3607  LPHTTPHEADERW lphttpHdr = NULL;
3608  BOOL request_only = !!(dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS);
3609  INT requested_index = lpdwIndex ? *lpdwIndex : 0;
3611  INT index = -1;
3612 
3613  EnterCriticalSection( &request->headers_section );
3614 
3615  /* Find requested header structure */
3616  switch (level)
3617  {
3618  case HTTP_QUERY_CUSTOM:
3619  if (!lpBuffer)
3620  {
3621  LeaveCriticalSection( &request->headers_section );
3622  return ERROR_INVALID_PARAMETER;
3623  }
3624  index = HTTP_GetCustomHeaderIndex(request, lpBuffer, requested_index, request_only);
3625  break;
3627  {
3628  LPWSTR headers;
3629  DWORD len = 0;
3631 
3632  if (request_only)
3634  else
3636  if (!headers)
3637  {
3638  LeaveCriticalSection( &request->headers_section );
3639  return ERROR_OUTOFMEMORY;
3640  }
3641 
3642  len = strlenW(headers) * sizeof(WCHAR);
3643  if (len + sizeof(WCHAR) > *lpdwBufferLength)
3644  {
3645  len += sizeof(WCHAR);
3647  }
3648  else if (lpBuffer)
3649  {
3650  memcpy(lpBuffer, headers, len + sizeof(WCHAR));
3651  TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
3652  res = ERROR_SUCCESS;
3653  }
3654  *lpdwBufferLength = len;
3655 
3656  heap_free(headers);
3657  LeaveCriticalSection( &request->headers_section );
3658  return res;
3659  }
3661  {
3662  LPWSTR headers;
3663  DWORD len;
3664 
3665  if (request_only)
3667  else
3669 
3670  if (!headers)
3671  {
3672  LeaveCriticalSection( &request->headers_section );
3673  return ERROR_OUTOFMEMORY;
3674  }
3675 
3676  len = strlenW(headers) * sizeof(WCHAR);
3677  if (len > *lpdwBufferLength)
3678  {
3679  *lpdwBufferLength = len;
3680  heap_free(headers);
3681  LeaveCriticalSection( &request->headers_section );
3683  }
3684 
3685  if (lpBuffer)
3686  {
3687  DWORD i;
3688 
3689  TRACE("returning data: %s\n", debugstr_wn(headers, len / sizeof(WCHAR)));
3690 
3691  for (i = 0; i < len / sizeof(WCHAR); i++)
3692  {
3693  if (headers[i] == '\n')
3694  headers[i] = 0;
3695  }
3697  }
3698  *lpdwBufferLength = len - sizeof(WCHAR);
3699 
3700  heap_free(headers);
3701  LeaveCriticalSection( &request->headers_section );
3702  return ERROR_SUCCESS;
3703  }
3705  if (request->statusText)
3706  {
3707  DWORD len = strlenW(request->statusText);
3708  if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3709  {
3710  *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3711  LeaveCriticalSection( &request->headers_section );
3713  }
3714  if (lpBuffer)
3715  {
3716  memcpy(lpBuffer, request->statusText, (len + 1) * sizeof(WCHAR));
3717  TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3718  }
3719  *lpdwBufferLength = len * sizeof(WCHAR);
3720  LeaveCriticalSection( &request->headers_section );
3721  return ERROR_SUCCESS;
3722  }
3723  break;
3724  case HTTP_QUERY_VERSION:
3725  if (request->version)
3726  {
3728  if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
3729  {
3730  *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
3731  LeaveCriticalSection( &request->headers_section );
3733  }
3734  if (lpBuffer)
3735  {
3736  memcpy(lpBuffer, request->version, (len + 1) * sizeof(WCHAR));
3737  TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
3738  }
3739  *lpdwBufferLength = len * sizeof(WCHAR);
3740  LeaveCriticalSection( &request->headers_section );
3741  return ERROR_SUCCESS;
3742  }
3743  break;
3746  requested_index,request_only);
3747  break;
3748  case HTTP_QUERY_STATUS_CODE: {
3750 
3751  if(request_only)
3752  {
3753  LeaveCriticalSection( &request->headers_section );
3755  }
3756 
3757  if(requested_index)
3758  break;
3759 
3761  if(*lpdwBufferLength >= sizeof(DWORD))
3762  *(DWORD*)lpBuffer = request->status_code;
3763  else
3765  *lpdwBufferLength = sizeof(DWORD);
3766  }else {
3767  WCHAR buf[12];
3768  DWORD size;
3769  static const WCHAR formatW[] = {'%','u',0};
3770 
3771  size = sprintfW(buf, formatW, request->status_code) * sizeof(WCHAR);
3772 
3773  if(size <= *lpdwBufferLength) {
3774  memcpy(lpBuffer, buf, size+sizeof(WCHAR));
3775  }else {
3776  size += sizeof(WCHAR);
3778  }
3779 
3781  }
3782  LeaveCriticalSection( &request->headers_section );
3783  return res;
3784  }
3785  default:
3787 
3790  requested_index,request_only);
3791  }
3792 
3793  if (index >= 0)
3794  lphttpHdr = &request->custHeaders[index];
3795 
3796  /* Ensure header satisfies requested attributes */
3797  if (!lphttpHdr ||
3799  (~lphttpHdr->wFlags & HDR_ISREQUEST)))
3800  {
3801  LeaveCriticalSection( &request->headers_section );
3803  }
3804 
3805  /* coalesce value to requested type */
3807  {
3808  unsigned long value;
3809 
3810  if (*lpdwBufferLength != sizeof(DWORD))
3811  {
3812  LeaveCriticalSection( &request->headers_section );
3814  }
3815 
3816  errno = 0;
3817  value = strtoulW( lphttpHdr->lpszValue, NULL, 10 );
3818  if (value > UINT_MAX || (value == ULONG_MAX && errno == ERANGE))
3819  {
3820  LeaveCriticalSection( &request->headers_section );
3822  }
3823 
3824  *(DWORD *)lpBuffer = value;
3825  TRACE(" returning number: %u\n", *(DWORD *)lpBuffer);
3826  }
3828  {
3829  time_t tmpTime;
3830  struct tm tmpTM;
3831  SYSTEMTIME *STHook;
3832 
3833  tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
3834 
3835  tmpTM = *gmtime(&tmpTime);
3836  STHook = (SYSTEMTIME *)lpBuffer;
3837  STHook->wDay = tmpTM.tm_mday;
3838  STHook->wHour = tmpTM.tm_hour;
3839  STHook->wMilliseconds = 0;
3840  STHook->wMinute = tmpTM.tm_min;
3841  STHook->wDayOfWeek = tmpTM.tm_wday;
3842  STHook->wMonth = tmpTM.tm_mon + 1;
3843  STHook->wSecond = tmpTM.tm_sec;
3844  STHook->wYear = 1900+tmpTM.tm_year;
3845 
3846  TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3847  STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
3848  STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
3849  }
3850  else if (lphttpHdr->lpszValue)
3851  {
3852  DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
3853 
3854  if (len > *lpdwBufferLength)
3855  {
3856  *lpdwBufferLength = len;
3857  LeaveCriticalSection( &request->headers_section );
3859  }
3860  if (lpBuffer)
3861  {
3862  memcpy(lpBuffer, lphttpHdr->lpszValue, len);
3863  TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
3864  }
3865  *lpdwBufferLength = len - sizeof(WCHAR);
3866  }
3867  if (lpdwIndex) (*lpdwIndex)++;
3868 
3869  LeaveCriticalSection( &request->headers_section );
3870  return ERROR_SUCCESS;
3871 }
3872 
3873 /***********************************************************************
3874  * HttpQueryInfoW (WININET.@)
3875  *
3876  * Queries for information about an HTTP request
3877  *
3878  * RETURNS
3879  * TRUE on success
3880  * FALSE on failure
3881  *
3882  */
3885 {
3887  DWORD res;
3888 
3889  if (TRACE_ON(wininet)) {
3890 #define FE(x) { x, #x }
3891  static const wininet_flag_info query_flags[] = {