2 * Wininet - HTTP Implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2002 CodeWeavers Inc.
6 * Copyright 2002 TransGaming Technologies Inc.
7 * Copyright 2004 Mike McCormack for CodeWeavers
8 * Copyright 2005 Aric Stewart for CodeWeavers
9 * Copyright 2006 Robert Shearman for CodeWeavers
10 * Copyright 2011 Jacek Caban for CodeWeavers
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #include "wine/port.h"
33 #if defined(__MINGW32__) || defined (_MSC_VER)
37 #include <sys/types.h>
38 #ifdef HAVE_SYS_SOCKET_H
39 # include <sys/socket.h>
41 #ifdef HAVE_ARPA_INET_H
42 # include <arpa/inet.h>
61 #define NO_SHLWAPI_STREAM
62 #define NO_SHLWAPI_REG
63 #define NO_SHLWAPI_STRFCNS
64 #define NO_SHLWAPI_GDI
71 #include "wine/debug.h"
72 #include "wine/exception.h"
73 #include "wine/unicode.h"
75 WINE_DEFAULT_DEBUG_CHANNEL(wininet
);
77 static const WCHAR g_szHttp1_0
[] = {'H','T','T','P','/','1','.','0',0};
78 static const WCHAR g_szHttp1_1
[] = {'H','T','T','P','/','1','.','1',0};
79 static const WCHAR szOK
[] = {'O','K',0};
80 static const WCHAR szDefaultHeader
[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
81 static const WCHAR hostW
[] = { 'H','o','s','t',0 };
82 static const WCHAR szAuthorization
[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
83 static const WCHAR szProxy_Authorization
[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
84 static const WCHAR szStatus
[] = { 'S','t','a','t','u','s',0 };
85 static const WCHAR szKeepAlive
[] = {'K','e','e','p','-','A','l','i','v','e',0};
86 static const WCHAR szGET
[] = { 'G','E','T', 0 };
87 static const WCHAR szHEAD
[] = { 'H','E','A','D', 0 };
88 static const WCHAR szCrLf
[] = {'\r','\n', 0};
90 static const WCHAR szAccept
[] = { 'A','c','c','e','p','t',0 };
91 static const WCHAR szAccept_Charset
[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
92 static const WCHAR szAccept_Encoding
[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
93 static const WCHAR szAccept_Language
[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
94 static const WCHAR szAccept_Ranges
[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
95 static const WCHAR szAge
[] = { 'A','g','e',0 };
96 static const WCHAR szAllow
[] = { 'A','l','l','o','w',0 };
97 static const WCHAR szCache_Control
[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
98 static const WCHAR szConnection
[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
99 static const WCHAR szContent_Base
[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
100 static const WCHAR szContent_Disposition
[] = { 'C','o','n','t','e','n','t','-','D','i','s','p','o','s','i','t','i','o','n',0 };
101 static const WCHAR szContent_Encoding
[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
102 static const WCHAR szContent_ID
[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
103 static const WCHAR szContent_Language
[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
104 static const WCHAR szContent_Length
[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
105 static const WCHAR szContent_Location
[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
106 static const WCHAR szContent_MD5
[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
107 static const WCHAR szContent_Range
[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
108 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 };
109 static const WCHAR szContent_Type
[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
110 static const WCHAR szCookie
[] = { 'C','o','o','k','i','e',0 };
111 static const WCHAR szDate
[] = { 'D','a','t','e',0 };
112 static const WCHAR szFrom
[] = { 'F','r','o','m',0 };
113 static const WCHAR szETag
[] = { 'E','T','a','g',0 };
114 static const WCHAR szExpect
[] = { 'E','x','p','e','c','t',0 };
115 static const WCHAR szExpires
[] = { 'E','x','p','i','r','e','s',0 };
116 static const WCHAR szIf_Match
[] = { 'I','f','-','M','a','t','c','h',0 };
117 static const WCHAR szIf_Modified_Since
[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
118 static const WCHAR szIf_None_Match
[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
119 static const WCHAR szIf_Range
[] = { 'I','f','-','R','a','n','g','e',0 };
120 static const WCHAR szIf_Unmodified_Since
[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
121 static const WCHAR szLast_Modified
[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
122 static const WCHAR szLocation
[] = { 'L','o','c','a','t','i','o','n',0 };
123 static const WCHAR szMax_Forwards
[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
124 static const WCHAR szMime_Version
[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
125 static const WCHAR szPragma
[] = { 'P','r','a','g','m','a',0 };
126 static const WCHAR szProxy_Authenticate
[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
127 static const WCHAR szProxy_Connection
[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
128 static const WCHAR szPublic
[] = { 'P','u','b','l','i','c',0 };
129 static const WCHAR szRange
[] = { 'R','a','n','g','e',0 };
130 static const WCHAR szReferer
[] = { 'R','e','f','e','r','e','r',0 };
131 static const WCHAR szRetry_After
[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
132 static const WCHAR szServer
[] = { 'S','e','r','v','e','r',0 };
133 static const WCHAR szSet_Cookie
[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
134 static const WCHAR szTransfer_Encoding
[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
135 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 };
136 static const WCHAR szUpgrade
[] = { 'U','p','g','r','a','d','e',0 };
137 static const WCHAR szURI
[] = { 'U','R','I',0 };
138 static const WCHAR szUser_Agent
[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
139 static const WCHAR szVary
[] = { 'V','a','r','y',0 };
140 static const WCHAR szVia
[] = { 'V','i','a',0 };
141 static const WCHAR szWarning
[] = { 'W','a','r','n','i','n','g',0 };
142 static const WCHAR szWWW_Authenticate
[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
144 #define HTTP_REFERER szReferer
145 #define HTTP_ACCEPT szAccept
146 #define HTTP_USERAGENT szUser_Agent
148 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
149 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
150 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
151 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
152 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
153 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
154 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
156 #define COLLECT_TIME 60000
158 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
169 unsigned int auth_data_len
;
170 BOOL finished
; /* finished authenticating */
174 typedef struct _basicAuthorizationData
181 UINT authorizationLen
;
182 } basicAuthorizationData
;
184 typedef struct _authorizationData
198 static struct list basicAuthorizationCache
= LIST_INIT(basicAuthorizationCache
);
199 static struct list authorizationCache
= LIST_INIT(authorizationCache
);
201 static CRITICAL_SECTION authcache_cs
;
202 static CRITICAL_SECTION_DEBUG critsect_debug
=
205 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
206 0, 0, { (DWORD_PTR
)(__FILE__
": authcache_cs") }
208 static CRITICAL_SECTION authcache_cs
= { &critsect_debug
, -1, 0, 0, 0, 0 };
210 static BOOL
HTTP_GetResponseHeaders(http_request_t
*req
, BOOL clear
);
211 static DWORD
HTTP_ProcessHeader(http_request_t
*req
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
);
212 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
);
213 static DWORD
HTTP_InsertCustomHeader(http_request_t
*req
, LPHTTPHEADERW lpHdr
);
214 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*req
, LPCWSTR lpszField
, INT index
, BOOL Request
);
215 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*req
, DWORD index
);
216 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
);
217 static DWORD
HTTP_HttpQueryInfoW(http_request_t
*, DWORD
, LPVOID
, LPDWORD
, LPDWORD
);
218 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*req
, LPCWSTR lpszUrl
);
219 static UINT
HTTP_DecodeBase64(LPCWSTR base64
, LPSTR bin
);
220 static BOOL
HTTP_VerifyValidHeader(http_request_t
*req
, LPCWSTR field
);
221 static BOOL
drain_content(http_request_t
*,BOOL
);
223 static CRITICAL_SECTION connection_pool_cs
;
224 static CRITICAL_SECTION_DEBUG connection_pool_debug
=
226 0, 0, &connection_pool_cs
,
227 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
228 0, 0, { (DWORD_PTR
)(__FILE__
": connection_pool_cs") }
230 static CRITICAL_SECTION connection_pool_cs
= { &connection_pool_debug
, -1, 0, 0, 0, 0 };
232 static struct list connection_pool
= LIST_INIT(connection_pool
);
233 static BOOL collector_running
;
235 void server_addref(server_t
*server
)
237 InterlockedIncrement(&server
->ref
);
240 void server_release(server_t
*server
)
242 if(InterlockedDecrement(&server
->ref
))
245 list_remove(&server
->entry
);
247 if(server
->cert_chain
)
248 CertFreeCertificateChain(server
->cert_chain
);
249 heap_free(server
->name
);
250 heap_free(server
->scheme_host_port
);
254 static BOOL
process_host_port(server_t
*server
)
260 static const WCHAR httpW
[] = {'h','t','t','p',0};
261 static const WCHAR httpsW
[] = {'h','t','t','p','s',0};
262 static const WCHAR formatW
[] = {'%','s',':','/','/','%','s',':','%','u',0};
264 name_len
= strlenW(server
->name
);
265 buf
= heap_alloc((name_len
+ 10 /* strlen("://:<port>") */)*sizeof(WCHAR
) + sizeof(httpsW
));
269 sprintfW(buf
, formatW
, server
->is_https
? httpsW
: httpW
, server
->name
, server
->port
);
270 server
->scheme_host_port
= buf
;
272 server
->host_port
= server
->scheme_host_port
+ 7 /* strlen("http://") */;
276 default_port
= server
->port
== (server
->is_https
? INTERNET_DEFAULT_HTTPS_PORT
: INTERNET_DEFAULT_HTTP_PORT
);
277 server
->canon_host_port
= default_port
? server
->name
: server
->host_port
;
281 server_t
*get_server(const WCHAR
*name
, INTERNET_PORT port
, BOOL is_https
, BOOL do_create
)
283 server_t
*iter
, *server
= NULL
;
285 if(port
== INTERNET_INVALID_PORT_NUMBER
)
286 port
= INTERNET_DEFAULT_HTTP_PORT
;
288 EnterCriticalSection(&connection_pool_cs
);
290 LIST_FOR_EACH_ENTRY(iter
, &connection_pool
, server_t
, entry
) {
291 if(iter
->port
== port
&& !strcmpW(iter
->name
, name
) && iter
->is_https
== is_https
) {
293 server_addref(server
);
298 if(!server
&& do_create
) {
299 server
= heap_alloc_zero(sizeof(*server
));
301 server
->ref
= 2; /* list reference and return */
303 server
->is_https
= is_https
;
304 list_init(&server
->conn_pool
);
305 server
->name
= heap_strdupW(name
);
306 if(server
->name
&& process_host_port(server
)) {
307 list_add_head(&connection_pool
, &server
->entry
);
315 LeaveCriticalSection(&connection_pool_cs
);
320 BOOL
collect_connections(collect_type_t collect_type
)
322 netconn_t
*netconn
, *netconn_safe
;
323 server_t
*server
, *server_safe
;
324 BOOL remaining
= FALSE
;
327 now
= GetTickCount64();
329 LIST_FOR_EACH_ENTRY_SAFE(server
, server_safe
, &connection_pool
, server_t
, entry
) {
330 LIST_FOR_EACH_ENTRY_SAFE(netconn
, netconn_safe
, &server
->conn_pool
, netconn_t
, pool_entry
) {
331 if(collect_type
> COLLECT_TIMEOUT
|| netconn
->keep_until
< now
) {
332 TRACE("freeing %p\n", netconn
);
333 list_remove(&netconn
->pool_entry
);
334 free_netconn(netconn
);
340 if(collect_type
== COLLECT_CLEANUP
) {
341 list_remove(&server
->entry
);
342 list_init(&server
->entry
);
343 server_release(server
);
350 static DWORD WINAPI
collect_connections_proc(void *arg
)
352 BOOL remaining_conns
;
355 /* FIXME: Use more sophisticated method */
358 EnterCriticalSection(&connection_pool_cs
);
360 remaining_conns
= collect_connections(COLLECT_TIMEOUT
);
362 collector_running
= FALSE
;
364 LeaveCriticalSection(&connection_pool_cs
);
365 }while(remaining_conns
);
367 FreeLibraryAndExitThread(WININET_hModule
, 0);
370 static LPHTTPHEADERW
HTTP_GetHeader(http_request_t
*req
, LPCWSTR head
)
373 HeaderIndex
= HTTP_GetCustomHeaderIndex(req
, head
, 0, TRUE
);
374 if (HeaderIndex
== -1)
377 return &req
->custHeaders
[HeaderIndex
];
386 struct data_stream_vtbl_t
{
387 DWORD (*get_avail_data
)(data_stream_t
*,http_request_t
*);
388 BOOL (*end_of_data
)(data_stream_t
*,http_request_t
*);
389 DWORD (*read
)(data_stream_t
*,http_request_t
*,BYTE
*,DWORD
,DWORD
*,read_mode_t
);
390 BOOL (*drain_content
)(data_stream_t
*,http_request_t
*);
391 void (*destroy
)(data_stream_t
*);
395 data_stream_t data_stream
;
397 BYTE buf
[READ_BUFFER_SIZE
];
403 static inline void destroy_data_stream(data_stream_t
*stream
)
405 stream
->vtbl
->destroy(stream
);
408 static void reset_data_stream(http_request_t
*req
)
410 destroy_data_stream(req
->data_stream
);
411 req
->data_stream
= &req
->netconn_stream
.data_stream
;
412 req
->read_pos
= req
->read_size
= req
->netconn_stream
.content_read
= 0;
413 req
->read_chunked
= req
->read_gzip
= FALSE
;
419 data_stream_t stream
;
420 data_stream_t
*parent_stream
;
422 BYTE buf
[READ_BUFFER_SIZE
];
428 static DWORD
gzip_get_avail_data(data_stream_t
*stream
, http_request_t
*req
)
430 /* Allow reading only from read buffer */
434 static BOOL
gzip_end_of_data(data_stream_t
*stream
, http_request_t
*req
)
436 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
437 return gzip_stream
->end_of_data
;
440 static DWORD
gzip_read(data_stream_t
*stream
, http_request_t
*req
, BYTE
*buf
, DWORD size
,
441 DWORD
*read
, read_mode_t read_mode
)
443 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
444 z_stream
*zstream
= &gzip_stream
->zstream
;
445 DWORD current_read
, ret_read
= 0;
448 DWORD res
= ERROR_SUCCESS
;
450 while(size
&& !gzip_stream
->end_of_data
) {
451 end
= gzip_stream
->parent_stream
->vtbl
->end_of_data(gzip_stream
->parent_stream
, req
);
453 if(gzip_stream
->buf_size
<= 64 && !end
) {
454 if(gzip_stream
->buf_pos
) {
455 if(gzip_stream
->buf_size
)
456 memmove(gzip_stream
->buf
, gzip_stream
->buf
+gzip_stream
->buf_pos
, gzip_stream
->buf_size
);
457 gzip_stream
->buf_pos
= 0;
459 res
= gzip_stream
->parent_stream
->vtbl
->read(gzip_stream
->parent_stream
, req
, gzip_stream
->buf
+gzip_stream
->buf_size
,
460 sizeof(gzip_stream
->buf
)-gzip_stream
->buf_size
, ¤t_read
, read_mode
);
461 gzip_stream
->buf_size
+= current_read
;
462 if(res
!= ERROR_SUCCESS
)
464 end
= gzip_stream
->parent_stream
->vtbl
->end_of_data(gzip_stream
->parent_stream
, req
);
465 if(!current_read
&& !end
) {
466 if(read_mode
!= READMODE_NOBLOCK
) {
467 WARN("unexpected end of data\n");
468 gzip_stream
->end_of_data
= TRUE
;
472 if(gzip_stream
->buf_size
<= 64 && !end
)
476 zstream
->next_in
= gzip_stream
->buf
+gzip_stream
->buf_pos
;
477 zstream
->avail_in
= gzip_stream
->buf_size
-(end
? 0 : 64);
478 zstream
->next_out
= buf
+ret_read
;
479 zstream
->avail_out
= size
;
480 zres
= inflate(&gzip_stream
->zstream
, 0);
481 current_read
= size
- zstream
->avail_out
;
482 size
-= current_read
;
483 ret_read
+= current_read
;
484 gzip_stream
->buf_size
-= zstream
->next_in
- (gzip_stream
->buf
+gzip_stream
->buf_pos
);
485 gzip_stream
->buf_pos
= zstream
->next_in
-gzip_stream
->buf
;
486 if(zres
== Z_STREAM_END
) {
487 TRACE("end of data\n");
488 gzip_stream
->end_of_data
= TRUE
;
490 }else if(zres
!= Z_OK
) {
491 WARN("inflate failed %d: %s\n", zres
, debugstr_a(zstream
->msg
));
493 res
= ERROR_INTERNET_DECODING_FAILED
;
497 if(ret_read
&& read_mode
== READMODE_ASYNC
)
498 read_mode
= READMODE_NOBLOCK
;
501 TRACE("read %u bytes\n", ret_read
);
506 static BOOL
gzip_drain_content(data_stream_t
*stream
, http_request_t
*req
)
508 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
509 return gzip_stream
->parent_stream
->vtbl
->drain_content(gzip_stream
->parent_stream
, req
);
512 static void gzip_destroy(data_stream_t
*stream
)
514 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
516 destroy_data_stream(gzip_stream
->parent_stream
);
518 if(!gzip_stream
->end_of_data
)
519 inflateEnd(&gzip_stream
->zstream
);
520 heap_free(gzip_stream
);
523 static const data_stream_vtbl_t gzip_stream_vtbl
= {
531 static voidpf
wininet_zalloc(voidpf opaque
, uInt items
, uInt size
)
533 return heap_alloc(items
*size
);
536 static void wininet_zfree(voidpf opaque
, voidpf address
)
541 static DWORD
init_gzip_stream(http_request_t
*req
)
543 gzip_stream_t
*gzip_stream
;
546 gzip_stream
= heap_alloc_zero(sizeof(gzip_stream_t
));
548 return ERROR_OUTOFMEMORY
;
550 gzip_stream
->stream
.vtbl
= &gzip_stream_vtbl
;
551 gzip_stream
->zstream
.zalloc
= wininet_zalloc
;
552 gzip_stream
->zstream
.zfree
= wininet_zfree
;
554 zres
= inflateInit2(&gzip_stream
->zstream
, 0x1f);
556 ERR("inflateInit failed: %d\n", zres
);
557 heap_free(gzip_stream
);
558 return ERROR_OUTOFMEMORY
;
561 index
= HTTP_GetCustomHeaderIndex(req
, szContent_Length
, 0, FALSE
);
563 HTTP_DeleteCustomHeader(req
, index
);
566 memcpy(gzip_stream
->buf
, req
->read_buf
+req
->read_pos
, req
->read_size
);
567 gzip_stream
->buf_size
= req
->read_size
;
568 req
->read_pos
= req
->read_size
= 0;
571 req
->read_gzip
= TRUE
;
572 gzip_stream
->parent_stream
= req
->data_stream
;
573 req
->data_stream
= &gzip_stream
->stream
;
574 return ERROR_SUCCESS
;
579 static DWORD
init_gzip_stream(http_request_t
*req
)
581 ERR("gzip stream not supported, missing zlib.\n");
582 return ERROR_SUCCESS
;
587 /***********************************************************************
588 * HTTP_Tokenize (internal)
590 * Tokenize a string, allocating memory for the tokens.
592 static LPWSTR
* HTTP_Tokenize(LPCWSTR string
, LPCWSTR token_string
)
594 LPWSTR
* token_array
;
601 /* empty string has no tokens */
605 for (i
= 0; string
[i
]; i
++)
607 if (!strncmpW(string
+i
, token_string
, strlenW(token_string
)))
611 /* we want to skip over separators, but not the null terminator */
612 for (j
= 0; j
< strlenW(token_string
) - 1; j
++)
620 /* add 1 for terminating NULL */
621 token_array
= heap_alloc((tokens
+1) * sizeof(*token_array
));
622 token_array
[tokens
] = NULL
;
625 for (i
= 0; i
< tokens
; i
++)
628 next_token
= strstrW(string
, token_string
);
629 if (!next_token
) next_token
= string
+strlenW(string
);
630 len
= next_token
- string
;
631 token_array
[i
] = heap_alloc((len
+1)*sizeof(WCHAR
));
632 memcpy(token_array
[i
], string
, len
*sizeof(WCHAR
));
633 token_array
[i
][len
] = '\0';
634 string
= next_token
+strlenW(token_string
);
639 /***********************************************************************
640 * HTTP_FreeTokens (internal)
642 * Frees memory returned from HTTP_Tokenize.
644 static void HTTP_FreeTokens(LPWSTR
* token_array
)
647 for (i
= 0; token_array
[i
]; i
++) heap_free(token_array
[i
]);
648 heap_free(token_array
);
651 static void HTTP_FixURL(http_request_t
*request
)
653 static const WCHAR szSlash
[] = { '/',0 };
654 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/', 0 };
656 /* If we don't have a path we set it to root */
657 if (NULL
== request
->path
)
658 request
->path
= heap_strdupW(szSlash
);
659 else /* remove \r and \n*/
661 int nLen
= strlenW(request
->path
);
662 while ((nLen
>0 ) && ((request
->path
[nLen
-1] == '\r')||(request
->path
[nLen
-1] == '\n')))
665 request
->path
[nLen
]='\0';
667 /* Replace '\' with '/' */
670 if (request
->path
[nLen
] == '\\') request
->path
[nLen
]='/';
674 if(CSTR_EQUAL
!= CompareStringW( LOCALE_INVARIANT
, NORM_IGNORECASE
,
675 request
->path
, strlenW(request
->path
), szHttp
, strlenW(szHttp
) )
676 && request
->path
[0] != '/') /* not an absolute path ?? --> fix it !! */
678 WCHAR
*fixurl
= heap_alloc((strlenW(request
->path
) + 2)*sizeof(WCHAR
));
680 strcpyW(fixurl
+ 1, request
->path
);
681 heap_free( request
->path
);
682 request
->path
= fixurl
;
686 static LPWSTR
HTTP_BuildHeaderRequestString( http_request_t
*request
, LPCWSTR verb
, LPCWSTR path
, LPCWSTR version
)
688 LPWSTR requestString
;
694 static const WCHAR szSpace
[] = { ' ',0 };
695 static const WCHAR szColon
[] = { ':',' ',0 };
696 static const WCHAR sztwocrlf
[] = {'\r','\n','\r','\n', 0};
698 /* allocate space for an array of all the string pointers to be added */
699 len
= (request
->nCustHeaders
)*4 + 10;
700 req
= heap_alloc(len
*sizeof(LPCWSTR
));
702 /* add the verb, path and HTTP version string */
710 /* Append custom request headers */
711 for (i
= 0; i
< request
->nCustHeaders
; i
++)
713 if (request
->custHeaders
[i
].wFlags
& HDR_ISREQUEST
)
716 req
[n
++] = request
->custHeaders
[i
].lpszField
;
718 req
[n
++] = request
->custHeaders
[i
].lpszValue
;
720 TRACE("Adding custom header %s (%s)\n",
721 debugstr_w(request
->custHeaders
[i
].lpszField
),
722 debugstr_w(request
->custHeaders
[i
].lpszValue
));
727 ERR("oops. buffer overrun\n");
730 requestString
= HTTP_build_req( req
, 4 );
734 * Set (header) termination string for request
735 * Make sure there's exactly two new lines at the end of the request
737 p
= &requestString
[strlenW(requestString
)-1];
738 while ( (*p
== '\n') || (*p
== '\r') )
740 strcpyW( p
+1, sztwocrlf
);
742 return requestString
;
745 static void HTTP_ProcessCookies( http_request_t
*request
)
749 LPHTTPHEADERW setCookieHeader
;
751 if(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
)
754 while((HeaderIndex
= HTTP_GetCustomHeaderIndex(request
, szSet_Cookie
, numCookies
++, FALSE
)) != -1)
760 setCookieHeader
= &request
->custHeaders
[HeaderIndex
];
762 if (!setCookieHeader
->lpszValue
)
765 host
= HTTP_GetHeader(request
, hostW
);
769 data
= strchrW(setCookieHeader
->lpszValue
, '=');
773 name
= heap_strndupW(setCookieHeader
->lpszValue
, data
-setCookieHeader
->lpszValue
);
778 set_cookie(host
->lpszValue
, request
->path
, name
, data
);
783 static void strip_spaces(LPWSTR start
)
788 while (*str
== ' ' && *str
!= '\0')
792 memmove(start
, str
, sizeof(WCHAR
) * (strlenW(str
) + 1));
794 end
= start
+ strlenW(start
) - 1;
795 while (end
>= start
&& *end
== ' ')
802 static inline BOOL
is_basic_auth_value( LPCWSTR pszAuthValue
, LPWSTR
*pszRealm
)
804 static const WCHAR szBasic
[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
805 static const WCHAR szRealm
[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
807 is_basic
= !strncmpiW(pszAuthValue
, szBasic
, ARRAYSIZE(szBasic
)) &&
808 ((pszAuthValue
[ARRAYSIZE(szBasic
)] == ' ') || !pszAuthValue
[ARRAYSIZE(szBasic
)]);
809 if (is_basic
&& pszRealm
)
812 LPCWSTR ptr
= &pszAuthValue
[ARRAYSIZE(szBasic
)];
816 token
= strchrW(ptr
,'=');
820 while (*realm
== ' ' && *realm
!= '\0')
822 if(!strncmpiW(realm
, szRealm
, ARRAYSIZE(szRealm
)) &&
823 (realm
[ARRAYSIZE(szRealm
)] == ' ' || realm
[ARRAYSIZE(szRealm
)] == '='))
826 while (*token
== ' ' && *token
!= '\0')
830 *pszRealm
= heap_strdupW(token
);
831 strip_spaces(*pszRealm
);
838 static void destroy_authinfo( struct HttpAuthInfo
*authinfo
)
840 if (!authinfo
) return;
842 if (SecIsValidHandle(&authinfo
->ctx
))
843 DeleteSecurityContext(&authinfo
->ctx
);
844 if (SecIsValidHandle(&authinfo
->cred
))
845 FreeCredentialsHandle(&authinfo
->cred
);
847 heap_free(authinfo
->auth_data
);
848 heap_free(authinfo
->scheme
);
852 static UINT
retrieve_cached_basic_authorization(LPWSTR host
, LPWSTR realm
, LPSTR
*auth_data
)
854 basicAuthorizationData
*ad
;
857 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host
),debugstr_w(realm
));
859 EnterCriticalSection(&authcache_cs
);
860 LIST_FOR_EACH_ENTRY(ad
, &basicAuthorizationCache
, basicAuthorizationData
, entry
)
862 if (!strcmpiW(host
,ad
->host
) && !strcmpW(realm
,ad
->realm
))
864 TRACE("Authorization found in cache\n");
865 *auth_data
= heap_alloc(ad
->authorizationLen
);
866 memcpy(*auth_data
,ad
->authorization
,ad
->authorizationLen
);
867 rc
= ad
->authorizationLen
;
871 LeaveCriticalSection(&authcache_cs
);
875 static void cache_basic_authorization(LPWSTR host
, LPWSTR realm
, LPSTR auth_data
, UINT auth_data_len
)
878 basicAuthorizationData
* ad
= NULL
;
880 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host
),debugstr_w(realm
),debugstr_an(auth_data
,auth_data_len
));
882 EnterCriticalSection(&authcache_cs
);
883 LIST_FOR_EACH(cursor
, &basicAuthorizationCache
)
885 basicAuthorizationData
*check
= LIST_ENTRY(cursor
,basicAuthorizationData
,entry
);
886 if (!strcmpiW(host
,check
->host
) && !strcmpW(realm
,check
->realm
))
895 TRACE("Found match in cache, replacing\n");
896 heap_free(ad
->authorization
);
897 ad
->authorization
= heap_alloc(auth_data_len
);
898 memcpy(ad
->authorization
, auth_data
, auth_data_len
);
899 ad
->authorizationLen
= auth_data_len
;
903 ad
= heap_alloc(sizeof(basicAuthorizationData
));
904 ad
->host
= heap_strdupW(host
);
905 ad
->realm
= heap_strdupW(realm
);
906 ad
->authorization
= heap_alloc(auth_data_len
);
907 memcpy(ad
->authorization
, auth_data
, auth_data_len
);
908 ad
->authorizationLen
= auth_data_len
;
909 list_add_head(&basicAuthorizationCache
,&ad
->entry
);
910 TRACE("authorization cached\n");
912 LeaveCriticalSection(&authcache_cs
);
915 static BOOL
retrieve_cached_authorization(LPWSTR host
, LPWSTR scheme
,
916 SEC_WINNT_AUTH_IDENTITY_W
*nt_auth_identity
)
918 authorizationData
*ad
;
920 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host
), debugstr_w(scheme
));
922 EnterCriticalSection(&authcache_cs
);
923 LIST_FOR_EACH_ENTRY(ad
, &authorizationCache
, authorizationData
, entry
) {
924 if(!strcmpiW(host
, ad
->host
) && !strcmpiW(scheme
, ad
->scheme
)) {
925 TRACE("Authorization found in cache\n");
927 nt_auth_identity
->User
= heap_strdupW(ad
->user
);
928 nt_auth_identity
->Password
= heap_strdupW(ad
->password
);
929 nt_auth_identity
->Domain
= heap_alloc(sizeof(WCHAR
)*ad
->domain_len
);
930 if(!nt_auth_identity
->User
|| !nt_auth_identity
->Password
||
931 (!nt_auth_identity
->Domain
&& ad
->domain_len
)) {
932 heap_free(nt_auth_identity
->User
);
933 heap_free(nt_auth_identity
->Password
);
934 heap_free(nt_auth_identity
->Domain
);
938 nt_auth_identity
->Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
939 nt_auth_identity
->UserLength
= ad
->user_len
;
940 nt_auth_identity
->PasswordLength
= ad
->password_len
;
941 memcpy(nt_auth_identity
->Domain
, ad
->domain
, sizeof(WCHAR
)*ad
->domain_len
);
942 nt_auth_identity
->DomainLength
= ad
->domain_len
;
943 LeaveCriticalSection(&authcache_cs
);
947 LeaveCriticalSection(&authcache_cs
);
952 static void cache_authorization(LPWSTR host
, LPWSTR scheme
,
953 SEC_WINNT_AUTH_IDENTITY_W
*nt_auth_identity
)
955 authorizationData
*ad
;
958 TRACE("Caching authorization for %s:%s\n", debugstr_w(host
), debugstr_w(scheme
));
960 EnterCriticalSection(&authcache_cs
);
961 LIST_FOR_EACH_ENTRY(ad
, &authorizationCache
, authorizationData
, entry
)
962 if(!strcmpiW(host
, ad
->host
) && !strcmpiW(scheme
, ad
->scheme
)) {
969 heap_free(ad
->password
);
970 heap_free(ad
->domain
);
972 ad
= heap_alloc(sizeof(authorizationData
));
974 LeaveCriticalSection(&authcache_cs
);
978 ad
->host
= heap_strdupW(host
);
979 ad
->scheme
= heap_strdupW(scheme
);
980 list_add_head(&authorizationCache
, &ad
->entry
);
983 ad
->user
= heap_strndupW(nt_auth_identity
->User
, nt_auth_identity
->UserLength
);
984 ad
->password
= heap_strndupW(nt_auth_identity
->Password
, nt_auth_identity
->PasswordLength
);
985 ad
->domain
= heap_strndupW(nt_auth_identity
->Domain
, nt_auth_identity
->DomainLength
);
986 ad
->user_len
= nt_auth_identity
->UserLength
;
987 ad
->password_len
= nt_auth_identity
->PasswordLength
;
988 ad
->domain_len
= nt_auth_identity
->DomainLength
;
990 if(!ad
->host
|| !ad
->scheme
|| !ad
->user
|| !ad
->password
991 || (nt_auth_identity
->Domain
&& !ad
->domain
)) {
993 heap_free(ad
->scheme
);
995 heap_free(ad
->password
);
996 heap_free(ad
->domain
);
997 list_remove(&ad
->entry
);
1001 LeaveCriticalSection(&authcache_cs
);
1004 static BOOL
HTTP_DoAuthorization( http_request_t
*request
, LPCWSTR pszAuthValue
,
1005 struct HttpAuthInfo
**ppAuthInfo
,
1006 LPWSTR domain_and_username
, LPWSTR password
,
1009 SECURITY_STATUS sec_status
;
1010 struct HttpAuthInfo
*pAuthInfo
= *ppAuthInfo
;
1012 LPWSTR szRealm
= NULL
;
1014 TRACE("%s\n", debugstr_w(pszAuthValue
));
1021 pAuthInfo
= heap_alloc(sizeof(*pAuthInfo
));
1025 SecInvalidateHandle(&pAuthInfo
->cred
);
1026 SecInvalidateHandle(&pAuthInfo
->ctx
);
1027 memset(&pAuthInfo
->exp
, 0, sizeof(pAuthInfo
->exp
));
1028 pAuthInfo
->attr
= 0;
1029 pAuthInfo
->auth_data
= NULL
;
1030 pAuthInfo
->auth_data_len
= 0;
1031 pAuthInfo
->finished
= FALSE
;
1033 if (is_basic_auth_value(pszAuthValue
,NULL
))
1035 static const WCHAR szBasic
[] = {'B','a','s','i','c',0};
1036 pAuthInfo
->scheme
= heap_strdupW(szBasic
);
1037 if (!pAuthInfo
->scheme
)
1039 heap_free(pAuthInfo
);
1046 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity
;
1048 pAuthInfo
->scheme
= heap_strdupW(pszAuthValue
);
1049 if (!pAuthInfo
->scheme
)
1051 heap_free(pAuthInfo
);
1055 if (domain_and_username
)
1057 WCHAR
*user
= strchrW(domain_and_username
, '\\');
1058 WCHAR
*domain
= domain_and_username
;
1060 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1062 pAuthData
= &nt_auth_identity
;
1067 user
= domain_and_username
;
1071 nt_auth_identity
.Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
1072 nt_auth_identity
.User
= user
;
1073 nt_auth_identity
.UserLength
= strlenW(nt_auth_identity
.User
);
1074 nt_auth_identity
.Domain
= domain
;
1075 nt_auth_identity
.DomainLength
= domain
? user
- domain
- 1 : 0;
1076 nt_auth_identity
.Password
= password
;
1077 nt_auth_identity
.PasswordLength
= strlenW(nt_auth_identity
.Password
);
1079 cache_authorization(host
, pAuthInfo
->scheme
, &nt_auth_identity
);
1081 else if(retrieve_cached_authorization(host
, pAuthInfo
->scheme
, &nt_auth_identity
))
1082 pAuthData
= &nt_auth_identity
;
1084 /* use default credentials */
1087 sec_status
= AcquireCredentialsHandleW(NULL
, pAuthInfo
->scheme
,
1088 SECPKG_CRED_OUTBOUND
, NULL
,
1090 NULL
, &pAuthInfo
->cred
,
1093 if(pAuthData
&& !domain_and_username
) {
1094 heap_free(nt_auth_identity
.User
);
1095 heap_free(nt_auth_identity
.Domain
);
1096 heap_free(nt_auth_identity
.Password
);
1099 if (sec_status
== SEC_E_OK
)
1101 PSecPkgInfoW sec_pkg_info
;
1102 sec_status
= QuerySecurityPackageInfoW(pAuthInfo
->scheme
, &sec_pkg_info
);
1103 if (sec_status
== SEC_E_OK
)
1105 pAuthInfo
->max_token
= sec_pkg_info
->cbMaxToken
;
1106 FreeContextBuffer(sec_pkg_info
);
1109 if (sec_status
!= SEC_E_OK
)
1111 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1112 debugstr_w(pAuthInfo
->scheme
), sec_status
);
1113 heap_free(pAuthInfo
->scheme
);
1114 heap_free(pAuthInfo
);
1118 *ppAuthInfo
= pAuthInfo
;
1120 else if (pAuthInfo
->finished
)
1123 if ((strlenW(pszAuthValue
) < strlenW(pAuthInfo
->scheme
)) ||
1124 strncmpiW(pszAuthValue
, pAuthInfo
->scheme
, strlenW(pAuthInfo
->scheme
)))
1126 ERR("authentication scheme changed from %s to %s\n",
1127 debugstr_w(pAuthInfo
->scheme
), debugstr_w(pszAuthValue
));
1131 if (is_basic_auth_value(pszAuthValue
,&szRealm
))
1135 char *auth_data
= NULL
;
1136 UINT auth_data_len
= 0;
1138 TRACE("basic authentication realm %s\n",debugstr_w(szRealm
));
1140 if (!domain_and_username
)
1142 if (host
&& szRealm
)
1143 auth_data_len
= retrieve_cached_basic_authorization(host
, szRealm
,&auth_data
);
1144 if (auth_data_len
== 0)
1152 userlen
= WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, lstrlenW(domain_and_username
), NULL
, 0, NULL
, NULL
);
1153 passlen
= WideCharToMultiByte(CP_UTF8
, 0, password
, lstrlenW(password
), NULL
, 0, NULL
, NULL
);
1155 /* length includes a nul terminator, which will be re-used for the ':' */
1156 auth_data
= heap_alloc(userlen
+ 1 + passlen
);
1163 WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, -1, auth_data
, userlen
, NULL
, NULL
);
1164 auth_data
[userlen
] = ':';
1165 WideCharToMultiByte(CP_UTF8
, 0, password
, -1, &auth_data
[userlen
+1], passlen
, NULL
, NULL
);
1166 auth_data_len
= userlen
+ 1 + passlen
;
1167 if (host
&& szRealm
)
1168 cache_basic_authorization(host
, szRealm
, auth_data
, auth_data_len
);
1171 pAuthInfo
->auth_data
= auth_data
;
1172 pAuthInfo
->auth_data_len
= auth_data_len
;
1173 pAuthInfo
->finished
= TRUE
;
1179 LPCWSTR pszAuthData
;
1180 SecBufferDesc out_desc
, in_desc
;
1182 unsigned char *buffer
;
1183 ULONG context_req
= ISC_REQ_CONNECTION
| ISC_REQ_USE_DCE_STYLE
|
1184 ISC_REQ_MUTUAL_AUTH
| ISC_REQ_DELEGATE
;
1186 in
.BufferType
= SECBUFFER_TOKEN
;
1190 in_desc
.ulVersion
= 0;
1191 in_desc
.cBuffers
= 1;
1192 in_desc
.pBuffers
= &in
;
1194 pszAuthData
= pszAuthValue
+ strlenW(pAuthInfo
->scheme
);
1195 if (*pszAuthData
== ' ')
1198 in
.cbBuffer
= HTTP_DecodeBase64(pszAuthData
, NULL
);
1199 in
.pvBuffer
= heap_alloc(in
.cbBuffer
);
1200 HTTP_DecodeBase64(pszAuthData
, in
.pvBuffer
);
1203 buffer
= heap_alloc(pAuthInfo
->max_token
);
1205 out
.BufferType
= SECBUFFER_TOKEN
;
1206 out
.cbBuffer
= pAuthInfo
->max_token
;
1207 out
.pvBuffer
= buffer
;
1209 out_desc
.ulVersion
= 0;
1210 out_desc
.cBuffers
= 1;
1211 out_desc
.pBuffers
= &out
;
1213 sec_status
= InitializeSecurityContextW(first
? &pAuthInfo
->cred
: NULL
,
1214 first
? NULL
: &pAuthInfo
->ctx
,
1215 first
? request
->server
->name
: NULL
,
1216 context_req
, 0, SECURITY_NETWORK_DREP
,
1217 in
.pvBuffer
? &in_desc
: NULL
,
1218 0, &pAuthInfo
->ctx
, &out_desc
,
1219 &pAuthInfo
->attr
, &pAuthInfo
->exp
);
1220 if (sec_status
== SEC_E_OK
)
1222 pAuthInfo
->finished
= TRUE
;
1223 pAuthInfo
->auth_data
= out
.pvBuffer
;
1224 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
1225 TRACE("sending last auth packet\n");
1227 else if (sec_status
== SEC_I_CONTINUE_NEEDED
)
1229 pAuthInfo
->auth_data
= out
.pvBuffer
;
1230 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
1231 TRACE("sending next auth packet\n");
1235 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status
);
1236 heap_free(out
.pvBuffer
);
1237 destroy_authinfo(pAuthInfo
);
1246 /***********************************************************************
1247 * HTTP_HttpAddRequestHeadersW (internal)
1249 static DWORD
HTTP_HttpAddRequestHeadersW(http_request_t
*request
,
1250 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1255 DWORD len
, res
= ERROR_HTTP_INVALID_HEADER
;
1257 TRACE("copying header: %s\n", debugstr_wn(lpszHeader
, dwHeaderLength
));
1259 if( dwHeaderLength
== ~0U )
1260 len
= strlenW(lpszHeader
);
1262 len
= dwHeaderLength
;
1263 buffer
= heap_alloc(sizeof(WCHAR
)*(len
+1));
1264 lstrcpynW( buffer
, lpszHeader
, len
+ 1);
1270 LPWSTR
* pFieldAndValue
;
1272 lpszEnd
= lpszStart
;
1274 while (*lpszEnd
!= '\0')
1276 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
1281 if (*lpszStart
== '\0')
1284 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
1287 lpszEnd
++; /* Jump over newline */
1289 TRACE("interpreting header %s\n", debugstr_w(lpszStart
));
1290 if (*lpszStart
== '\0')
1292 /* Skip 0-length headers */
1293 lpszStart
= lpszEnd
;
1294 res
= ERROR_SUCCESS
;
1297 pFieldAndValue
= HTTP_InterpretHttpHeader(lpszStart
);
1300 res
= HTTP_VerifyValidHeader(request
, pFieldAndValue
[0]);
1301 if (res
== ERROR_SUCCESS
)
1302 res
= HTTP_ProcessHeader(request
, pFieldAndValue
[0],
1303 pFieldAndValue
[1], dwModifier
| HTTP_ADDHDR_FLAG_REQ
);
1304 HTTP_FreeTokens(pFieldAndValue
);
1307 lpszStart
= lpszEnd
;
1308 } while (res
== ERROR_SUCCESS
);
1314 /***********************************************************************
1315 * HttpAddRequestHeadersW (WININET.@)
1317 * Adds one or more HTTP header to the request handler
1320 * On Windows if dwHeaderLength includes the trailing '\0', then
1321 * HttpAddRequestHeadersW() adds it too. However this results in an
1322 * invalid HTTP header which is rejected by some servers so we probably
1323 * don't need to match Windows on that point.
1330 BOOL WINAPI
HttpAddRequestHeadersW(HINTERNET hHttpRequest
,
1331 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1333 http_request_t
*request
;
1334 DWORD res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
1336 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_wn(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
1341 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
1342 if (request
&& request
->hdr
.htype
== WH_HHTTPREQ
)
1343 res
= HTTP_HttpAddRequestHeadersW( request
, lpszHeader
, dwHeaderLength
, dwModifier
);
1345 WININET_Release( &request
->hdr
);
1347 if(res
!= ERROR_SUCCESS
)
1349 return res
== ERROR_SUCCESS
;
1352 /***********************************************************************
1353 * HttpAddRequestHeadersA (WININET.@)
1355 * Adds one or more HTTP header to the request handler
1362 BOOL WINAPI
HttpAddRequestHeadersA(HINTERNET hHttpRequest
,
1363 LPCSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1369 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_an(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
1371 len
= MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, NULL
, 0 );
1372 hdr
= heap_alloc(len
*sizeof(WCHAR
));
1373 MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, hdr
, len
);
1374 if( dwHeaderLength
!= ~0U )
1375 dwHeaderLength
= len
;
1377 r
= HttpAddRequestHeadersW( hHttpRequest
, hdr
, dwHeaderLength
, dwModifier
);
1383 static void free_accept_types( WCHAR
**accept_types
)
1385 WCHAR
*ptr
, **types
= accept_types
;
1388 while ((ptr
= *types
))
1393 heap_free( accept_types
);
1396 static WCHAR
**convert_accept_types( const char **accept_types
)
1399 const char **types
= accept_types
;
1401 BOOL invalid_pointer
= FALSE
;
1403 if (!types
) return NULL
;
1409 /* find out how many there are */
1410 if (*types
&& **types
)
1412 TRACE("accept type: %s\n", debugstr_a(*types
));
1418 WARN("invalid accept type pointer\n");
1419 invalid_pointer
= TRUE
;
1424 if (invalid_pointer
) return NULL
;
1425 if (!(typesW
= heap_alloc( sizeof(WCHAR
*) * (count
+ 1) ))) return NULL
;
1427 types
= accept_types
;
1430 if (*types
&& **types
) typesW
[count
++] = heap_strdupAtoW( *types
);
1433 typesW
[count
] = NULL
;
1437 /***********************************************************************
1438 * HttpOpenRequestA (WININET.@)
1440 * Open a HTTP request handle
1443 * HINTERNET a HTTP request handle on success
1447 HINTERNET WINAPI
HttpOpenRequestA(HINTERNET hHttpSession
,
1448 LPCSTR lpszVerb
, LPCSTR lpszObjectName
, LPCSTR lpszVersion
,
1449 LPCSTR lpszReferrer
, LPCSTR
*lpszAcceptTypes
,
1450 DWORD dwFlags
, DWORD_PTR dwContext
)
1452 LPWSTR szVerb
= NULL
, szObjectName
= NULL
;
1453 LPWSTR szVersion
= NULL
, szReferrer
= NULL
, *szAcceptTypes
= NULL
;
1454 HINTERNET rc
= FALSE
;
1456 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
1457 debugstr_a(lpszVerb
), debugstr_a(lpszObjectName
),
1458 debugstr_a(lpszVersion
), debugstr_a(lpszReferrer
), lpszAcceptTypes
,
1459 dwFlags
, dwContext
);
1463 szVerb
= heap_strdupAtoW(lpszVerb
);
1470 szObjectName
= heap_strdupAtoW(lpszObjectName
);
1471 if ( !szObjectName
)
1477 szVersion
= heap_strdupAtoW(lpszVersion
);
1484 szReferrer
= heap_strdupAtoW(lpszReferrer
);
1489 szAcceptTypes
= convert_accept_types( lpszAcceptTypes
);
1490 rc
= HttpOpenRequestW(hHttpSession
, szVerb
, szObjectName
, szVersion
, szReferrer
,
1491 (const WCHAR
**)szAcceptTypes
, dwFlags
, dwContext
);
1494 free_accept_types(szAcceptTypes
);
1495 heap_free(szReferrer
);
1496 heap_free(szVersion
);
1497 heap_free(szObjectName
);
1502 /***********************************************************************
1505 static UINT
HTTP_EncodeBase64( LPCSTR bin
, unsigned int len
, LPWSTR base64
)
1508 static const CHAR HTTP_Base64Enc
[] =
1509 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1513 /* first 6 bits, all from bin[0] */
1514 base64
[n
++] = HTTP_Base64Enc
[(bin
[0] & 0xfc) >> 2];
1515 x
= (bin
[0] & 3) << 4;
1517 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1520 base64
[n
++] = HTTP_Base64Enc
[x
];
1525 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[1]&0xf0) >> 4 ) ];
1526 x
= ( bin
[1] & 0x0f ) << 2;
1528 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1531 base64
[n
++] = HTTP_Base64Enc
[x
];
1535 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[2]&0xc0 ) >> 6 ) ];
1537 /* last 6 bits, all from bin [2] */
1538 base64
[n
++] = HTTP_Base64Enc
[ bin
[2] & 0x3f ];
1546 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1547 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1548 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1549 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1550 static const signed char HTTP_Base64Dec
[256] =
1552 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1553 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1554 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1555 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1556 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1557 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1558 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1559 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1560 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1561 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1562 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1563 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1564 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1565 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1566 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1567 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1568 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1569 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1570 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1571 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1572 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1573 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1574 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1575 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1576 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1577 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1581 /***********************************************************************
1584 static UINT
HTTP_DecodeBase64( LPCWSTR base64
, LPSTR bin
)
1592 if (base64
[0] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1593 ((in
[0] = HTTP_Base64Dec
[base64
[0]]) == -1) ||
1594 base64
[1] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1595 ((in
[1] = HTTP_Base64Dec
[base64
[1]]) == -1))
1597 WARN("invalid base64: %s\n", debugstr_w(base64
));
1601 bin
[n
] = (unsigned char) (in
[0] << 2 | in
[1] >> 4);
1604 if ((base64
[2] == '=') && (base64
[3] == '='))
1606 if (base64
[2] > ARRAYSIZE(HTTP_Base64Dec
) ||
1607 ((in
[2] = HTTP_Base64Dec
[base64
[2]]) == -1))
1609 WARN("invalid base64: %s\n", debugstr_w(&base64
[2]));
1613 bin
[n
] = (unsigned char) (in
[1] << 4 | in
[2] >> 2);
1616 if (base64
[3] == '=')
1618 if (base64
[3] > ARRAYSIZE(HTTP_Base64Dec
) ||
1619 ((in
[3] = HTTP_Base64Dec
[base64
[3]]) == -1))
1621 WARN("invalid base64: %s\n", debugstr_w(&base64
[3]));
1625 bin
[n
] = (unsigned char) (((in
[2] << 6) & 0xc0) | in
[3]);
1634 /***********************************************************************
1635 * HTTP_InsertAuthorization
1637 * Insert or delete the authorization field in the request header.
1639 static BOOL
HTTP_InsertAuthorization( http_request_t
*request
, struct HttpAuthInfo
*pAuthInfo
, LPCWSTR header
)
1643 static const WCHAR wszSpace
[] = {' ',0};
1644 static const WCHAR wszBasic
[] = {'B','a','s','i','c',0};
1646 WCHAR
*authorization
= NULL
;
1648 if (pAuthInfo
->auth_data_len
)
1650 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1651 len
= strlenW(pAuthInfo
->scheme
)+1+((pAuthInfo
->auth_data_len
+2)*4)/3;
1652 authorization
= heap_alloc((len
+1)*sizeof(WCHAR
));
1656 strcpyW(authorization
, pAuthInfo
->scheme
);
1657 strcatW(authorization
, wszSpace
);
1658 HTTP_EncodeBase64(pAuthInfo
->auth_data
,
1659 pAuthInfo
->auth_data_len
,
1660 authorization
+strlenW(authorization
));
1662 /* clear the data as it isn't valid now that it has been sent to the
1663 * server, unless it's Basic authentication which doesn't do
1664 * connection tracking */
1665 if (strcmpiW(pAuthInfo
->scheme
, wszBasic
))
1667 heap_free(pAuthInfo
->auth_data
);
1668 pAuthInfo
->auth_data
= NULL
;
1669 pAuthInfo
->auth_data_len
= 0;
1673 TRACE("Inserting authorization: %s\n", debugstr_w(authorization
));
1675 HTTP_ProcessHeader(request
, header
, authorization
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
1676 heap_free(authorization
);
1681 static WCHAR
*build_proxy_path_url(http_request_t
*req
)
1686 len
= strlenW(req
->server
->scheme_host_port
);
1687 size
= len
+ strlenW(req
->path
) + 1;
1688 if(*req
->path
!= '/')
1690 url
= heap_alloc(size
* sizeof(WCHAR
));
1694 memcpy(url
, req
->server
->scheme_host_port
, len
*sizeof(WCHAR
));
1695 if(*req
->path
!= '/')
1698 strcpyW(url
+len
, req
->path
);
1700 TRACE("url=%s\n", debugstr_w(url
));
1704 /***********************************************************************
1705 * HTTP_DealWithProxy
1707 static BOOL
HTTP_DealWithProxy(appinfo_t
*hIC
, http_session_t
*session
, http_request_t
*request
)
1709 static const WCHAR protoHttp
[] = { 'h','t','t','p',0 };
1710 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/',0 };
1711 static const WCHAR szFormat
[] = { 'h','t','t','p',':','/','/','%','s',0 };
1712 WCHAR buf
[INTERNET_MAX_HOST_NAME_LENGTH
];
1713 WCHAR protoProxy
[INTERNET_MAX_URL_LENGTH
];
1714 DWORD protoProxyLen
= INTERNET_MAX_URL_LENGTH
;
1715 WCHAR proxy
[INTERNET_MAX_URL_LENGTH
];
1716 static WCHAR szNul
[] = { 0 };
1717 URL_COMPONENTSW UrlComponents
;
1718 server_t
*new_server
;
1721 memset( &UrlComponents
, 0, sizeof UrlComponents
);
1722 UrlComponents
.dwStructSize
= sizeof UrlComponents
;
1723 UrlComponents
.lpszHostName
= buf
;
1724 UrlComponents
.dwHostNameLength
= INTERNET_MAX_HOST_NAME_LENGTH
;
1726 if (!INTERNET_FindProxyForProtocol(hIC
->proxy
, protoHttp
, protoProxy
, &protoProxyLen
))
1728 if( CSTR_EQUAL
!= CompareStringW(LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
1729 protoProxy
,strlenW(szHttp
),szHttp
,strlenW(szHttp
)) )
1730 sprintfW(proxy
, szFormat
, protoProxy
);
1732 strcpyW(proxy
, protoProxy
);
1733 if( !InternetCrackUrlW(proxy
, 0, 0, &UrlComponents
) )
1735 if( UrlComponents
.dwHostNameLength
== 0 )
1738 if( !request
->path
)
1739 request
->path
= szNul
;
1741 is_https
= (UrlComponents
.nScheme
== INTERNET_SCHEME_HTTPS
);
1742 if (is_https
&& UrlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
1743 UrlComponents
.nPort
= INTERNET_DEFAULT_HTTPS_PORT
;
1745 new_server
= get_server(UrlComponents
.lpszHostName
, UrlComponents
.nPort
, is_https
, TRUE
);
1749 request
->proxy
= new_server
;
1751 TRACE("proxy server=%s port=%d\n", debugstr_w(new_server
->name
), new_server
->port
);
1755 static DWORD
HTTP_ResolveName(http_request_t
*request
)
1757 server_t
*server
= request
->proxy
? request
->proxy
: request
->server
;
1761 if(server
->addr_len
)
1762 return ERROR_SUCCESS
;
1764 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
1765 INTERNET_STATUS_RESOLVING_NAME
,
1767 (strlenW(server
->name
)+1) * sizeof(WCHAR
));
1769 addr_len
= sizeof(server
->addr
);
1770 if (!GetAddress(server
->name
, server
->port
, (struct sockaddr
*)&server
->addr
, &addr_len
))
1771 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
1773 switch(server
->addr
.ss_family
) {
1775 addr
= &((struct sockaddr_in
*)&server
->addr
)->sin_addr
;
1778 addr
= &((struct sockaddr_in6
*)&server
->addr
)->sin6_addr
;
1781 WARN("unsupported family %d\n", server
->addr
.ss_family
);
1782 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
1785 server
->addr_len
= addr_len
;
1786 inet_ntop(server
->addr
.ss_family
, addr
, server
->addr_str
, sizeof(server
->addr_str
));
1787 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
1788 INTERNET_STATUS_NAME_RESOLVED
,
1789 server
->addr_str
, strlen(server
->addr_str
)+1);
1791 TRACE("resolved %s to %s\n", debugstr_w(server
->name
), server
->addr_str
);
1792 return ERROR_SUCCESS
;
1795 static BOOL
HTTP_GetRequestURL(http_request_t
*req
, LPWSTR buf
)
1797 static const WCHAR http
[] = { 'h','t','t','p',':','/','/',0 };
1798 static const WCHAR https
[] = { 'h','t','t','p','s',':','/','/',0 };
1799 static const WCHAR slash
[] = { '/',0 };
1800 LPHTTPHEADERW host_header
;
1803 host_header
= HTTP_GetHeader(req
, hostW
);
1807 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1811 strcpyW(buf
, scheme
);
1812 strcatW(buf
, host_header
->lpszValue
);
1813 if (req
->path
[0] != '/')
1814 strcatW(buf
, slash
);
1815 strcatW(buf
, req
->path
);
1820 /***********************************************************************
1821 * HTTPREQ_Destroy (internal)
1823 * Deallocate request handle
1826 static void HTTPREQ_Destroy(object_header_t
*hdr
)
1828 http_request_t
*request
= (http_request_t
*) hdr
;
1833 if(request
->hCacheFile
) {
1834 CloseHandle(request
->hCacheFile
);
1835 DeleteFileW(request
->cacheFile
);
1837 heap_free(request
->cacheFile
);
1839 request
->read_section
.DebugInfo
->Spare
[0] = 0;
1840 DeleteCriticalSection( &request
->read_section
);
1841 WININET_Release(&request
->session
->hdr
);
1843 destroy_authinfo(request
->authInfo
);
1844 destroy_authinfo(request
->proxyAuthInfo
);
1847 server_release(request
->server
);
1849 server_release(request
->proxy
);
1851 heap_free(request
->path
);
1852 heap_free(request
->verb
);
1853 heap_free(request
->rawHeaders
);
1854 heap_free(request
->version
);
1855 heap_free(request
->statusText
);
1857 for (i
= 0; i
< request
->nCustHeaders
; i
++)
1859 heap_free(request
->custHeaders
[i
].lpszField
);
1860 heap_free(request
->custHeaders
[i
].lpszValue
);
1862 destroy_data_stream(request
->data_stream
);
1863 heap_free(request
->custHeaders
);
1866 static void http_release_netconn(http_request_t
*req
, BOOL reuse
)
1868 TRACE("%p %p\n",req
, req
->netconn
);
1873 if(reuse
&& req
->netconn
->keep_alive
) {
1876 EnterCriticalSection(&connection_pool_cs
);
1878 list_add_head(&req
->netconn
->server
->conn_pool
, &req
->netconn
->pool_entry
);
1879 req
->netconn
->keep_until
= GetTickCount64() + COLLECT_TIME
;
1880 req
->netconn
= NULL
;
1882 run_collector
= !collector_running
;
1883 collector_running
= TRUE
;
1885 LeaveCriticalSection(&connection_pool_cs
);
1888 HANDLE thread
= NULL
;
1891 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
, (const WCHAR
*)WININET_hModule
, &module
);
1893 thread
= CreateThread(NULL
, 0, collect_connections_proc
, NULL
, 0, NULL
);
1895 EnterCriticalSection(&connection_pool_cs
);
1896 collector_running
= FALSE
;
1897 LeaveCriticalSection(&connection_pool_cs
);
1900 FreeLibrary(module
);
1903 CloseHandle(thread
);
1908 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
1909 INTERNET_STATUS_CLOSING_CONNECTION
, 0, 0);
1911 free_netconn(req
->netconn
);
1912 req
->netconn
= NULL
;
1914 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
1915 INTERNET_STATUS_CONNECTION_CLOSED
, 0, 0);
1918 static BOOL
HTTP_KeepAlive(http_request_t
*request
)
1920 WCHAR szVersion
[10];
1921 WCHAR szConnectionResponse
[20];
1922 DWORD dwBufferSize
= sizeof(szVersion
);
1923 BOOL keepalive
= FALSE
;
1925 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1926 * the connection is keep-alive by default */
1927 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_VERSION
, szVersion
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1928 && !strcmpiW(szVersion
, g_szHttp1_1
))
1933 dwBufferSize
= sizeof(szConnectionResponse
);
1934 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_PROXY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1935 || HTTP_HttpQueryInfoW(request
, HTTP_QUERY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
)
1937 keepalive
= !strcmpiW(szConnectionResponse
, szKeepAlive
);
1943 static void HTTPREQ_CloseConnection(object_header_t
*hdr
)
1945 http_request_t
*req
= (http_request_t
*)hdr
;
1947 http_release_netconn(req
, drain_content(req
, FALSE
));
1950 static DWORD
str_to_buffer(const WCHAR
*str
, void *buffer
, DWORD
*size
, BOOL unicode
)
1956 if (*size
< (len
+ 1) * sizeof(WCHAR
))
1958 *size
= (len
+ 1) * sizeof(WCHAR
);
1959 return ERROR_INSUFFICIENT_BUFFER
;
1961 strcpyW(buffer
, str
);
1963 return ERROR_SUCCESS
;
1967 len
= WideCharToMultiByte(CP_ACP
, 0, str
, -1, buffer
, *size
, NULL
, NULL
);
1971 return ERROR_INSUFFICIENT_BUFFER
;
1974 return ERROR_SUCCESS
;
1978 static DWORD
HTTPREQ_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
1980 http_request_t
*req
= (http_request_t
*)hdr
;
1983 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO
:
1985 INTERNET_DIAGNOSTIC_SOCKET_INFO
*info
= buffer
;
1987 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1989 if (*size
< sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
))
1990 return ERROR_INSUFFICIENT_BUFFER
;
1991 *size
= sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
);
1992 /* FIXME: can't get a SOCKET from our connection since we don't use
1996 /* FIXME: get source port from req->netConnection */
1997 info
->SourcePort
= 0;
1998 info
->DestPort
= req
->server
->port
;
2000 if (HTTP_KeepAlive(req
))
2001 info
->Flags
|= IDSI_FLAG_KEEP_ALIVE
;
2003 info
->Flags
|= IDSI_FLAG_PROXY
;
2004 if (req
->netconn
->secure
)
2005 info
->Flags
|= IDSI_FLAG_SECURE
;
2007 return ERROR_SUCCESS
;
2011 TRACE("Queried undocumented option 98, forwarding to INTERNET_OPTION_SECURITY_FLAGS\n");
2013 case INTERNET_OPTION_SECURITY_FLAGS
:
2017 if (*size
< sizeof(ULONG
))
2018 return ERROR_INSUFFICIENT_BUFFER
;
2020 *size
= sizeof(DWORD
);
2021 flags
= req
->netconn
? req
->netconn
->security_flags
: req
->security_flags
| req
->server
->security_flags
;
2022 *(DWORD
*)buffer
= flags
;
2024 TRACE("INTERNET_OPTION_SECURITY_FLAGS %x\n", flags
);
2025 return ERROR_SUCCESS
;
2028 case INTERNET_OPTION_HANDLE_TYPE
:
2029 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2031 if (*size
< sizeof(ULONG
))
2032 return ERROR_INSUFFICIENT_BUFFER
;
2034 *size
= sizeof(DWORD
);
2035 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_HTTP_REQUEST
;
2036 return ERROR_SUCCESS
;
2038 case INTERNET_OPTION_URL
: {
2039 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
2042 static const WCHAR httpW
[] = {'h','t','t','p',':','/','/',0};
2044 TRACE("INTERNET_OPTION_URL\n");
2046 host
= HTTP_GetHeader(req
, hostW
);
2047 strcpyW(url
, httpW
);
2048 strcatW(url
, host
->lpszValue
);
2049 strcatW(url
, req
->path
);
2051 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url
));
2052 return str_to_buffer(url
, buffer
, size
, unicode
);
2054 case INTERNET_OPTION_USER_AGENT
:
2055 return str_to_buffer(req
->session
->appInfo
->agent
, buffer
, size
, unicode
);
2056 case INTERNET_OPTION_USERNAME
:
2057 return str_to_buffer(req
->session
->userName
, buffer
, size
, unicode
);
2058 case INTERNET_OPTION_PASSWORD
:
2059 return str_to_buffer(req
->session
->password
, buffer
, size
, unicode
);
2060 case INTERNET_OPTION_PROXY_USERNAME
:
2061 return str_to_buffer(req
->session
->appInfo
->proxyUsername
, buffer
, size
, unicode
);
2062 case INTERNET_OPTION_PROXY_PASSWORD
:
2063 return str_to_buffer(req
->session
->appInfo
->proxyPassword
, buffer
, size
, unicode
);
2065 case INTERNET_OPTION_CACHE_TIMESTAMPS
: {
2066 INTERNET_CACHE_ENTRY_INFOW
*info
;
2067 INTERNET_CACHE_TIMESTAMPS
*ts
= buffer
;
2068 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
2069 DWORD nbytes
, error
;
2072 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2074 if (*size
< sizeof(*ts
))
2076 *size
= sizeof(*ts
);
2077 return ERROR_INSUFFICIENT_BUFFER
;
2080 HTTP_GetRequestURL(req
, url
);
2081 ret
= GetUrlCacheEntryInfoW(url
, NULL
, &nbytes
);
2082 error
= GetLastError();
2083 if (!ret
&& error
== ERROR_INSUFFICIENT_BUFFER
)
2085 if (!(info
= heap_alloc(nbytes
)))
2086 return ERROR_OUTOFMEMORY
;
2088 GetUrlCacheEntryInfoW(url
, info
, &nbytes
);
2090 ts
->ftExpires
= info
->ExpireTime
;
2091 ts
->ftLastModified
= info
->LastModifiedTime
;
2094 *size
= sizeof(*ts
);
2095 return ERROR_SUCCESS
;
2100 case INTERNET_OPTION_DATAFILE_NAME
: {
2103 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2105 if(!req
->cacheFile
) {
2107 return ERROR_INTERNET_ITEM_NOT_FOUND
;
2111 req_size
= (lstrlenW(req
->cacheFile
)+1) * sizeof(WCHAR
);
2112 if(*size
< req_size
)
2113 return ERROR_INSUFFICIENT_BUFFER
;
2116 memcpy(buffer
, req
->cacheFile
, *size
);
2117 return ERROR_SUCCESS
;
2119 req_size
= WideCharToMultiByte(CP_ACP
, 0, req
->cacheFile
, -1, NULL
, 0, NULL
, NULL
);
2120 if (req_size
> *size
)
2121 return ERROR_INSUFFICIENT_BUFFER
;
2123 *size
= WideCharToMultiByte(CP_ACP
, 0, req
->cacheFile
,
2124 -1, buffer
, *size
, NULL
, NULL
);
2125 return ERROR_SUCCESS
;
2129 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT
: {
2130 PCCERT_CONTEXT context
;
2132 if(*size
< sizeof(INTERNET_CERTIFICATE_INFOA
)) {
2133 *size
= sizeof(INTERNET_CERTIFICATE_INFOA
);
2134 return ERROR_INSUFFICIENT_BUFFER
;
2137 context
= (PCCERT_CONTEXT
)NETCON_GetCert(req
->netconn
);
2139 INTERNET_CERTIFICATE_INFOA
*info
= (INTERNET_CERTIFICATE_INFOA
*)buffer
;
2142 memset(info
, 0, sizeof(*info
));
2143 info
->ftExpiry
= context
->pCertInfo
->NotAfter
;
2144 info
->ftStart
= context
->pCertInfo
->NotBefore
;
2145 len
= CertNameToStrA(context
->dwCertEncodingType
,
2146 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
|CERT_NAME_STR_CRLF_FLAG
, NULL
, 0);
2147 info
->lpszSubjectInfo
= LocalAlloc(0, len
);
2148 if(info
->lpszSubjectInfo
)
2149 CertNameToStrA(context
->dwCertEncodingType
,
2150 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
|CERT_NAME_STR_CRLF_FLAG
,
2151 info
->lpszSubjectInfo
, len
);
2152 len
= CertNameToStrA(context
->dwCertEncodingType
,
2153 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
|CERT_NAME_STR_CRLF_FLAG
, NULL
, 0);
2154 info
->lpszIssuerInfo
= LocalAlloc(0, len
);
2155 if(info
->lpszIssuerInfo
)
2156 CertNameToStrA(context
->dwCertEncodingType
,
2157 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
|CERT_NAME_STR_CRLF_FLAG
,
2158 info
->lpszIssuerInfo
, len
);
2159 info
->dwKeySize
= NETCON_GetCipherStrength(req
->netconn
);
2160 CertFreeCertificateContext(context
);
2161 return ERROR_SUCCESS
;
2163 return ERROR_NOT_SUPPORTED
;
2165 case INTERNET_OPTION_CONNECT_TIMEOUT
:
2166 if (*size
< sizeof(DWORD
))
2167 return ERROR_INSUFFICIENT_BUFFER
;
2169 *size
= sizeof(DWORD
);
2170 *(DWORD
*)buffer
= req
->connect_timeout
;
2171 return ERROR_SUCCESS
;
2172 case INTERNET_OPTION_REQUEST_FLAGS
: {
2175 if (*size
< sizeof(DWORD
))
2176 return ERROR_INSUFFICIENT_BUFFER
;
2178 /* FIXME: Add support for:
2179 * INTERNET_REQFLAG_FROM_CACHE
2180 * INTERNET_REQFLAG_CACHE_WRITE_DISABLED
2184 flags
|= INTERNET_REQFLAG_VIA_PROXY
;
2185 if(!req
->rawHeaders
)
2186 flags
|= INTERNET_REQFLAG_NO_HEADERS
;
2188 TRACE("INTERNET_OPTION_REQUEST_FLAGS returning %x\n", flags
);
2190 *size
= sizeof(DWORD
);
2191 *(DWORD
*)buffer
= flags
;
2192 return ERROR_SUCCESS
;
2196 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
2199 static DWORD
HTTPREQ_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
2201 http_request_t
*req
= (http_request_t
*)hdr
;
2204 case 99: /* Undocumented, seems to be INTERNET_OPTION_SECURITY_FLAGS with argument validation */
2205 TRACE("Undocumented option 99\n");
2207 if (!buffer
|| size
!= sizeof(DWORD
))
2208 return ERROR_INVALID_PARAMETER
;
2209 if(*(DWORD
*)buffer
& ~SECURITY_SET_MASK
)
2210 return ERROR_INTERNET_OPTION_NOT_SETTABLE
;
2213 case INTERNET_OPTION_SECURITY_FLAGS
:
2217 if (!buffer
|| size
!= sizeof(DWORD
))
2218 return ERROR_INVALID_PARAMETER
;
2219 flags
= *(DWORD
*)buffer
;
2220 TRACE("INTERNET_OPTION_SECURITY_FLAGS %08x\n", flags
);
2221 flags
&= SECURITY_SET_MASK
;
2222 req
->security_flags
|= flags
;
2224 req
->netconn
->security_flags
|= flags
;
2225 return ERROR_SUCCESS
;
2227 case INTERNET_OPTION_CONNECT_TIMEOUT
:
2228 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
2229 req
->connect_timeout
= *(DWORD
*)buffer
;
2230 return ERROR_SUCCESS
;
2232 case INTERNET_OPTION_SEND_TIMEOUT
:
2233 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
2234 req
->send_timeout
= *(DWORD
*)buffer
;
2235 return ERROR_SUCCESS
;
2237 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
2238 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
2239 req
->receive_timeout
= *(DWORD
*)buffer
;
2240 return ERROR_SUCCESS
;
2242 case INTERNET_OPTION_USERNAME
:
2243 heap_free(req
->session
->userName
);
2244 if (!(req
->session
->userName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
2245 return ERROR_SUCCESS
;
2247 case INTERNET_OPTION_PASSWORD
:
2248 heap_free(req
->session
->password
);
2249 if (!(req
->session
->password
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
2250 return ERROR_SUCCESS
;
2252 case INTERNET_OPTION_PROXY_USERNAME
:
2253 heap_free(req
->session
->appInfo
->proxyUsername
);
2254 if (!(req
->session
->appInfo
->proxyUsername
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
2255 return ERROR_SUCCESS
;
2257 case INTERNET_OPTION_PROXY_PASSWORD
:
2258 heap_free(req
->session
->appInfo
->proxyPassword
);
2259 if (!(req
->session
->appInfo
->proxyPassword
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
2260 return ERROR_SUCCESS
;
2262 case INTERNET_OPTION_HTTP_DECODING
:
2263 if(size
!= sizeof(BOOL
))
2264 return ERROR_INVALID_PARAMETER
;
2265 req
->decoding
= *(BOOL
*)buffer
;
2266 return ERROR_SUCCESS
;
2269 return INET_SetOption(hdr
, option
, buffer
, size
);
2272 static void commit_cache_entry(http_request_t
*req
)
2274 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
2278 CloseHandle(req
->hCacheFile
);
2279 req
->hCacheFile
= NULL
;
2281 if(HTTP_GetRequestURL(req
, url
)) {
2284 headersLen
= req
->rawHeaders
? strlenW(req
->rawHeaders
) : 0;
2285 CommitUrlCacheEntryW(url
, req
->cacheFile
, req
->expires
,
2286 req
->last_modified
, NORMAL_CACHE_ENTRY
,
2287 req
->rawHeaders
, headersLen
, NULL
, 0);
2291 static void create_cache_entry(http_request_t
*req
)
2293 static const WCHAR no_cacheW
[] = {'n','o','-','c','a','c','h','e',0};
2294 static const WCHAR no_storeW
[] = {'n','o','-','s','t','o','r','e',0};
2296 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
2297 WCHAR file_name
[MAX_PATH
+1];
2300 /* FIXME: We should free previous cache file earlier */
2301 heap_free(req
->cacheFile
);
2302 CloseHandle(req
->hCacheFile
);
2303 req
->hCacheFile
= NULL
;
2305 if(req
->hdr
.dwFlags
& INTERNET_FLAG_NO_CACHE_WRITE
)
2309 int header_idx
= HTTP_GetCustomHeaderIndex(req
, szCache_Control
, 0, FALSE
);
2310 if(header_idx
!=-1 && (!strcmpiW(req
->custHeaders
[header_idx
].lpszValue
, no_cacheW
)
2311 || !strcmpiW(req
->custHeaders
[header_idx
].lpszValue
, no_storeW
)))
2316 if(!(req
->hdr
.dwFlags
& INTERNET_FLAG_NEED_FILE
))
2319 FIXME("INTERNET_FLAG_NEED_FILE is not supported correctly\n");
2322 b
= HTTP_GetRequestURL(req
, url
);
2324 WARN("Could not get URL\n");
2328 b
= CreateUrlCacheEntryW(url
, req
->contentLength
== ~0u ? 0 : req
->contentLength
, NULL
, file_name
, 0);
2330 WARN("Could not create cache entry: %08x\n", GetLastError());
2334 req
->cacheFile
= heap_strdupW(file_name
);
2335 req
->hCacheFile
= CreateFileW(req
->cacheFile
, GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
2336 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
2337 if(req
->hCacheFile
== INVALID_HANDLE_VALUE
) {
2338 WARN("Could not create file: %u\n", GetLastError());
2339 req
->hCacheFile
= NULL
;
2343 if(req
->read_size
) {
2346 b
= WriteFile(req
->hCacheFile
, req
->read_buf
+req
->read_pos
, req
->read_size
, &written
, NULL
);
2348 FIXME("WriteFile failed: %u\n", GetLastError());
2350 if(req
->data_stream
->vtbl
->end_of_data(req
->data_stream
, req
))
2351 commit_cache_entry(req
);
2355 /* read some more data into the read buffer (the read section must be held) */
2356 static DWORD
read_more_data( http_request_t
*req
, int maxlen
)
2363 /* move existing data to the start of the buffer */
2365 memmove( req
->read_buf
, req
->read_buf
+ req
->read_pos
, req
->read_size
);
2369 if (maxlen
== -1) maxlen
= sizeof(req
->read_buf
);
2371 res
= NETCON_recv( req
->netconn
, req
->read_buf
+ req
->read_size
,
2372 maxlen
- req
->read_size
, 0, &len
);
2373 if(res
== ERROR_SUCCESS
)
2374 req
->read_size
+= len
;
2379 /* remove some amount of data from the read buffer (the read section must be held) */
2380 static void remove_data( http_request_t
*req
, int count
)
2382 if (!(req
->read_size
-= count
)) req
->read_pos
= 0;
2383 else req
->read_pos
+= count
;
2386 static BOOL
read_line( http_request_t
*req
, LPSTR buffer
, DWORD
*len
)
2388 int count
, bytes_read
, pos
= 0;
2391 EnterCriticalSection( &req
->read_section
);
2394 BYTE
*eol
= memchr( req
->read_buf
+ req
->read_pos
, '\n', req
->read_size
);
2398 count
= eol
- (req
->read_buf
+ req
->read_pos
);
2399 bytes_read
= count
+ 1;
2401 else count
= bytes_read
= req
->read_size
;
2403 count
= min( count
, *len
- pos
);
2404 memcpy( buffer
+ pos
, req
->read_buf
+ req
->read_pos
, count
);
2406 remove_data( req
, bytes_read
);
2409 if ((res
= read_more_data( req
, -1 )) != ERROR_SUCCESS
|| !req
->read_size
)
2412 TRACE( "returning empty string %u\n", res
);
2413 LeaveCriticalSection( &req
->read_section
);
2414 INTERNET_SetLastError(res
);
2418 LeaveCriticalSection( &req
->read_section
);
2422 if (pos
&& buffer
[pos
- 1] == '\r') pos
--;
2425 buffer
[*len
- 1] = 0;
2426 TRACE( "returning %s\n", debugstr_a(buffer
));
2430 /* check if we have reached the end of the data to read (the read section must be held) */
2431 static BOOL
end_of_read_data( http_request_t
*req
)
2433 return !req
->read_size
&& req
->data_stream
->vtbl
->end_of_data(req
->data_stream
, req
);
2436 static DWORD
read_http_stream(http_request_t
*req
, BYTE
*buf
, DWORD size
, DWORD
*read
, read_mode_t read_mode
)
2440 res
= req
->data_stream
->vtbl
->read(req
->data_stream
, req
, buf
, size
, read
, read_mode
);
2441 assert(*read
<= size
);
2443 if(req
->hCacheFile
) {
2448 bres
= WriteFile(req
->hCacheFile
, buf
, *read
, &written
, NULL
);
2450 FIXME("WriteFile failed: %u\n", GetLastError());
2453 if(req
->data_stream
->vtbl
->end_of_data(req
->data_stream
, req
))
2454 commit_cache_entry(req
);
2460 /* fetch some more data into the read buffer (the read section must be held) */
2461 static DWORD
refill_read_buffer(http_request_t
*req
, read_mode_t read_mode
, DWORD
*read_bytes
)
2465 if(req
->read_size
== sizeof(req
->read_buf
))
2466 return ERROR_SUCCESS
;
2470 memmove(req
->read_buf
, req
->read_buf
+req
->read_pos
, req
->read_size
);
2474 res
= read_http_stream(req
, req
->read_buf
+req
->read_size
, sizeof(req
->read_buf
) - req
->read_size
,
2476 req
->read_size
+= read
;
2478 TRACE("read %u bytes, read_size %u\n", read
, req
->read_size
);
2484 /* return the size of data available to be read immediately (the read section must be held) */
2485 static DWORD
get_avail_data( http_request_t
*req
)
2487 return req
->read_size
+ req
->data_stream
->vtbl
->get_avail_data(req
->data_stream
, req
);
2490 static DWORD
netconn_get_avail_data(data_stream_t
*stream
, http_request_t
*req
)
2492 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2496 NETCON_query_data_available(req
->netconn
, &avail
);
2497 return netconn_stream
->content_length
== ~0u
2499 : min(avail
, netconn_stream
->content_length
-netconn_stream
->content_read
);
2502 static BOOL
netconn_end_of_data(data_stream_t
*stream
, http_request_t
*req
)
2504 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2505 return netconn_stream
->content_read
== netconn_stream
->content_length
|| !req
->netconn
;
2508 static DWORD
netconn_read(data_stream_t
*stream
, http_request_t
*req
, BYTE
*buf
, DWORD size
,
2509 DWORD
*read
, read_mode_t read_mode
)
2511 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2514 size
= min(size
, netconn_stream
->content_length
-netconn_stream
->content_read
);
2516 if(read_mode
== READMODE_NOBLOCK
) {
2517 DWORD avail
= netconn_get_avail_data(stream
, req
);
2522 if(size
&& req
->netconn
) {
2523 if(NETCON_recv(req
->netconn
, buf
, size
, read_mode
== READMODE_SYNC
? MSG_WAITALL
: 0, &len
) != ERROR_SUCCESS
)
2526 netconn_stream
->content_length
= netconn_stream
->content_read
;
2529 netconn_stream
->content_read
+= *read
= len
;
2530 TRACE("read %u bytes\n", len
);
2531 return ERROR_SUCCESS
;
2534 static BOOL
netconn_drain_content(data_stream_t
*stream
, http_request_t
*req
)
2536 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2541 if(netconn_end_of_data(stream
, req
))
2545 avail
= netconn_get_avail_data(stream
, req
);
2549 if(NETCON_recv(req
->netconn
, buf
, min(avail
, sizeof(buf
)), 0, &len
) != ERROR_SUCCESS
)
2552 netconn_stream
->content_read
+= len
;
2553 }while(netconn_stream
->content_read
< netconn_stream
->content_length
);
2558 static void netconn_destroy(data_stream_t
*stream
)
2562 static const data_stream_vtbl_t netconn_stream_vtbl
= {
2563 netconn_get_avail_data
,
2564 netconn_end_of_data
,
2566 netconn_drain_content
,
2570 /* read some more data into the read buffer (the read section must be held) */
2571 static DWORD
read_more_chunked_data(chunked_stream_t
*stream
, http_request_t
*req
, int maxlen
)
2576 if (stream
->buf_pos
)
2578 /* move existing data to the start of the buffer */
2579 if(stream
->buf_size
)
2580 memmove(stream
->buf
, stream
->buf
+ stream
->buf_pos
, stream
->buf_size
);
2581 stream
->buf_pos
= 0;
2584 if (maxlen
== -1) maxlen
= sizeof(stream
->buf
);
2586 res
= NETCON_recv( req
->netconn
, stream
->buf
+ stream
->buf_size
,
2587 maxlen
- stream
->buf_size
, 0, &len
);
2588 if(res
== ERROR_SUCCESS
)
2589 stream
->buf_size
+= len
;
2594 /* remove some amount of data from the read buffer (the read section must be held) */
2595 static void remove_chunked_data(chunked_stream_t
*stream
, int count
)
2597 if (!(stream
->buf_size
-= count
)) stream
->buf_pos
= 0;
2598 else stream
->buf_pos
+= count
;
2601 /* discard data contents until we reach end of line (the read section must be held) */
2602 static DWORD
discard_chunked_eol(chunked_stream_t
*stream
, http_request_t
*req
)
2608 BYTE
*eol
= memchr(stream
->buf
+ stream
->buf_pos
, '\n', stream
->buf_size
);
2611 remove_chunked_data(stream
, (eol
+ 1) - (stream
->buf
+ stream
->buf_pos
));
2614 stream
->buf_pos
= stream
->buf_size
= 0; /* discard everything */
2615 if ((res
= read_more_chunked_data(stream
, req
, -1)) != ERROR_SUCCESS
) return res
;
2616 } while (stream
->buf_size
);
2617 return ERROR_SUCCESS
;
2620 /* read the size of the next chunk (the read section must be held) */
2621 static DWORD
start_next_chunk(chunked_stream_t
*stream
, http_request_t
*req
)
2624 DWORD chunk_size
= 0, res
;
2626 if(stream
->chunk_size
!= ~0u && (res
= discard_chunked_eol(stream
, req
)) != ERROR_SUCCESS
)
2631 while (stream
->buf_size
)
2633 char ch
= stream
->buf
[stream
->buf_pos
];
2634 if (ch
>= '0' && ch
<= '9') chunk_size
= chunk_size
* 16 + ch
- '0';
2635 else if (ch
>= 'a' && ch
<= 'f') chunk_size
= chunk_size
* 16 + ch
- 'a' + 10;
2636 else if (ch
>= 'A' && ch
<= 'F') chunk_size
= chunk_size
* 16 + ch
- 'A' + 10;
2637 else if (ch
== ';' || ch
== '\r' || ch
== '\n')
2639 TRACE( "reading %u byte chunk\n", chunk_size
);
2640 stream
->chunk_size
= chunk_size
;
2641 req
->contentLength
+= chunk_size
;
2642 return discard_chunked_eol(stream
, req
);
2644 remove_chunked_data(stream
, 1);
2646 if ((res
= read_more_chunked_data(stream
, req
, -1)) != ERROR_SUCCESS
) return res
;
2647 if (!stream
->buf_size
)
2649 stream
->chunk_size
= 0;
2650 return ERROR_SUCCESS
;
2655 static DWORD
chunked_get_avail_data(data_stream_t
*stream
, http_request_t
*req
)
2657 /* Allow reading only from read buffer */
2661 static BOOL
chunked_end_of_data(data_stream_t
*stream
, http_request_t
*req
)
2663 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2664 return !chunked_stream
->chunk_size
;
2667 static DWORD
chunked_read(data_stream_t
*stream
, http_request_t
*req
, BYTE
*buf
, DWORD size
,
2668 DWORD
*read
, read_mode_t read_mode
)
2670 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2671 DWORD read_bytes
= 0, ret_read
= 0, res
= ERROR_SUCCESS
;
2673 if(chunked_stream
->chunk_size
== ~0u) {
2674 res
= start_next_chunk(chunked_stream
, req
);
2675 if(res
!= ERROR_SUCCESS
)
2679 while(size
&& chunked_stream
->chunk_size
) {
2680 if(chunked_stream
->buf_size
) {
2681 read_bytes
= min(size
, min(chunked_stream
->buf_size
, chunked_stream
->chunk_size
));
2683 /* this could block */
2684 if(read_mode
== READMODE_NOBLOCK
&& read_bytes
== chunked_stream
->chunk_size
)
2687 memcpy(buf
+ret_read
, chunked_stream
->buf
+chunked_stream
->buf_pos
, read_bytes
);
2688 remove_chunked_data(chunked_stream
, read_bytes
);
2690 read_bytes
= min(size
, chunked_stream
->chunk_size
);
2692 if(read_mode
== READMODE_NOBLOCK
) {
2695 if(!req
->netconn
|| !NETCON_query_data_available(req
->netconn
, &avail
) || !avail
)
2697 if(read_bytes
> avail
)
2700 /* this could block */
2701 if(read_bytes
== chunked_stream
->chunk_size
)
2705 res
= NETCON_recv(req
->netconn
, (char *)buf
+ret_read
, read_bytes
, 0, (int*)&read_bytes
);
2706 if(res
!= ERROR_SUCCESS
)
2710 chunked_stream
->chunk_size
-= read_bytes
;
2712 ret_read
+= read_bytes
;
2713 if(!chunked_stream
->chunk_size
) {
2714 assert(read_mode
!= READMODE_NOBLOCK
);
2715 res
= start_next_chunk(chunked_stream
, req
);
2716 if(res
!= ERROR_SUCCESS
)
2720 if(read_mode
== READMODE_ASYNC
)
2721 read_mode
= READMODE_NOBLOCK
;
2724 TRACE("read %u bytes\n", ret_read
);
2729 static BOOL
chunked_drain_content(data_stream_t
*stream
, http_request_t
*req
)
2731 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2733 /* FIXME: we can do better */
2734 return !chunked_stream
->chunk_size
;
2737 static void chunked_destroy(data_stream_t
*stream
)
2739 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2740 heap_free(chunked_stream
);
2743 static const data_stream_vtbl_t chunked_stream_vtbl
= {
2744 chunked_get_avail_data
,
2745 chunked_end_of_data
,
2747 chunked_drain_content
,
2751 /* set the request content length based on the headers */
2752 static DWORD
set_content_length(http_request_t
*request
)
2754 static const WCHAR szChunked
[] = {'c','h','u','n','k','e','d',0};
2758 if(request
->status_code
== HTTP_STATUS_NO_CONTENT
) {
2759 request
->contentLength
= request
->netconn_stream
.content_length
= 0;
2760 return ERROR_SUCCESS
;
2763 size
= sizeof(request
->contentLength
);
2764 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_CONTENT_LENGTH
,
2765 &request
->contentLength
, &size
, NULL
) != ERROR_SUCCESS
)
2766 request
->contentLength
= ~0u;
2767 request
->netconn_stream
.content_length
= request
->contentLength
;
2768 request
->netconn_stream
.content_read
= request
->read_size
;
2770 size
= sizeof(encoding
);
2771 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_TRANSFER_ENCODING
, encoding
, &size
, NULL
) == ERROR_SUCCESS
&&
2772 !strcmpiW(encoding
, szChunked
))
2774 chunked_stream_t
*chunked_stream
;
2776 chunked_stream
= heap_alloc(sizeof(*chunked_stream
));
2778 return ERROR_OUTOFMEMORY
;
2780 chunked_stream
->data_stream
.vtbl
= &chunked_stream_vtbl
;
2781 chunked_stream
->buf_size
= chunked_stream
->buf_pos
= 0;
2782 chunked_stream
->chunk_size
= ~0u;
2784 if(request
->read_size
) {
2785 memcpy(chunked_stream
->buf
, request
->read_buf
+request
->read_pos
, request
->read_size
);
2786 chunked_stream
->buf_size
= request
->read_size
;
2787 request
->read_size
= request
->read_pos
= 0;
2790 request
->data_stream
= &chunked_stream
->data_stream
;
2791 request
->contentLength
= ~0u;
2792 request
->read_chunked
= TRUE
;
2795 if(request
->decoding
) {
2798 static const WCHAR gzipW
[] = {'g','z','i','p',0};
2800 encoding_idx
= HTTP_GetCustomHeaderIndex(request
, szContent_Encoding
, 0, FALSE
);
2801 if(encoding_idx
!= -1 && !strcmpiW(request
->custHeaders
[encoding_idx
].lpszValue
, gzipW
))
2802 return init_gzip_stream(request
);
2805 return ERROR_SUCCESS
;
2808 static void send_request_complete(http_request_t
*req
, DWORD_PTR result
, DWORD error
)
2810 INTERNET_ASYNC_RESULT iar
;
2812 iar
.dwResult
= result
;
2813 iar
.dwError
= error
;
2815 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2816 sizeof(INTERNET_ASYNC_RESULT
));
2819 static void HTTP_ReceiveRequestData(http_request_t
*req
, BOOL first_notif
, DWORD
*ret_size
)
2821 DWORD res
, read
= 0, avail
= 0;
2826 EnterCriticalSection( &req
->read_section
);
2828 mode
= first_notif
&& req
->read_size
? READMODE_NOBLOCK
: READMODE_ASYNC
;
2829 res
= refill_read_buffer(req
, mode
, &read
);
2830 if(res
== ERROR_SUCCESS
)
2831 avail
= get_avail_data(req
);
2833 LeaveCriticalSection( &req
->read_section
);
2835 if(res
!= ERROR_SUCCESS
|| (mode
!= READMODE_NOBLOCK
&& !read
)) {
2836 WARN("res %u read %u, closing connection\n", res
, read
);
2837 http_release_netconn(req
, FALSE
);
2840 if(res
!= ERROR_SUCCESS
) {
2841 send_request_complete(req
, 0, res
);
2850 send_request_complete(req
, req
->session
->hdr
.dwInternalFlags
& INET_OPENURL
? (DWORD_PTR
)req
->hdr
.hInternet
: 1, avail
);
2853 /* read data from the http connection (the read section must be held) */
2854 static DWORD
HTTPREQ_Read(http_request_t
*req
, void *buffer
, DWORD size
, DWORD
*read
, BOOL sync
)
2856 DWORD current_read
= 0, ret_read
= 0;
2857 read_mode_t read_mode
;
2858 DWORD res
= ERROR_SUCCESS
;
2860 read_mode
= req
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
? READMODE_ASYNC
: READMODE_SYNC
;
2862 EnterCriticalSection( &req
->read_section
);
2864 if(req
->read_size
) {
2865 ret_read
= min(size
, req
->read_size
);
2866 memcpy(buffer
, req
->read_buf
+req
->read_pos
, ret_read
);
2867 req
->read_size
-= ret_read
;
2868 req
->read_pos
+= ret_read
;
2869 if(read_mode
== READMODE_ASYNC
)
2870 read_mode
= READMODE_NOBLOCK
;
2873 if(ret_read
< size
) {
2874 res
= read_http_stream(req
, (BYTE
*)buffer
+ret_read
, size
-ret_read
, ¤t_read
, read_mode
);
2875 ret_read
+= current_read
;
2878 LeaveCriticalSection( &req
->read_section
);
2881 TRACE( "retrieved %u bytes (%u)\n", ret_read
, req
->contentLength
);
2883 if(size
&& !ret_read
)
2884 http_release_netconn(req
, res
== ERROR_SUCCESS
);
2889 static BOOL
drain_content(http_request_t
*req
, BOOL blocking
)
2893 if(!req
->netconn
|| req
->contentLength
== -1)
2896 if(!strcmpW(req
->verb
, szHEAD
))
2900 return req
->data_stream
->vtbl
->drain_content(req
->data_stream
, req
);
2902 EnterCriticalSection( &req
->read_section
);
2905 DWORD bytes_read
, res
;
2908 res
= HTTPREQ_Read(req
, buf
, sizeof(buf
), &bytes_read
, TRUE
);
2909 if(res
!= ERROR_SUCCESS
) {
2919 LeaveCriticalSection( &req
->read_section
);
2923 static DWORD
HTTPREQ_ReadFile(object_header_t
*hdr
, void *buffer
, DWORD size
, DWORD
*read
)
2925 http_request_t
*req
= (http_request_t
*)hdr
;
2928 EnterCriticalSection( &req
->read_section
);
2929 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2930 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2932 res
= HTTPREQ_Read(req
, buffer
, size
, read
, TRUE
);
2933 if(res
== ERROR_SUCCESS
)
2935 LeaveCriticalSection( &req
->read_section
);
2945 } read_file_ex_task_t
;
2947 static void AsyncReadFileExProc(task_header_t
*hdr
)
2949 read_file_ex_task_t
*task
= (read_file_ex_task_t
*)hdr
;
2950 http_request_t
*req
= (http_request_t
*)task
->hdr
.hdr
;
2953 TRACE("INTERNETREADFILEEXW %p\n", task
->hdr
.hdr
);
2955 res
= HTTPREQ_Read(req
, task
->buf
, task
->size
, task
->ret_read
, TRUE
);
2956 send_request_complete(req
, res
== ERROR_SUCCESS
, res
);
2959 static DWORD
HTTPREQ_ReadFileEx(object_header_t
*hdr
, void *buf
, DWORD size
, DWORD
*ret_read
,
2960 DWORD flags
, DWORD_PTR context
)
2963 http_request_t
*req
= (http_request_t
*)hdr
;
2964 DWORD res
, read
, cread
, error
= ERROR_SUCCESS
;
2966 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2967 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2969 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2971 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2973 read_file_ex_task_t
*task
;
2975 if (TryEnterCriticalSection( &req
->read_section
))
2977 if (get_avail_data(req
))
2979 res
= HTTPREQ_Read(req
, buf
, size
, &read
, FALSE
);
2980 LeaveCriticalSection( &req
->read_section
);
2983 LeaveCriticalSection( &req
->read_section
);
2986 task
= alloc_async_task(&req
->hdr
, AsyncReadFileExProc
, sizeof(*task
));
2989 task
->ret_read
= ret_read
;
2991 INTERNET_AsyncCall(&task
->hdr
);
2993 return ERROR_IO_PENDING
;
2998 EnterCriticalSection( &req
->read_section
);
2999 if(hdr
->dwError
== ERROR_SUCCESS
)
3000 hdr
->dwError
= INTERNET_HANDLE_IN_USE
;
3001 else if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
3002 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
3005 res
= HTTPREQ_Read(req
, (char*)buf
+read
, size
-read
, &cread
, !(flags
& IRF_NO_WAIT
));
3006 if(res
!= ERROR_SUCCESS
)
3010 if(read
== size
|| end_of_read_data(req
))
3013 LeaveCriticalSection( &req
->read_section
);
3015 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
3016 &cread
, sizeof(cread
));
3017 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
3018 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
3020 EnterCriticalSection( &req
->read_section
);
3023 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
3024 hdr
->dwError
= ERROR_SUCCESS
;
3026 error
= hdr
->dwError
;
3028 LeaveCriticalSection( &req
->read_section
);
3032 if (res
== ERROR_SUCCESS
) {
3033 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
3034 &read
, sizeof(read
));
3037 return res
==ERROR_SUCCESS
? error
: res
;
3040 static DWORD
HTTPREQ_WriteFile(object_header_t
*hdr
, const void *buffer
, DWORD size
, DWORD
*written
)
3043 http_request_t
*request
= (http_request_t
*)hdr
;
3045 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
3048 res
= NETCON_send(request
->netconn
, buffer
, size
, 0, (LPINT
)written
);
3049 if (res
== ERROR_SUCCESS
)
3050 request
->bytesWritten
+= *written
;
3052 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_SENT
, written
, sizeof(DWORD
));
3059 } http_data_available_task_t
;
3061 static void AsyncQueryDataAvailableProc(task_header_t
*hdr
)
3063 http_data_available_task_t
*task
= (http_data_available_task_t
*)hdr
;
3065 HTTP_ReceiveRequestData((http_request_t
*)task
->hdr
.hdr
, FALSE
, task
->ret_size
);
3068 static DWORD
HTTPREQ_QueryDataAvailable(object_header_t
*hdr
, DWORD
*available
, DWORD flags
, DWORD_PTR ctx
)
3070 http_request_t
*req
= (http_request_t
*)hdr
;
3072 TRACE("(%p %p %x %lx)\n", req
, available
, flags
, ctx
);
3074 if (req
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
3076 http_data_available_task_t
*task
;
3078 /* never wait, if we can't enter the section we queue an async request right away */
3079 if (TryEnterCriticalSection( &req
->read_section
))
3081 refill_read_buffer(req
, READMODE_NOBLOCK
, NULL
);
3082 if ((*available
= get_avail_data( req
))) goto done
;
3083 if (end_of_read_data( req
)) goto done
;
3084 LeaveCriticalSection( &req
->read_section
);
3087 task
= alloc_async_task(&req
->hdr
, AsyncQueryDataAvailableProc
, sizeof(*task
));
3088 task
->ret_size
= available
;
3089 INTERNET_AsyncCall(&task
->hdr
);
3090 return ERROR_IO_PENDING
;
3093 EnterCriticalSection( &req
->read_section
);
3095 if (!(*available
= get_avail_data( req
)) && !end_of_read_data( req
))
3097 refill_read_buffer( req
, READMODE_ASYNC
, NULL
);
3098 *available
= get_avail_data( req
);
3102 LeaveCriticalSection( &req
->read_section
);
3104 TRACE( "returning %u\n", *available
);
3105 return ERROR_SUCCESS
;
3108 static const object_vtbl_t HTTPREQVtbl
= {
3110 HTTPREQ_CloseConnection
,
3111 HTTPREQ_QueryOption
,
3116 HTTPREQ_QueryDataAvailable
,
3120 /***********************************************************************
3121 * HTTP_HttpOpenRequestW (internal)
3123 * Open a HTTP request handle
3126 * HINTERNET a HTTP request handle on success
3130 static DWORD
HTTP_HttpOpenRequestW(http_session_t
*session
,
3131 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
3132 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
3133 DWORD dwFlags
, DWORD_PTR dwContext
, HINTERNET
*ret
)
3135 appinfo_t
*hIC
= session
->appInfo
;
3136 http_request_t
*request
;
3137 DWORD len
, res
= ERROR_SUCCESS
;
3141 request
= alloc_object(&session
->hdr
, &HTTPREQVtbl
, sizeof(http_request_t
));
3143 return ERROR_OUTOFMEMORY
;
3145 request
->hdr
.htype
= WH_HHTTPREQ
;
3146 request
->hdr
.dwFlags
= dwFlags
;
3147 request
->hdr
.dwContext
= dwContext
;
3148 request
->contentLength
= ~0u;
3150 request
->netconn_stream
.data_stream
.vtbl
= &netconn_stream_vtbl
;
3151 request
->data_stream
= &request
->netconn_stream
.data_stream
;
3152 request
->connect_timeout
= session
->connect_timeout
;
3153 request
->send_timeout
= session
->send_timeout
;
3154 request
->receive_timeout
= session
->receive_timeout
;
3156 InitializeCriticalSection( &request
->read_section
);
3157 request
->read_section
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": http_request_t.read_section");
3159 WININET_AddRef( &session
->hdr
);
3160 request
->session
= session
;
3161 list_add_head( &session
->hdr
.children
, &request
->hdr
.entry
);
3163 request
->server
= get_server(session
->hostName
, session
->hostPort
, (dwFlags
& INTERNET_FLAG_SECURE
) != 0, TRUE
);
3164 if(!request
->server
) {
3165 WININET_Release(&request
->hdr
);
3166 return ERROR_OUTOFMEMORY
;
3169 if (dwFlags
& INTERNET_FLAG_IGNORE_CERT_CN_INVALID
)
3170 request
->security_flags
|= SECURITY_FLAG_IGNORE_CERT_CN_INVALID
;
3171 if (dwFlags
& INTERNET_FLAG_IGNORE_CERT_DATE_INVALID
)
3172 request
->security_flags
|= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
;
3174 if (lpszObjectName
&& *lpszObjectName
) {
3178 rc
= UrlEscapeW(lpszObjectName
, NULL
, &len
, URL_ESCAPE_SPACES_ONLY
);
3179 if (rc
!= E_POINTER
)
3180 len
= strlenW(lpszObjectName
)+1;
3181 request
->path
= heap_alloc(len
*sizeof(WCHAR
));
3182 rc
= UrlEscapeW(lpszObjectName
, request
->path
, &len
,
3183 URL_ESCAPE_SPACES_ONLY
);
3186 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName
),rc
);
3187 strcpyW(request
->path
,lpszObjectName
);
3190 static const WCHAR slashW
[] = {'/',0};
3192 request
->path
= heap_strdupW(slashW
);
3195 if (lpszReferrer
&& *lpszReferrer
)
3196 HTTP_ProcessHeader(request
, HTTP_REFERER
, lpszReferrer
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
3198 if (lpszAcceptTypes
)
3201 for (i
= 0; lpszAcceptTypes
[i
]; i
++)
3203 if (!*lpszAcceptTypes
[i
]) continue;
3204 HTTP_ProcessHeader(request
, HTTP_ACCEPT
, lpszAcceptTypes
[i
],
3205 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
|
3206 HTTP_ADDHDR_FLAG_REQ
|
3207 (i
== 0 ? HTTP_ADDHDR_FLAG_REPLACE
: 0));
3211 request
->verb
= heap_strdupW(lpszVerb
&& *lpszVerb
? lpszVerb
: szGET
);
3212 request
->version
= heap_strdupW(lpszVersion
&& *lpszVersion
? lpszVersion
: g_szHttp1_1
);
3214 HTTP_ProcessHeader(request
, hostW
, request
->server
->canon_host_port
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
3216 if (session
->hostPort
== INTERNET_INVALID_PORT_NUMBER
)
3217 session
->hostPort
= INTERNET_DEFAULT_HTTP_PORT
;
3219 if (hIC
->proxy
&& hIC
->proxy
[0])
3220 HTTP_DealWithProxy( hIC
, session
, request
);
3222 INTERNET_SendCallback(&session
->hdr
, dwContext
,
3223 INTERNET_STATUS_HANDLE_CREATED
, &request
->hdr
.hInternet
,
3226 TRACE("<-- %u (%p)\n", res
, request
);
3228 if(res
!= ERROR_SUCCESS
) {
3229 WININET_Release( &request
->hdr
);
3234 *ret
= request
->hdr
.hInternet
;
3235 return ERROR_SUCCESS
;
3238 /***********************************************************************
3239 * HttpOpenRequestW (WININET.@)
3241 * Open a HTTP request handle
3244 * HINTERNET a HTTP request handle on success
3248 HINTERNET WINAPI
HttpOpenRequestW(HINTERNET hHttpSession
,
3249 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
3250 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
3251 DWORD dwFlags
, DWORD_PTR dwContext
)
3253 http_session_t
*session
;
3254 HINTERNET handle
= NULL
;
3257 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
3258 debugstr_w(lpszVerb
), debugstr_w(lpszObjectName
),
3259 debugstr_w(lpszVersion
), debugstr_w(lpszReferrer
), lpszAcceptTypes
,
3260 dwFlags
, dwContext
);
3261 if(lpszAcceptTypes
!=NULL
)
3264 for(i
=0;lpszAcceptTypes
[i
]!=NULL
;i
++)
3265 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes
[i
]));
3268 session
= (http_session_t
*) get_handle_object( hHttpSession
);
3269 if (NULL
== session
|| session
->hdr
.htype
!= WH_HHTTPSESSION
)
3271 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
3276 * My tests seem to show that the windows version does not
3277 * become asynchronous until after this point. And anyhow
3278 * if this call was asynchronous then how would you get the
3279 * necessary HINTERNET pointer returned by this function.
3282 res
= HTTP_HttpOpenRequestW(session
, lpszVerb
, lpszObjectName
,
3283 lpszVersion
, lpszReferrer
, lpszAcceptTypes
,
3284 dwFlags
, dwContext
, &handle
);
3287 WININET_Release( &session
->hdr
);
3288 TRACE("returning %p\n", handle
);
3289 if(res
!= ERROR_SUCCESS
)
3294 static const LPCWSTR header_lookup
[] = {
3295 szMime_Version
, /* HTTP_QUERY_MIME_VERSION = 0 */
3296 szContent_Type
, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3297 szContent_Transfer_Encoding
,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3298 szContent_ID
, /* HTTP_QUERY_CONTENT_ID = 3 */
3299 NULL
, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3300 szContent_Length
, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3301 szContent_Language
, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3302 szAllow
, /* HTTP_QUERY_ALLOW = 7 */
3303 szPublic
, /* HTTP_QUERY_PUBLIC = 8 */
3304 szDate
, /* HTTP_QUERY_DATE = 9 */
3305 szExpires
, /* HTTP_QUERY_EXPIRES = 10 */
3306 szLast_Modified
, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3307 NULL
, /* HTTP_QUERY_MESSAGE_ID = 12 */
3308 szURI
, /* HTTP_QUERY_URI = 13 */
3309 szFrom
, /* HTTP_QUERY_DERIVED_FROM = 14 */
3310 NULL
, /* HTTP_QUERY_COST = 15 */
3311 NULL
, /* HTTP_QUERY_LINK = 16 */
3312 szPragma
, /* HTTP_QUERY_PRAGMA = 17 */
3313 NULL
, /* HTTP_QUERY_VERSION = 18 */
3314 szStatus
, /* HTTP_QUERY_STATUS_CODE = 19 */
3315 NULL
, /* HTTP_QUERY_STATUS_TEXT = 20 */
3316 NULL
, /* HTTP_QUERY_RAW_HEADERS = 21 */
3317 NULL
, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3318 szConnection
, /* HTTP_QUERY_CONNECTION = 23 */
3319 szAccept
, /* HTTP_QUERY_ACCEPT = 24 */
3320 szAccept_Charset
, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3321 szAccept_Encoding
, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3322 szAccept_Language
, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3323 szAuthorization
, /* HTTP_QUERY_AUTHORIZATION = 28 */
3324 szContent_Encoding
, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3325 NULL
, /* HTTP_QUERY_FORWARDED = 30 */
3326 NULL
, /* HTTP_QUERY_FROM = 31 */
3327 szIf_Modified_Since
, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3328 szLocation
, /* HTTP_QUERY_LOCATION = 33 */
3329 NULL
, /* HTTP_QUERY_ORIG_URI = 34 */
3330 szReferer
, /* HTTP_QUERY_REFERER = 35 */
3331 szRetry_After
, /* HTTP_QUERY_RETRY_AFTER = 36 */
3332 szServer
, /* HTTP_QUERY_SERVER = 37 */
3333 NULL
, /* HTTP_TITLE = 38 */
3334 szUser_Agent
, /* HTTP_QUERY_USER_AGENT = 39 */
3335 szWWW_Authenticate
, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3336 szProxy_Authenticate
, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3337 szAccept_Ranges
, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3338 szSet_Cookie
, /* HTTP_QUERY_SET_COOKIE = 43 */
3339 szCookie
, /* HTTP_QUERY_COOKIE = 44 */
3340 NULL
, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3341 NULL
, /* HTTP_QUERY_REFRESH = 46 */
3342 szContent_Disposition
, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3343 szAge
, /* HTTP_QUERY_AGE = 48 */
3344 szCache_Control
, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3345 szContent_Base
, /* HTTP_QUERY_CONTENT_BASE = 50 */
3346 szContent_Location
, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3347 szContent_MD5
, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3348 szContent_Range
, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3349 szETag
, /* HTTP_QUERY_ETAG = 54 */
3350 hostW
, /* HTTP_QUERY_HOST = 55 */
3351 szIf_Match
, /* HTTP_QUERY_IF_MATCH = 56 */
3352 szIf_None_Match
, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3353 szIf_Range
, /* HTTP_QUERY_IF_RANGE = 58 */
3354 szIf_Unmodified_Since
, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3355 szMax_Forwards
, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3356 szProxy_Authorization
, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3357 szRange
, /* HTTP_QUERY_RANGE = 62 */
3358 szTransfer_Encoding
, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3359 szUpgrade
, /* HTTP_QUERY_UPGRADE = 64 */
3360 szVary
, /* HTTP_QUERY_VARY = 65 */
3361 szVia
, /* HTTP_QUERY_VIA = 66 */
3362 szWarning
, /* HTTP_QUERY_WARNING = 67 */
3363 szExpect
, /* HTTP_QUERY_EXPECT = 68 */
3364 szProxy_Connection
, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3365 szUnless_Modified_Since
, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3368 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3370 /***********************************************************************
3371 * HTTP_HttpQueryInfoW (internal)
3373 static DWORD
HTTP_HttpQueryInfoW(http_request_t
*request
, DWORD dwInfoLevel
,
3374 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3376 LPHTTPHEADERW lphttpHdr
= NULL
;
3377 BOOL request_only
= dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
;
3378 INT requested_index
= lpdwIndex
? *lpdwIndex
: 0;
3379 DWORD level
= (dwInfoLevel
& ~HTTP_QUERY_MODIFIER_FLAGS_MASK
);
3382 /* Find requested header structure */
3385 case HTTP_QUERY_CUSTOM
:
3386 if (!lpBuffer
) return ERROR_INVALID_PARAMETER
;
3387 index
= HTTP_GetCustomHeaderIndex(request
, lpBuffer
, requested_index
, request_only
);
3389 case HTTP_QUERY_RAW_HEADERS_CRLF
:
3393 DWORD res
= ERROR_INVALID_PARAMETER
;
3396 headers
= HTTP_BuildHeaderRequestString(request
, request
->verb
, request
->path
, request
->version
);
3398 headers
= request
->rawHeaders
;
3401 len
= strlenW(headers
) * sizeof(WCHAR
);
3403 if (len
+ sizeof(WCHAR
) > *lpdwBufferLength
)
3405 len
+= sizeof(WCHAR
);
3406 res
= ERROR_INSUFFICIENT_BUFFER
;
3411 memcpy(lpBuffer
, headers
, len
+ sizeof(WCHAR
));
3414 len
= strlenW(szCrLf
) * sizeof(WCHAR
);
3415 memcpy(lpBuffer
, szCrLf
, sizeof(szCrLf
));
3417 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
/ sizeof(WCHAR
)));
3418 res
= ERROR_SUCCESS
;
3420 *lpdwBufferLength
= len
;
3422 if (request_only
) heap_free(headers
);
3425 case HTTP_QUERY_RAW_HEADERS
:
3427 LPWSTR
* ppszRawHeaderLines
= HTTP_Tokenize(request
->rawHeaders
, szCrLf
);
3429 LPWSTR pszString
= lpBuffer
;
3431 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
3432 size
+= strlenW(ppszRawHeaderLines
[i
]) + 1;
3434 if (size
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3436 HTTP_FreeTokens(ppszRawHeaderLines
);
3437 *lpdwBufferLength
= (size
+ 1) * sizeof(WCHAR
);
3438 return ERROR_INSUFFICIENT_BUFFER
;
3442 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
3444 DWORD len
= strlenW(ppszRawHeaderLines
[i
]);
3445 memcpy(pszString
, ppszRawHeaderLines
[i
], (len
+1)*sizeof(WCHAR
));
3449 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, size
));
3451 *lpdwBufferLength
= size
* sizeof(WCHAR
);
3452 HTTP_FreeTokens(ppszRawHeaderLines
);
3454 return ERROR_SUCCESS
;
3456 case HTTP_QUERY_STATUS_TEXT
:
3457 if (request
->statusText
)
3459 DWORD len
= strlenW(request
->statusText
);
3460 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3462 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
3463 return ERROR_INSUFFICIENT_BUFFER
;
3467 memcpy(lpBuffer
, request
->statusText
, (len
+ 1) * sizeof(WCHAR
));
3468 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
3470 *lpdwBufferLength
= len
* sizeof(WCHAR
);
3471 return ERROR_SUCCESS
;
3474 case HTTP_QUERY_VERSION
:
3475 if (request
->version
)
3477 DWORD len
= strlenW(request
->version
);
3478 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3480 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
3481 return ERROR_INSUFFICIENT_BUFFER
;
3485 memcpy(lpBuffer
, request
->version
, (len
+ 1) * sizeof(WCHAR
));
3486 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
3488 *lpdwBufferLength
= len
* sizeof(WCHAR
);
3489 return ERROR_SUCCESS
;
3492 case HTTP_QUERY_CONTENT_ENCODING
:
3493 index
= HTTP_GetCustomHeaderIndex(request
, header_lookup
[request
->read_gzip
? HTTP_QUERY_CONTENT_TYPE
: level
],
3494 requested_index
,request_only
);
3496 case HTTP_QUERY_STATUS_CODE
: {
3497 DWORD res
= ERROR_SUCCESS
;
3500 return ERROR_HTTP_INVALID_QUERY_REQUEST
;
3505 if(dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
) {
3506 if(*lpdwBufferLength
>= sizeof(DWORD
))
3507 *(DWORD
*)lpBuffer
= request
->status_code
;
3509 res
= ERROR_INSUFFICIENT_BUFFER
;
3510 *lpdwBufferLength
= sizeof(DWORD
);
3514 static const WCHAR formatW
[] = {'%','u',0};
3516 size
= sprintfW(buf
, formatW
, request
->status_code
) * sizeof(WCHAR
);
3518 if(size
<= *lpdwBufferLength
) {
3519 memcpy(lpBuffer
, buf
, size
+sizeof(WCHAR
));
3521 size
+= sizeof(WCHAR
);
3522 res
= ERROR_INSUFFICIENT_BUFFER
;
3525 *lpdwBufferLength
= size
;
3530 assert (LAST_TABLE_HEADER
== (HTTP_QUERY_UNLESS_MODIFIED_SINCE
+ 1));
3532 if (level
< LAST_TABLE_HEADER
&& header_lookup
[level
])
3533 index
= HTTP_GetCustomHeaderIndex(request
, header_lookup
[level
],
3534 requested_index
,request_only
);
3538 lphttpHdr
= &request
->custHeaders
[index
];
3540 /* Ensure header satisfies requested attributes */
3542 ((dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
) &&
3543 (~lphttpHdr
->wFlags
& HDR_ISREQUEST
)))
3545 return ERROR_HTTP_HEADER_NOT_FOUND
;
3548 if (lpdwIndex
) (*lpdwIndex
)++;
3550 /* coalesce value to requested type */
3551 if (dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
&& lpBuffer
)
3553 *(int *)lpBuffer
= atoiW(lphttpHdr
->lpszValue
);
3554 TRACE(" returning number: %d\n", *(int *)lpBuffer
);
3556 else if (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
&& lpBuffer
)
3562 tmpTime
= ConvertTimeString(lphttpHdr
->lpszValue
);
3564 tmpTM
= *gmtime(&tmpTime
);
3565 STHook
= (SYSTEMTIME
*)lpBuffer
;
3566 STHook
->wDay
= tmpTM
.tm_mday
;
3567 STHook
->wHour
= tmpTM
.tm_hour
;
3568 STHook
->wMilliseconds
= 0;
3569 STHook
->wMinute
= tmpTM
.tm_min
;
3570 STHook
->wDayOfWeek
= tmpTM
.tm_wday
;
3571 STHook
->wMonth
= tmpTM
.tm_mon
+ 1;
3572 STHook
->wSecond
= tmpTM
.tm_sec
;
3573 STHook
->wYear
= 1900+tmpTM
.tm_year
;
3575 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3576 STHook
->wYear
, STHook
->wMonth
, STHook
->wDay
, STHook
->wDayOfWeek
,
3577 STHook
->wHour
, STHook
->wMinute
, STHook
->wSecond
, STHook
->wMilliseconds
);
3579 else if (lphttpHdr
->lpszValue
)
3581 DWORD len
= (strlenW(lphttpHdr
->lpszValue
) + 1) * sizeof(WCHAR
);
3583 if (len
> *lpdwBufferLength
)
3585 *lpdwBufferLength
= len
;
3586 return ERROR_INSUFFICIENT_BUFFER
;
3590 memcpy(lpBuffer
, lphttpHdr
->lpszValue
, len
);
3591 TRACE("! returning string: %s\n", debugstr_w(lpBuffer
));
3593 *lpdwBufferLength
= len
- sizeof(WCHAR
);
3595 return ERROR_SUCCESS
;
3598 /***********************************************************************
3599 * HttpQueryInfoW (WININET.@)
3601 * Queries for information about an HTTP request
3608 BOOL WINAPI
HttpQueryInfoW(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3609 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3611 http_request_t
*request
;
3614 if (TRACE_ON(wininet
)) {
3615 #define FE(x) { x, #x }
3616 static const wininet_flag_info query_flags
[] = {
3617 FE(HTTP_QUERY_MIME_VERSION
),
3618 FE(HTTP_QUERY_CONTENT_TYPE
),
3619 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING
),
3620 FE(HTTP_QUERY_CONTENT_ID
),
3621 FE(HTTP_QUERY_CONTENT_DESCRIPTION
),
3622 FE(HTTP_QUERY_CONTENT_LENGTH
),
3623 FE(HTTP_QUERY_CONTENT_LANGUAGE
),
3624 FE(HTTP_QUERY_ALLOW
),
3625 FE(HTTP_QUERY_PUBLIC
),
3626 FE(HTTP_QUERY_DATE
),
3627 FE(HTTP_QUERY_EXPIRES
),
3628 FE(HTTP_QUERY_LAST_MODIFIED
),
3629 FE(HTTP_QUERY_MESSAGE_ID
),
3631 FE(HTTP_QUERY_DERIVED_FROM
),
3632 FE(HTTP_QUERY_COST
),
3633 FE(HTTP_QUERY_LINK
),
3634 FE(HTTP_QUERY_PRAGMA
),
3635 FE(HTTP_QUERY_VERSION
),
3636 FE(HTTP_QUERY_STATUS_CODE
),
3637 FE(HTTP_QUERY_STATUS_TEXT
),
3638 FE(HTTP_QUERY_RAW_HEADERS
),
3639 FE(HTTP_QUERY_RAW_HEADERS_CRLF
),
3640 FE(HTTP_QUERY_CONNECTION
),
3641 FE(HTTP_QUERY_ACCEPT
),
3642 FE(HTTP_QUERY_ACCEPT_CHARSET
),
3643 FE(HTTP_QUERY_ACCEPT_ENCODING
),
3644 FE(HTTP_QUERY_ACCEPT_LANGUAGE
),
3645 FE(HTTP_QUERY_AUTHORIZATION
),
3646 FE(HTTP_QUERY_CONTENT_ENCODING
),
3647 FE(HTTP_QUERY_FORWARDED
),
3648 FE(HTTP_QUERY_FROM
),
3649 FE(HTTP_QUERY_IF_MODIFIED_SINCE
),
3650 FE(HTTP_QUERY_LOCATION
),
3651 FE(HTTP_QUERY_ORIG_URI
),
3652 FE(HTTP_QUERY_REFERER
),
3653 FE(HTTP_QUERY_RETRY_AFTER
),
3654 FE(HTTP_QUERY_SERVER
),
3655 FE(HTTP_QUERY_TITLE
),
3656 FE(HTTP_QUERY_USER_AGENT
),
3657 FE(HTTP_QUERY_WWW_AUTHENTICATE
),
3658 FE(HTTP_QUERY_PROXY_AUTHENTICATE
),
3659 FE(HTTP_QUERY_ACCEPT_RANGES
),
3660 FE(HTTP_QUERY_SET_COOKIE
),
3661 FE(HTTP_QUERY_COOKIE
),
3662 FE(HTTP_QUERY_REQUEST_METHOD
),
3663 FE(HTTP_QUERY_REFRESH
),
3664 FE(HTTP_QUERY_CONTENT_DISPOSITION
),
3666 FE(HTTP_QUERY_CACHE_CONTROL
),
3667 FE(HTTP_QUERY_CONTENT_BASE
),
3668 FE(HTTP_QUERY_CONTENT_LOCATION
),
3669 FE(HTTP_QUERY_CONTENT_MD5
),
3670 FE(HTTP_QUERY_CONTENT_RANGE
),
3671 FE(HTTP_QUERY_ETAG
),
3672 FE(HTTP_QUERY_HOST
),
3673 FE(HTTP_QUERY_IF_MATCH
),
3674 FE(HTTP_QUERY_IF_NONE_MATCH
),
3675 FE(HTTP_QUERY_IF_RANGE
),
3676 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE
),
3677 FE(HTTP_QUERY_MAX_FORWARDS
),
3678 FE(HTTP_QUERY_PROXY_AUTHORIZATION
),
3679 FE(HTTP_QUERY_RANGE
),
3680 FE(HTTP_QUERY_TRANSFER_ENCODING
),
3681 FE(HTTP_QUERY_UPGRADE
),
3682 FE(HTTP_QUERY_VARY
),
3684 FE(HTTP_QUERY_WARNING
),
3685 FE(HTTP_QUERY_CUSTOM
)
3687 static const wininet_flag_info modifier_flags
[] = {
3688 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS
),
3689 FE(HTTP_QUERY_FLAG_SYSTEMTIME
),
3690 FE(HTTP_QUERY_FLAG_NUMBER
),
3691 FE(HTTP_QUERY_FLAG_COALESCE
)
3694 DWORD info_mod
= dwInfoLevel
& HTTP_QUERY_MODIFIER_FLAGS_MASK
;
3695 DWORD info
= dwInfoLevel
& HTTP_QUERY_HEADER_MASK
;
3698 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest
, dwInfoLevel
, info
);
3699 TRACE(" Attribute:");
3700 for (i
= 0; i
< (sizeof(query_flags
) / sizeof(query_flags
[0])); i
++) {
3701 if (query_flags
[i
].val
== info
) {
3702 TRACE(" %s", query_flags
[i
].name
);
3706 if (i
== (sizeof(query_flags
) / sizeof(query_flags
[0]))) {
3707 TRACE(" Unknown (%08x)", info
);
3710 TRACE(" Modifier:");
3711 for (i
= 0; i
< (sizeof(modifier_flags
) / sizeof(modifier_flags
[0])); i
++) {
3712 if (modifier_flags
[i
].val
& info_mod
) {
3713 TRACE(" %s", modifier_flags
[i
].name
);
3714 info_mod
&= ~ modifier_flags
[i
].val
;
3719 TRACE(" Unknown (%08x)", info_mod
);
3724 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
3725 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
3727 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
3731 if (lpBuffer
== NULL
)
3732 *lpdwBufferLength
= 0;
3733 res
= HTTP_HttpQueryInfoW( request
, dwInfoLevel
,
3734 lpBuffer
, lpdwBufferLength
, lpdwIndex
);
3738 WININET_Release( &request
->hdr
);
3740 TRACE("%u <--\n", res
);
3741 if(res
!= ERROR_SUCCESS
)
3743 return res
== ERROR_SUCCESS
;
3746 /***********************************************************************
3747 * HttpQueryInfoA (WININET.@)
3749 * Queries for information about an HTTP request
3756 BOOL WINAPI
HttpQueryInfoA(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3757 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3763 if((dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
) ||
3764 (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
))
3766 return HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, lpBuffer
,
3767 lpdwBufferLength
, lpdwIndex
);
3773 len
= (*lpdwBufferLength
)*sizeof(WCHAR
);
3774 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3776 alloclen
= MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, NULL
, 0 ) * sizeof(WCHAR
);
3782 bufferW
= heap_alloc(alloclen
);
3783 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3784 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3785 MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, bufferW
, alloclen
/ sizeof(WCHAR
) );
3792 result
= HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, bufferW
,
3796 len
= WideCharToMultiByte( CP_ACP
,0, bufferW
, len
/ sizeof(WCHAR
) + 1,
3797 lpBuffer
, *lpdwBufferLength
, NULL
, NULL
);
3798 *lpdwBufferLength
= len
- 1;
3800 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer
));
3803 /* since the strings being returned from HttpQueryInfoW should be
3804 * only ASCII characters, it is reasonable to assume that all of
3805 * the Unicode characters can be reduced to a single byte */
3806 *lpdwBufferLength
= len
/ sizeof(WCHAR
);
3808 heap_free( bufferW
);
3812 /***********************************************************************
3813 * HTTP_GetRedirectURL (internal)
3815 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*request
, LPCWSTR lpszUrl
)
3817 static WCHAR szHttp
[] = {'h','t','t','p',0};
3818 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3819 http_session_t
*session
= request
->session
;
3820 URL_COMPONENTSW urlComponents
;
3821 DWORD url_length
= 0;
3823 LPWSTR combined_url
;
3825 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3826 urlComponents
.lpszScheme
= (request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) ? szHttps
: szHttp
;
3827 urlComponents
.dwSchemeLength
= 0;
3828 urlComponents
.lpszHostName
= session
->hostName
;
3829 urlComponents
.dwHostNameLength
= 0;
3830 urlComponents
.nPort
= session
->hostPort
;
3831 urlComponents
.lpszUserName
= session
->userName
;
3832 urlComponents
.dwUserNameLength
= 0;
3833 urlComponents
.lpszPassword
= NULL
;
3834 urlComponents
.dwPasswordLength
= 0;
3835 urlComponents
.lpszUrlPath
= request
->path
;
3836 urlComponents
.dwUrlPathLength
= 0;
3837 urlComponents
.lpszExtraInfo
= NULL
;
3838 urlComponents
.dwExtraInfoLength
= 0;
3840 if (!InternetCreateUrlW(&urlComponents
, 0, NULL
, &url_length
) &&
3841 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3844 orig_url
= heap_alloc(url_length
);
3846 /* convert from bytes to characters */
3847 url_length
= url_length
/ sizeof(WCHAR
) - 1;
3848 if (!InternetCreateUrlW(&urlComponents
, 0, orig_url
, &url_length
))
3850 heap_free(orig_url
);
3855 if (!InternetCombineUrlW(orig_url
, lpszUrl
, NULL
, &url_length
, ICU_ENCODE_SPACES_ONLY
) &&
3856 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3858 heap_free(orig_url
);
3861 combined_url
= heap_alloc(url_length
* sizeof(WCHAR
));
3863 if (!InternetCombineUrlW(orig_url
, lpszUrl
, combined_url
, &url_length
, ICU_ENCODE_SPACES_ONLY
))
3865 heap_free(orig_url
);
3866 heap_free(combined_url
);
3869 heap_free(orig_url
);
3870 return combined_url
;
3874 /***********************************************************************
3875 * HTTP_HandleRedirect (internal)
3877 static DWORD
HTTP_HandleRedirect(http_request_t
*request
, LPCWSTR lpszUrl
)
3879 http_session_t
*session
= request
->session
;
3880 WCHAR path
[INTERNET_MAX_PATH_LENGTH
];
3885 /* if it's an absolute path, keep the same session info */
3886 lstrcpynW(path
, lpszUrl
, INTERNET_MAX_URL_LENGTH
);
3890 URL_COMPONENTSW urlComponents
;
3891 WCHAR protocol
[INTERNET_MAX_SCHEME_LENGTH
];
3892 WCHAR hostName
[INTERNET_MAX_HOST_NAME_LENGTH
];
3893 WCHAR userName
[INTERNET_MAX_USER_NAME_LENGTH
];
3894 BOOL custom_port
= FALSE
;
3896 static WCHAR httpW
[] = {'h','t','t','p',0};
3897 static WCHAR httpsW
[] = {'h','t','t','p','s',0};
3903 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3904 urlComponents
.lpszScheme
= protocol
;
3905 urlComponents
.dwSchemeLength
= INTERNET_MAX_SCHEME_LENGTH
;
3906 urlComponents
.lpszHostName
= hostName
;
3907 urlComponents
.dwHostNameLength
= INTERNET_MAX_HOST_NAME_LENGTH
;
3908 urlComponents
.lpszUserName
= userName
;
3909 urlComponents
.dwUserNameLength
= INTERNET_MAX_USER_NAME_LENGTH
;
3910 urlComponents
.lpszPassword
= NULL
;
3911 urlComponents
.dwPasswordLength
= 0;
3912 urlComponents
.lpszUrlPath
= path
;
3913 urlComponents
.dwUrlPathLength
= INTERNET_MAX_PATH_LENGTH
;
3914 urlComponents
.lpszExtraInfo
= NULL
;
3915 urlComponents
.dwExtraInfoLength
= 0;
3916 if(!InternetCrackUrlW(lpszUrl
, strlenW(lpszUrl
), 0, &urlComponents
))
3917 return INTERNET_GetLastError();
3919 if(!strcmpiW(protocol
, httpW
)) {
3920 if(request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) {
3921 TRACE("redirect from secure page to non-secure page\n");
3922 /* FIXME: warn about from secure redirect to non-secure page */
3923 request
->hdr
.dwFlags
&= ~INTERNET_FLAG_SECURE
;
3926 if(urlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
3927 urlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
3928 else if(urlComponents
.nPort
!= INTERNET_DEFAULT_HTTP_PORT
)
3930 }else if(!strcmpiW(protocol
, httpsW
)) {
3931 if(!(request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)) {
3932 TRACE("redirect from non-secure page to secure page\n");
3933 /* FIXME: notify about redirect to secure page */
3934 request
->hdr
.dwFlags
|= INTERNET_FLAG_SECURE
;
3937 if(urlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
3938 urlComponents
.nPort
= INTERNET_DEFAULT_HTTPS_PORT
;
3939 else if(urlComponents
.nPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
3943 heap_free(session
->hostName
);
3947 static const WCHAR fmt
[] = {'%','s',':','%','u',0};
3948 len
= lstrlenW(hostName
);
3949 len
+= 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3950 session
->hostName
= heap_alloc(len
*sizeof(WCHAR
));
3951 sprintfW(session
->hostName
, fmt
, hostName
, urlComponents
.nPort
);
3954 session
->hostName
= heap_strdupW(hostName
);
3956 HTTP_ProcessHeader(request
, hostW
, session
->hostName
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDREQ_FLAG_REPLACE
| HTTP_ADDHDR_FLAG_REQ
);
3958 heap_free(session
->userName
);
3959 session
->userName
= NULL
;
3961 session
->userName
= heap_strdupW(userName
);
3963 reset_data_stream(request
);
3965 if(strcmpiW(request
->server
->name
, hostName
) || request
->server
->port
!= urlComponents
.nPort
) {
3966 server_t
*new_server
;
3968 new_server
= get_server(hostName
, urlComponents
.nPort
, urlComponents
.nScheme
== INTERNET_SCHEME_HTTPS
, TRUE
);
3969 server_release(request
->server
);
3970 request
->server
= new_server
;
3973 heap_free(request
->path
);
3980 rc
= UrlEscapeW(path
, NULL
, &needed
, URL_ESCAPE_SPACES_ONLY
);
3981 if (rc
!= E_POINTER
)
3982 needed
= strlenW(path
)+1;
3983 request
->path
= heap_alloc(needed
*sizeof(WCHAR
));
3984 rc
= UrlEscapeW(path
, request
->path
, &needed
,
3985 URL_ESCAPE_SPACES_ONLY
);
3988 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path
),rc
);
3989 strcpyW(request
->path
,path
);
3993 /* Remove custom content-type/length headers on redirects. */
3994 index
= HTTP_GetCustomHeaderIndex(request
, szContent_Type
, 0, TRUE
);
3996 HTTP_DeleteCustomHeader(request
, index
);
3997 index
= HTTP_GetCustomHeaderIndex(request
, szContent_Length
, 0, TRUE
);
3999 HTTP_DeleteCustomHeader(request
, index
);
4001 return ERROR_SUCCESS
;
4004 /***********************************************************************
4005 * HTTP_build_req (internal)
4007 * concatenate all the strings in the request together
4009 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
)
4014 for( t
= list
; *t
; t
++ )
4015 len
+= strlenW( *t
);
4018 str
= heap_alloc(len
*sizeof(WCHAR
));
4021 for( t
= list
; *t
; t
++ )
4027 static DWORD
HTTP_SecureProxyConnect(http_request_t
*request
)
4029 server_t
*server
= request
->server
;
4030 LPWSTR requestString
;
4037 static const WCHAR connectW
[] = {'C','O','N','N','E','C','T',0};
4041 requestString
= HTTP_BuildHeaderRequestString( request
, connectW
, server
->host_port
, g_szHttp1_1
);
4043 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
4044 NULL
, 0, NULL
, NULL
);
4045 len
--; /* the nul terminator isn't needed */
4046 ascii_req
= heap_alloc(len
);
4047 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1, ascii_req
, len
, NULL
, NULL
);
4048 heap_free( requestString
);
4050 TRACE("full request -> %s\n", debugstr_an( ascii_req
, len
) );
4052 NETCON_set_timeout( request
->netconn
, TRUE
, request
->send_timeout
);
4053 res
= NETCON_send( request
->netconn
, ascii_req
, len
, 0, &cnt
);
4054 heap_free( ascii_req
);
4055 if (res
!= ERROR_SUCCESS
)
4058 responseLen
= HTTP_GetResponseHeaders( request
, TRUE
);
4060 return ERROR_HTTP_INVALID_HEADER
;
4062 return ERROR_SUCCESS
;
4065 static void HTTP_InsertCookies(http_request_t
*request
)
4067 DWORD cookie_size
, size
, cnt
= 0;
4071 static const WCHAR cookieW
[] = {'C','o','o','k','i','e',':',' ',0};
4073 host
= HTTP_GetHeader(request
, hostW
);
4077 if(get_cookie(host
->lpszValue
, request
->path
, NULL
, &cookie_size
) != ERROR_SUCCESS
)
4080 size
= sizeof(cookieW
) + cookie_size
* sizeof(WCHAR
) + sizeof(szCrLf
);
4081 if(!(cookies
= heap_alloc(size
)))
4084 cnt
+= sprintfW(cookies
, cookieW
);
4085 get_cookie(host
->lpszValue
, request
->path
, cookies
+cnt
, &cookie_size
);
4086 strcatW(cookies
, szCrLf
);
4088 HTTP_HttpAddRequestHeadersW(request
, cookies
, strlenW(cookies
), HTTP_ADDREQ_FLAG_REPLACE
);
4093 static WORD
HTTP_ParseWkday(LPCWSTR day
)
4095 static const WCHAR days
[7][4] = {{ 's','u','n',0 },
4103 for (i
= 0; i
< sizeof(days
)/sizeof(*days
); i
++)
4104 if (!strcmpiW(day
, days
[i
]))
4111 static WORD
HTTP_ParseMonth(LPCWSTR month
)
4113 static const WCHAR jan
[] = { 'j','a','n',0 };
4114 static const WCHAR feb
[] = { 'f','e','b',0 };
4115 static const WCHAR mar
[] = { 'm','a','r',0 };
4116 static const WCHAR apr
[] = { 'a','p','r',0 };
4117 static const WCHAR may
[] = { 'm','a','y',0 };
4118 static const WCHAR jun
[] = { 'j','u','n',0 };
4119 static const WCHAR jul
[] = { 'j','u','l',0 };
4120 static const WCHAR aug
[] = { 'a','u','g',0 };
4121 static const WCHAR sep
[] = { 's','e','p',0 };
4122 static const WCHAR oct
[] = { 'o','c','t',0 };
4123 static const WCHAR nov
[] = { 'n','o','v',0 };
4124 static const WCHAR dec
[] = { 'd','e','c',0 };
4126 if (!strcmpiW(month
, jan
)) return 1;
4127 if (!strcmpiW(month
, feb
)) return 2;
4128 if (!strcmpiW(month
, mar
)) return 3;
4129 if (!strcmpiW(month
, apr
)) return 4;
4130 if (!strcmpiW(month
, may
)) return 5;
4131 if (!strcmpiW(month
, jun
)) return 6;
4132 if (!strcmpiW(month
, jul
)) return 7;
4133 if (!strcmpiW(month
, aug
)) return 8;
4134 if (!strcmpiW(month
, sep
)) return 9;
4135 if (!strcmpiW(month
, oct
)) return 10;
4136 if (!strcmpiW(month
, nov
)) return 11;
4137 if (!strcmpiW(month
, dec
)) return 12;
4142 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4143 * optionally preceded by whitespace.
4144 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4145 * st, and sets *str to the first character after the time format.
4147 static BOOL
HTTP_ParseTime(SYSTEMTIME
*st
, LPCWSTR
*str
)
4153 while (isspaceW(*ptr
))
4156 num
= strtoulW(ptr
, &nextPtr
, 10);
4157 if (!nextPtr
|| nextPtr
<= ptr
|| *nextPtr
!= ':')
4159 ERR("unexpected time format %s\n", debugstr_w(ptr
));
4164 ERR("unexpected hour in time format %s\n", debugstr_w(ptr
));
4168 st
->wHour
= (WORD
)num
;
4169 num
= strtoulW(ptr
, &nextPtr
, 10);
4170 if (!nextPtr
|| nextPtr
<= ptr
|| *nextPtr
!= ':')
4172 ERR("unexpected time format %s\n", debugstr_w(ptr
));
4177 ERR("unexpected minute in time format %s\n", debugstr_w(ptr
));
4181 st
->wMinute
= (WORD
)num
;
4182 num
= strtoulW(ptr
, &nextPtr
, 10);
4183 if (!nextPtr
|| nextPtr
<= ptr
)
4185 ERR("unexpected time format %s\n", debugstr_w(ptr
));
4190 ERR("unexpected second in time format %s\n", debugstr_w(ptr
));
4195 st
->wSecond
= (WORD
)num
;
4199 static BOOL
HTTP_ParseDateAsAsctime(LPCWSTR value
, FILETIME
*ft
)
4201 static const WCHAR gmt
[]= { 'G','M','T',0 };
4202 WCHAR day
[4], *dayPtr
, month
[4], *monthPtr
, *nextPtr
;
4204 SYSTEMTIME st
= { 0 };
4207 for (ptr
= value
, dayPtr
= day
; *ptr
&& !isspaceW(*ptr
) &&
4208 dayPtr
- day
< sizeof(day
) / sizeof(day
[0]) - 1; ptr
++, dayPtr
++)
4211 st
.wDayOfWeek
= HTTP_ParseWkday(day
);
4212 if (st
.wDayOfWeek
>= 7)
4214 ERR("unexpected weekday %s\n", debugstr_w(day
));
4218 while (isspaceW(*ptr
))
4221 for (monthPtr
= month
; !isspace(*ptr
) &&
4222 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
4226 st
.wMonth
= HTTP_ParseMonth(month
);
4227 if (!st
.wMonth
|| st
.wMonth
> 12)
4229 ERR("unexpected month %s\n", debugstr_w(month
));
4233 while (isspaceW(*ptr
))
4236 num
= strtoulW(ptr
, &nextPtr
, 10);
4237 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
4239 ERR("unexpected day %s\n", debugstr_w(ptr
));
4243 st
.wDay
= (WORD
)num
;
4245 while (isspaceW(*ptr
))
4248 if (!HTTP_ParseTime(&st
, &ptr
))
4251 while (isspaceW(*ptr
))
4254 num
= strtoulW(ptr
, &nextPtr
, 10);
4255 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
4257 ERR("unexpected year %s\n", debugstr_w(ptr
));
4261 st
.wYear
= (WORD
)num
;
4263 while (isspaceW(*ptr
))
4266 /* asctime() doesn't report a timezone, but some web servers do, so accept
4267 * with or without GMT.
4269 if (*ptr
&& strcmpW(ptr
, gmt
))
4271 ERR("unexpected timezone %s\n", debugstr_w(ptr
));
4274 return SystemTimeToFileTime(&st
, ft
);
4277 static BOOL
HTTP_ParseRfc1123Date(LPCWSTR value
, FILETIME
*ft
)
4279 static const WCHAR gmt
[]= { 'G','M','T',0 };
4280 WCHAR
*nextPtr
, day
[4], month
[4], *monthPtr
;
4283 SYSTEMTIME st
= { 0 };
4285 ptr
= strchrW(value
, ',');
4288 if (ptr
- value
!= 3)
4290 WARN("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4293 memcpy(day
, value
, (ptr
- value
) * sizeof(WCHAR
));
4295 st
.wDayOfWeek
= HTTP_ParseWkday(day
);
4296 if (st
.wDayOfWeek
> 6)
4298 WARN("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4303 while (isspaceW(*ptr
))
4306 num
= strtoulW(ptr
, &nextPtr
, 10);
4307 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
4309 WARN("unexpected day %s\n", debugstr_w(value
));
4313 st
.wDay
= (WORD
)num
;
4315 while (isspaceW(*ptr
))
4318 for (monthPtr
= month
; !isspace(*ptr
) &&
4319 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
4323 st
.wMonth
= HTTP_ParseMonth(month
);
4324 if (!st
.wMonth
|| st
.wMonth
> 12)
4326 WARN("unexpected month %s\n", debugstr_w(month
));
4330 while (isspaceW(*ptr
))
4333 num
= strtoulW(ptr
, &nextPtr
, 10);
4334 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
4336 ERR("unexpected year %s\n", debugstr_w(value
));
4340 st
.wYear
= (WORD
)num
;
4342 if (!HTTP_ParseTime(&st
, &ptr
))
4345 while (isspaceW(*ptr
))
4348 if (strcmpW(ptr
, gmt
))
4350 ERR("unexpected time zone %s\n", debugstr_w(ptr
));
4353 return SystemTimeToFileTime(&st
, ft
);
4356 static WORD
HTTP_ParseWeekday(LPCWSTR day
)
4358 static const WCHAR days
[7][10] = {{ 's','u','n','d','a','y',0 },
4359 { 'm','o','n','d','a','y',0 },
4360 { 't','u','e','s','d','a','y',0 },
4361 { 'w','e','d','n','e','s','d','a','y',0 },
4362 { 't','h','u','r','s','d','a','y',0 },
4363 { 'f','r','i','d','a','y',0 },
4364 { 's','a','t','u','r','d','a','y',0 }};
4366 for (i
= 0; i
< sizeof(days
)/sizeof(*days
); i
++)
4367 if (!strcmpiW(day
, days
[i
]))
4374 static BOOL
HTTP_ParseRfc850Date(LPCWSTR value
, FILETIME
*ft
)
4376 static const WCHAR gmt
[]= { 'G','M','T',0 };
4377 WCHAR
*nextPtr
, day
[10], month
[4], *monthPtr
;
4380 SYSTEMTIME st
= { 0 };
4382 ptr
= strchrW(value
, ',');
4385 if (ptr
- value
== 3)
4387 memcpy(day
, value
, (ptr
- value
) * sizeof(WCHAR
));
4389 st
.wDayOfWeek
= HTTP_ParseWkday(day
);
4390 if (st
.wDayOfWeek
> 6)
4392 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4396 else if (ptr
- value
< sizeof(day
) / sizeof(day
[0]))
4398 memcpy(day
, value
, (ptr
- value
) * sizeof(WCHAR
));
4399 day
[ptr
- value
+ 1] = 0;
4400 st
.wDayOfWeek
= HTTP_ParseWeekday(day
);
4401 if (st
.wDayOfWeek
> 6)
4403 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4409 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4414 while (isspaceW(*ptr
))
4417 num
= strtoulW(ptr
, &nextPtr
, 10);
4418 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
4420 ERR("unexpected day %s\n", debugstr_w(value
));
4424 st
.wDay
= (WORD
)num
;
4428 ERR("unexpected month format %s\n", debugstr_w(ptr
));
4433 for (monthPtr
= month
; *ptr
!= '-' &&
4434 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
4438 st
.wMonth
= HTTP_ParseMonth(month
);
4439 if (!st
.wMonth
|| st
.wMonth
> 12)
4441 ERR("unexpected month %s\n", debugstr_w(month
));
4447 ERR("unexpected year format %s\n", debugstr_w(ptr
));
4452 num
= strtoulW(ptr
, &nextPtr
, 10);
4453 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
4455 ERR("unexpected year %s\n", debugstr_w(value
));
4459 st
.wYear
= (WORD
)num
;
4461 if (!HTTP_ParseTime(&st
, &ptr
))
4464 while (isspaceW(*ptr
))
4467 if (strcmpW(ptr
, gmt
))
4469 ERR("unexpected time zone %s\n", debugstr_w(ptr
));
4472 return SystemTimeToFileTime(&st
, ft
);
4475 static BOOL
HTTP_ParseDate(LPCWSTR value
, FILETIME
*ft
)
4477 static const WCHAR zero
[] = { '0',0 };
4480 if (!strcmpW(value
, zero
))
4482 ft
->dwLowDateTime
= ft
->dwHighDateTime
= 0;
4485 else if (strchrW(value
, ','))
4487 ret
= HTTP_ParseRfc1123Date(value
, ft
);
4490 ret
= HTTP_ParseRfc850Date(value
, ft
);
4492 ERR("unexpected date format %s\n", debugstr_w(value
));
4497 ret
= HTTP_ParseDateAsAsctime(value
, ft
);
4499 ERR("unexpected date format %s\n", debugstr_w(value
));
4504 static void HTTP_ProcessExpires(http_request_t
*request
)
4506 BOOL expirationFound
= FALSE
;
4509 /* Look for a Cache-Control header with a max-age directive, as it takes
4510 * precedence over the Expires header.
4512 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szCache_Control
, 0, FALSE
);
4513 if (headerIndex
!= -1)
4515 LPHTTPHEADERW ccHeader
= &request
->custHeaders
[headerIndex
];
4518 for (ptr
= ccHeader
->lpszValue
; ptr
&& *ptr
; )
4520 LPWSTR comma
= strchrW(ptr
, ','), end
, equal
;
4525 end
= ptr
+ strlenW(ptr
);
4526 for (equal
= end
- 1; equal
> ptr
&& *equal
!= '='; equal
--)
4530 static const WCHAR max_age
[] = {
4531 'm','a','x','-','a','g','e',0 };
4533 if (!strncmpiW(ptr
, max_age
, equal
- ptr
- 1))
4538 age
= strtoulW(equal
+ 1, &nextPtr
, 10);
4539 if (nextPtr
> equal
+ 1)
4543 NtQuerySystemTime( &ft
);
4544 /* Age is in seconds, FILETIME resolution is in
4545 * 100 nanosecond intervals.
4547 ft
.QuadPart
+= age
* (ULONGLONG
)1000000;
4548 request
->expires
.dwLowDateTime
= ft
.u
.LowPart
;
4549 request
->expires
.dwHighDateTime
= ft
.u
.HighPart
;
4550 expirationFound
= TRUE
;
4557 while (isspaceW(*ptr
))
4564 if (!expirationFound
)
4566 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szExpires
, 0, FALSE
);
4567 if (headerIndex
!= -1)
4569 LPHTTPHEADERW expiresHeader
= &request
->custHeaders
[headerIndex
];
4572 if (HTTP_ParseDate(expiresHeader
->lpszValue
, &ft
))
4574 expirationFound
= TRUE
;
4575 request
->expires
= ft
;
4579 if (!expirationFound
)
4583 /* With no known age, default to 10 minutes until expiration. */
4584 NtQuerySystemTime( &t
);
4585 t
.QuadPart
+= 10 * 60 * (ULONGLONG
)10000000;
4586 request
->expires
.dwLowDateTime
= t
.u
.LowPart
;
4587 request
->expires
.dwHighDateTime
= t
.u
.HighPart
;
4591 static void HTTP_ProcessLastModified(http_request_t
*request
)
4595 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szLast_Modified
, 0, FALSE
);
4596 if (headerIndex
!= -1)
4598 LPHTTPHEADERW expiresHeader
= &request
->custHeaders
[headerIndex
];
4601 if (HTTP_ParseDate(expiresHeader
->lpszValue
, &ft
))
4602 request
->last_modified
= ft
;
4606 static void http_process_keep_alive(http_request_t
*req
)
4610 index
= HTTP_GetCustomHeaderIndex(req
, szConnection
, 0, FALSE
);
4612 req
->netconn
->keep_alive
= !strcmpiW(req
->custHeaders
[index
].lpszValue
, szKeepAlive
);
4614 req
->netconn
->keep_alive
= !strcmpiW(req
->version
, g_szHttp1_1
);
4617 static DWORD
open_http_connection(http_request_t
*request
, BOOL
*reusing
)
4619 const BOOL is_https
= (request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) != 0;
4620 netconn_t
*netconn
= NULL
;
4623 assert(!request
->netconn
);
4624 reset_data_stream(request
);
4626 res
= HTTP_ResolveName(request
);
4627 if(res
!= ERROR_SUCCESS
)
4630 EnterCriticalSection(&connection_pool_cs
);
4632 while(!list_empty(&request
->server
->conn_pool
)) {
4633 netconn
= LIST_ENTRY(list_head(&request
->server
->conn_pool
), netconn_t
, pool_entry
);
4634 list_remove(&netconn
->pool_entry
);
4636 if(NETCON_is_alive(netconn
))
4639 TRACE("connection %p closed during idle\n", netconn
);
4640 free_netconn(netconn
);
4644 LeaveCriticalSection(&connection_pool_cs
);
4647 TRACE("<-- reusing %p netconn\n", netconn
);
4648 request
->netconn
= netconn
;
4650 return ERROR_SUCCESS
;
4653 TRACE("connecting to %s, proxy %s\n", debugstr_w(request
->server
->name
),
4654 request
->proxy
? debugstr_w(request
->proxy
->name
) : "(null)");
4656 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4657 INTERNET_STATUS_CONNECTING_TO_SERVER
,
4658 request
->server
->addr_str
,
4659 strlen(request
->server
->addr_str
)+1);
4661 res
= create_netconn(is_https
, request
->proxy
? request
->proxy
: request
->server
, request
->security_flags
,
4662 (request
->hdr
.ErrorMask
& INTERNET_ERROR_MASK_COMBINED_SEC_CERT
) != 0,
4663 request
->connect_timeout
, &netconn
);
4664 if(res
!= ERROR_SUCCESS
) {
4665 ERR("create_netconn failed: %u\n", res
);
4669 request
->netconn
= netconn
;
4671 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4672 INTERNET_STATUS_CONNECTED_TO_SERVER
,
4673 request
->server
->addr_str
, strlen(request
->server
->addr_str
)+1);
4676 /* Note: we differ from Microsoft's WinINet here. they seem to have
4677 * a bug that causes no status callbacks to be sent when starting
4678 * a tunnel to a proxy server using the CONNECT verb. i believe our
4679 * behaviour to be more correct and to not cause any incompatibilities
4680 * because using a secure connection through a proxy server is a rare
4681 * case that would be hard for anyone to depend on */
4683 res
= HTTP_SecureProxyConnect(request
);
4684 if(res
== ERROR_SUCCESS
)
4685 res
= NETCON_secure_connect(request
->netconn
, request
->server
);
4688 if(res
!= ERROR_SUCCESS
) {
4689 http_release_netconn(request
, FALSE
);
4694 TRACE("Created connection to %s: %p\n", debugstr_w(request
->server
->name
), netconn
);
4695 return ERROR_SUCCESS
;
4698 /***********************************************************************
4699 * HTTP_HttpSendRequestW (internal)
4701 * Sends the specified request to the HTTP server
4704 * ERROR_SUCCESS on success
4705 * win32 error code on failure
4708 static DWORD
HTTP_HttpSendRequestW(http_request_t
*request
, LPCWSTR lpszHeaders
,
4709 DWORD dwHeaderLength
, LPVOID lpOptional
, DWORD dwOptionalLength
,
4710 DWORD dwContentLength
, BOOL bEndRequest
)
4713 BOOL redirected
= FALSE
;
4714 LPWSTR requestString
= NULL
;
4717 static const WCHAR szPost
[] = { 'P','O','S','T',0 };
4718 static const WCHAR szContentLength
[] =
4719 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4720 WCHAR contentLengthStr
[sizeof szContentLength
/2 /* includes \r\n */ + 20 /* int */ ];
4723 TRACE("--> %p\n", request
);
4725 assert(request
->hdr
.htype
== WH_HHTTPREQ
);
4727 /* if the verb is NULL default to GET */
4729 request
->verb
= heap_strdupW(szGET
);
4731 if (dwContentLength
|| strcmpW(request
->verb
, szGET
))
4733 sprintfW(contentLengthStr
, szContentLength
, dwContentLength
);
4734 HTTP_HttpAddRequestHeadersW(request
, contentLengthStr
, -1L, HTTP_ADDREQ_FLAG_REPLACE
);
4735 request
->bytesToWrite
= dwContentLength
;
4737 if (request
->session
->appInfo
->agent
)
4739 WCHAR
*agent_header
;
4740 static const WCHAR user_agent
[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4743 len
= strlenW(request
->session
->appInfo
->agent
) + strlenW(user_agent
);
4744 agent_header
= heap_alloc(len
* sizeof(WCHAR
));
4745 sprintfW(agent_header
, user_agent
, request
->session
->appInfo
->agent
);
4747 HTTP_HttpAddRequestHeadersW(request
, agent_header
, strlenW(agent_header
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4748 heap_free(agent_header
);
4750 if (request
->hdr
.dwFlags
& INTERNET_FLAG_PRAGMA_NOCACHE
)
4752 static const WCHAR pragma_nocache
[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4753 HTTP_HttpAddRequestHeadersW(request
, pragma_nocache
, strlenW(pragma_nocache
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4755 if ((request
->hdr
.dwFlags
& INTERNET_FLAG_NO_CACHE_WRITE
) && !strcmpW(request
->verb
, szPost
))
4757 static const WCHAR cache_control
[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4758 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4759 HTTP_HttpAddRequestHeadersW(request
, cache_control
, strlenW(cache_control
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4762 /* add the headers the caller supplied */
4763 if( lpszHeaders
&& dwHeaderLength
)
4764 HTTP_HttpAddRequestHeadersW(request
, lpszHeaders
, dwHeaderLength
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REPLACE
);
4769 BOOL reusing_connection
;
4773 reusing_connection
= request
->netconn
!= NULL
;
4776 request
->contentLength
= ~0u;
4777 request
->bytesToWrite
= 0;
4780 if (TRACE_ON(wininet
))
4782 LPHTTPHEADERW Host
= HTTP_GetHeader(request
, hostW
);
4783 TRACE("Going to url %s %s\n", debugstr_w(Host
->lpszValue
), debugstr_w(request
->path
));
4786 HTTP_FixURL(request
);
4787 if (request
->hdr
.dwFlags
& INTERNET_FLAG_KEEP_CONNECTION
)
4789 HTTP_ProcessHeader(request
, szConnection
, szKeepAlive
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
4791 HTTP_InsertAuthorization(request
, request
->authInfo
, szAuthorization
);
4792 HTTP_InsertAuthorization(request
, request
->proxyAuthInfo
, szProxy_Authorization
);
4794 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
))
4795 HTTP_InsertCookies(request
);
4799 WCHAR
*url
= build_proxy_path_url(request
);
4800 requestString
= HTTP_BuildHeaderRequestString(request
, request
->verb
, url
, request
->version
);
4804 requestString
= HTTP_BuildHeaderRequestString(request
, request
->verb
, request
->path
, request
->version
);
4807 TRACE("Request header -> %s\n", debugstr_w(requestString
) );
4809 if (!reusing_connection
&& (res
= open_http_connection(request
, &reusing_connection
)) != ERROR_SUCCESS
)
4812 /* send the request as ASCII, tack on the optional data */
4813 if (!lpOptional
|| redirected
)
4814 dwOptionalLength
= 0;
4815 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
4816 NULL
, 0, NULL
, NULL
);
4817 ascii_req
= heap_alloc(len
+ dwOptionalLength
);
4818 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
4819 ascii_req
, len
, NULL
, NULL
);
4821 memcpy( &ascii_req
[len
-1], lpOptional
, dwOptionalLength
);
4822 len
= (len
+ dwOptionalLength
- 1);
4824 TRACE("full request -> %s\n", debugstr_a(ascii_req
) );
4826 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4827 INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
4829 NETCON_set_timeout( request
->netconn
, TRUE
, request
->send_timeout
);
4830 res
= NETCON_send(request
->netconn
, ascii_req
, len
, 0, &cnt
);
4831 heap_free( ascii_req
);
4832 if(res
!= ERROR_SUCCESS
) {
4833 TRACE("send failed: %u\n", res
);
4834 if(!reusing_connection
)
4836 http_release_netconn(request
, FALSE
);
4841 request
->bytesWritten
= dwOptionalLength
;
4843 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4844 INTERNET_STATUS_REQUEST_SENT
,
4845 &len
, sizeof(DWORD
));
4851 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4852 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
4854 responseLen
= HTTP_GetResponseHeaders(request
, TRUE
);
4855 /* FIXME: We should know that connection is closed before sending
4856 * headers. Otherwise wrong callbacks are executed */
4857 if(!responseLen
&& reusing_connection
) {
4858 TRACE("Connection closed by server, reconnecting\n");
4859 http_release_netconn(request
, FALSE
);
4864 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4865 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
,
4868 http_process_keep_alive(request
);
4869 HTTP_ProcessCookies(request
);
4870 HTTP_ProcessExpires(request
);
4871 HTTP_ProcessLastModified(request
);
4873 res
= set_content_length(request
);
4874 if(res
!= ERROR_SUCCESS
)
4876 if(!request
->contentLength
)
4877 http_release_netconn(request
, TRUE
);
4879 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
) && responseLen
)
4881 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
4882 dwBufferSize
=sizeof(szNewLocation
);
4883 switch(request
->status_code
) {
4884 case HTTP_STATUS_REDIRECT
:
4885 case HTTP_STATUS_MOVED
:
4886 case HTTP_STATUS_REDIRECT_KEEP_VERB
:
4887 case HTTP_STATUS_REDIRECT_METHOD
:
4888 if(HTTP_HttpQueryInfoW(request
,HTTP_QUERY_LOCATION
,szNewLocation
,&dwBufferSize
,NULL
) != ERROR_SUCCESS
)
4891 if (strcmpW(request
->verb
, szGET
) && strcmpW(request
->verb
, szHEAD
) &&
4892 request
->status_code
!= HTTP_STATUS_REDIRECT_KEEP_VERB
)
4894 heap_free(request
->verb
);
4895 request
->verb
= heap_strdupW(szGET
);
4897 http_release_netconn(request
, drain_content(request
, FALSE
));
4898 if ((new_url
= HTTP_GetRedirectURL( request
, szNewLocation
)))
4900 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
4901 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
4902 res
= HTTP_HandleRedirect(request
, new_url
);
4903 if (res
== ERROR_SUCCESS
)
4905 heap_free(requestString
);
4908 heap_free( new_url
);
4913 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTH
) && res
== ERROR_SUCCESS
)
4915 WCHAR szAuthValue
[2048];
4917 if (request
->status_code
== HTTP_STATUS_DENIED
)
4919 LPHTTPHEADERW Host
= HTTP_GetHeader(request
, hostW
);
4921 while (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_WWW_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
4923 if (HTTP_DoAuthorization(request
, szAuthValue
,
4925 request
->session
->userName
,
4926 request
->session
->password
,
4929 heap_free(requestString
);
4930 if(!drain_content(request
, TRUE
)) {
4931 FIXME("Could not drain content\n");
4932 http_release_netconn(request
, FALSE
);
4940 TRACE("Cleaning wrong authorization data\n");
4941 destroy_authinfo(request
->authInfo
);
4942 request
->authInfo
= NULL
;
4945 if (request
->status_code
== HTTP_STATUS_PROXY_AUTH_REQ
)
4948 while (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_PROXY_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
4950 if (HTTP_DoAuthorization(request
, szAuthValue
,
4951 &request
->proxyAuthInfo
,
4952 request
->session
->appInfo
->proxyUsername
,
4953 request
->session
->appInfo
->proxyPassword
,
4956 heap_free(requestString
);
4957 if(!drain_content(request
, TRUE
)) {
4958 FIXME("Could not drain content\n");
4959 http_release_netconn(request
, FALSE
);
4967 TRACE("Cleaning wrong proxy authorization data\n");
4968 destroy_authinfo(request
->proxyAuthInfo
);
4969 request
->proxyAuthInfo
= NULL
;
4975 res
= ERROR_SUCCESS
;
4980 heap_free(requestString
);
4982 /* TODO: send notification for P3P header */
4984 if(res
== ERROR_SUCCESS
)
4985 create_cache_entry(request
);
4987 if (request
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4989 if (res
== ERROR_SUCCESS
) {
4990 if(bEndRequest
&& request
->contentLength
&& request
->bytesWritten
== request
->bytesToWrite
)
4991 HTTP_ReceiveRequestData(request
, TRUE
, NULL
);
4993 send_request_complete(request
,
4994 request
->session
->hdr
.dwInternalFlags
& INET_OPENURL
? (DWORD_PTR
)request
->hdr
.hInternet
: 1, 0);
4996 send_request_complete(request
, 0, res
);
5012 } send_request_task_t
;
5014 /***********************************************************************
5016 * Helper functions for the HttpSendRequest(Ex) functions
5019 static void AsyncHttpSendRequestProc(task_header_t
*hdr
)
5021 send_request_task_t
*task
= (send_request_task_t
*)hdr
;
5022 http_request_t
*request
= (http_request_t
*)task
->hdr
.hdr
;
5024 TRACE("%p\n", request
);
5026 HTTP_HttpSendRequestW(request
, task
->headers
, task
->headers_len
, task
->optional
,
5027 task
->optional_len
, task
->content_len
, task
->end_request
);
5029 heap_free(task
->headers
);
5033 static DWORD
HTTP_HttpEndRequestW(http_request_t
*request
, DWORD dwFlags
, DWORD_PTR dwContext
)
5037 DWORD res
= ERROR_SUCCESS
;
5039 if(!request
->netconn
) {
5040 WARN("Not connected\n");
5041 send_request_complete(request
, 0, ERROR_INTERNET_OPERATION_CANCELLED
);
5042 return ERROR_INTERNET_OPERATION_CANCELLED
;
5045 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
5046 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
5048 responseLen
= HTTP_GetResponseHeaders(request
, TRUE
);
5050 res
= ERROR_HTTP_HEADER_NOT_FOUND
;
5052 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
5053 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
, sizeof(DWORD
));
5055 /* process cookies here. Is this right? */
5056 http_process_keep_alive(request
);
5057 HTTP_ProcessCookies(request
);
5058 HTTP_ProcessExpires(request
);
5059 HTTP_ProcessLastModified(request
);
5061 if ((res
= set_content_length(request
)) == ERROR_SUCCESS
) {
5062 if(!request
->contentLength
)
5063 http_release_netconn(request
, TRUE
);
5066 if (res
== ERROR_SUCCESS
&& !(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
))
5068 switch(request
->status_code
) {
5069 case HTTP_STATUS_REDIRECT
:
5070 case HTTP_STATUS_MOVED
:
5071 case HTTP_STATUS_REDIRECT_METHOD
:
5072 case HTTP_STATUS_REDIRECT_KEEP_VERB
: {
5073 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
5074 dwBufferSize
=sizeof(szNewLocation
);
5075 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_LOCATION
, szNewLocation
, &dwBufferSize
, NULL
) != ERROR_SUCCESS
)
5078 if (strcmpW(request
->verb
, szGET
) && strcmpW(request
->verb
, szHEAD
) &&
5079 request
->status_code
!= HTTP_STATUS_REDIRECT_KEEP_VERB
)
5081 heap_free(request
->verb
);
5082 request
->verb
= heap_strdupW(szGET
);
5084 http_release_netconn(request
, drain_content(request
, FALSE
));
5085 if ((new_url
= HTTP_GetRedirectURL( request
, szNewLocation
)))
5087 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
5088 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
5089 res
= HTTP_HandleRedirect(request
, new_url
);
5090 if (res
== ERROR_SUCCESS
)
5091 res
= HTTP_HttpSendRequestW(request
, NULL
, 0, NULL
, 0, 0, TRUE
);
5092 heap_free( new_url
);
5098 if(res
== ERROR_SUCCESS
)
5099 create_cache_entry(request
);
5101 if (res
== ERROR_SUCCESS
&& request
->contentLength
)
5102 HTTP_ReceiveRequestData(request
, TRUE
, NULL
);
5104 send_request_complete(request
, res
== ERROR_SUCCESS
, res
);
5109 /***********************************************************************
5110 * HttpEndRequestA (WININET.@)
5112 * Ends an HTTP request that was started by HttpSendRequestEx
5115 * TRUE if successful
5119 BOOL WINAPI
HttpEndRequestA(HINTERNET hRequest
,
5120 LPINTERNET_BUFFERSA lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
5122 TRACE("(%p, %p, %08x, %08lx)\n", hRequest
, lpBuffersOut
, dwFlags
, dwContext
);
5126 SetLastError(ERROR_INVALID_PARAMETER
);
5130 return HttpEndRequestW(hRequest
, NULL
, dwFlags
, dwContext
);
5137 } end_request_task_t
;
5139 static void AsyncHttpEndRequestProc(task_header_t
*hdr
)
5141 end_request_task_t
*task
= (end_request_task_t
*)hdr
;
5142 http_request_t
*req
= (http_request_t
*)task
->hdr
.hdr
;
5146 HTTP_HttpEndRequestW(req
, task
->flags
, task
->context
);
5149 /***********************************************************************
5150 * HttpEndRequestW (WININET.@)
5152 * Ends an HTTP request that was started by HttpSendRequestEx
5155 * TRUE if successful
5159 BOOL WINAPI
HttpEndRequestW(HINTERNET hRequest
,
5160 LPINTERNET_BUFFERSW lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
5162 http_request_t
*request
;
5165 TRACE("%p %p %x %lx -->\n", hRequest
, lpBuffersOut
, dwFlags
, dwContext
);
5169 SetLastError(ERROR_INVALID_PARAMETER
);
5173 request
= (http_request_t
*) get_handle_object( hRequest
);
5175 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
5177 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
5179 WININET_Release( &request
->hdr
);
5182 request
->hdr
.dwFlags
|= dwFlags
;
5184 if (request
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
5186 end_request_task_t
*task
;
5188 task
= alloc_async_task(&request
->hdr
, AsyncHttpEndRequestProc
, sizeof(*task
));
5189 task
->flags
= dwFlags
;
5190 task
->context
= dwContext
;
5192 INTERNET_AsyncCall(&task
->hdr
);
5193 res
= ERROR_IO_PENDING
;
5196 res
= HTTP_HttpEndRequestW(request
, dwFlags
, dwContext
);
5198 WININET_Release( &request
->hdr
);
5199 TRACE("%u <--\n", res
);
5200 if(res
!= ERROR_SUCCESS
)
5202 return res
== ERROR_SUCCESS
;
5205 /***********************************************************************
5206 * HttpSendRequestExA (WININET.@)
5208 * Sends the specified request to the HTTP server and allows chunked
5213 * Failure: FALSE, call GetLastError() for more information.
5215 BOOL WINAPI
HttpSendRequestExA(HINTERNET hRequest
,
5216 LPINTERNET_BUFFERSA lpBuffersIn
,
5217 LPINTERNET_BUFFERSA lpBuffersOut
,
5218 DWORD dwFlags
, DWORD_PTR dwContext
)
5220 INTERNET_BUFFERSW BuffersInW
;
5223 LPWSTR header
= NULL
;
5225 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
5226 lpBuffersOut
, dwFlags
, dwContext
);
5230 BuffersInW
.dwStructSize
= sizeof(LPINTERNET_BUFFERSW
);
5231 if (lpBuffersIn
->lpcszHeader
)
5233 headerlen
= MultiByteToWideChar(CP_ACP
,0,lpBuffersIn
->lpcszHeader
,
5234 lpBuffersIn
->dwHeadersLength
,0,0);
5235 header
= heap_alloc(headerlen
*sizeof(WCHAR
));
5236 if (!(BuffersInW
.lpcszHeader
= header
))
5238 SetLastError(ERROR_OUTOFMEMORY
);
5241 BuffersInW
.dwHeadersLength
= MultiByteToWideChar(CP_ACP
, 0,
5242 lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
5246 BuffersInW
.lpcszHeader
= NULL
;
5247 BuffersInW
.dwHeadersTotal
= lpBuffersIn
->dwHeadersTotal
;
5248 BuffersInW
.lpvBuffer
= lpBuffersIn
->lpvBuffer
;
5249 BuffersInW
.dwBufferLength
= lpBuffersIn
->dwBufferLength
;
5250 BuffersInW
.dwBufferTotal
= lpBuffersIn
->dwBufferTotal
;
5251 BuffersInW
.Next
= NULL
;
5254 rc
= HttpSendRequestExW(hRequest
, lpBuffersIn
? &BuffersInW
: NULL
, NULL
, dwFlags
, dwContext
);
5260 /***********************************************************************
5261 * HttpSendRequestExW (WININET.@)
5263 * Sends the specified request to the HTTP server and allows chunked
5268 * Failure: FALSE, call GetLastError() for more information.
5270 BOOL WINAPI
HttpSendRequestExW(HINTERNET hRequest
,
5271 LPINTERNET_BUFFERSW lpBuffersIn
,
5272 LPINTERNET_BUFFERSW lpBuffersOut
,
5273 DWORD dwFlags
, DWORD_PTR dwContext
)
5275 http_request_t
*request
;
5276 http_session_t
*session
;
5280 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
5281 lpBuffersOut
, dwFlags
, dwContext
);
5283 request
= (http_request_t
*) get_handle_object( hRequest
);
5285 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
5287 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5291 session
= request
->session
;
5292 assert(session
->hdr
.htype
== WH_HHTTPSESSION
);
5293 hIC
= session
->appInfo
;
5294 assert(hIC
->hdr
.htype
== WH_HINIT
);
5296 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
5298 send_request_task_t
*task
;
5300 task
= alloc_async_task(&request
->hdr
, AsyncHttpSendRequestProc
, sizeof(*task
));
5305 if (lpBuffersIn
->lpcszHeader
)
5307 if (lpBuffersIn
->dwHeadersLength
== ~0u)
5308 size
= (strlenW( lpBuffersIn
->lpcszHeader
) + 1) * sizeof(WCHAR
);
5310 size
= lpBuffersIn
->dwHeadersLength
* sizeof(WCHAR
);
5312 task
->headers
= heap_alloc(size
);
5313 memcpy(task
->headers
, lpBuffersIn
->lpcszHeader
, size
);
5315 else task
->headers
= NULL
;
5317 task
->headers_len
= size
/ sizeof(WCHAR
);
5318 task
->optional
= lpBuffersIn
->lpvBuffer
;
5319 task
->optional_len
= lpBuffersIn
->dwBufferLength
;
5320 task
->content_len
= lpBuffersIn
->dwBufferTotal
;
5324 task
->headers
= NULL
;
5325 task
->headers_len
= 0;
5326 task
->optional
= NULL
;
5327 task
->optional_len
= 0;
5328 task
->content_len
= 0;
5331 task
->end_request
= FALSE
;
5333 INTERNET_AsyncCall(&task
->hdr
);
5334 res
= ERROR_IO_PENDING
;
5339 res
= HTTP_HttpSendRequestW(request
, lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
5340 lpBuffersIn
->lpvBuffer
, lpBuffersIn
->dwBufferLength
,
5341 lpBuffersIn
->dwBufferTotal
, FALSE
);
5343 res
= HTTP_HttpSendRequestW(request
, NULL
, 0, NULL
, 0, 0, FALSE
);
5348 WININET_Release( &request
->hdr
);
5352 return res
== ERROR_SUCCESS
;
5355 /***********************************************************************
5356 * HttpSendRequestW (WININET.@)
5358 * Sends the specified request to the HTTP server
5365 BOOL WINAPI
HttpSendRequestW(HINTERNET hHttpRequest
, LPCWSTR lpszHeaders
,
5366 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
5368 http_request_t
*request
;
5369 http_session_t
*session
= NULL
;
5370 appinfo_t
*hIC
= NULL
;
5371 DWORD res
= ERROR_SUCCESS
;
5373 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest
,
5374 debugstr_wn(lpszHeaders
, dwHeaderLength
), dwHeaderLength
, lpOptional
, dwOptionalLength
);
5376 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
5377 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
5379 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5383 session
= request
->session
;
5384 if (NULL
== session
|| session
->hdr
.htype
!= WH_HHTTPSESSION
)
5386 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5390 hIC
= session
->appInfo
;
5391 if (NULL
== hIC
|| hIC
->hdr
.htype
!= WH_HINIT
)
5393 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5397 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
5399 send_request_task_t
*task
;
5401 task
= alloc_async_task(&request
->hdr
, AsyncHttpSendRequestProc
, sizeof(*task
));
5406 if (dwHeaderLength
== ~0u) size
= (strlenW(lpszHeaders
) + 1) * sizeof(WCHAR
);
5407 else size
= dwHeaderLength
* sizeof(WCHAR
);
5409 task
->headers
= heap_alloc(size
);
5410 memcpy(task
->headers
, lpszHeaders
, size
);
5413 task
->headers
= NULL
;
5414 task
->headers_len
= dwHeaderLength
;
5415 task
->optional
= lpOptional
;
5416 task
->optional_len
= dwOptionalLength
;
5417 task
->content_len
= dwOptionalLength
;
5418 task
->end_request
= TRUE
;
5420 INTERNET_AsyncCall(&task
->hdr
);
5421 res
= ERROR_IO_PENDING
;
5425 res
= HTTP_HttpSendRequestW(request
, lpszHeaders
,
5426 dwHeaderLength
, lpOptional
, dwOptionalLength
,
5427 dwOptionalLength
, TRUE
);
5431 WININET_Release( &request
->hdr
);
5434 return res
== ERROR_SUCCESS
;
5437 /***********************************************************************
5438 * HttpSendRequestA (WININET.@)
5440 * Sends the specified request to the HTTP server
5447 BOOL WINAPI
HttpSendRequestA(HINTERNET hHttpRequest
, LPCSTR lpszHeaders
,
5448 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
5451 LPWSTR szHeaders
=NULL
;
5452 DWORD nLen
=dwHeaderLength
;
5453 if(lpszHeaders
!=NULL
)
5455 nLen
=MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,NULL
,0);
5456 szHeaders
= heap_alloc(nLen
*sizeof(WCHAR
));
5457 MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,szHeaders
,nLen
);
5459 result
= HttpSendRequestW(hHttpRequest
, szHeaders
, nLen
, lpOptional
, dwOptionalLength
);
5460 heap_free(szHeaders
);
5464 /***********************************************************************
5465 * HTTPSESSION_Destroy (internal)
5467 * Deallocate session handle
5470 static void HTTPSESSION_Destroy(object_header_t
*hdr
)
5472 http_session_t
*session
= (http_session_t
*) hdr
;
5474 TRACE("%p\n", session
);
5476 WININET_Release(&session
->appInfo
->hdr
);
5478 heap_free(session
->hostName
);
5479 heap_free(session
->password
);
5480 heap_free(session
->userName
);
5483 static DWORD
HTTPSESSION_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
5485 http_session_t
*ses
= (http_session_t
*)hdr
;
5488 case INTERNET_OPTION_HANDLE_TYPE
:
5489 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5491 if (*size
< sizeof(ULONG
))
5492 return ERROR_INSUFFICIENT_BUFFER
;
5494 *size
= sizeof(DWORD
);
5495 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_CONNECT_HTTP
;
5496 return ERROR_SUCCESS
;
5497 case INTERNET_OPTION_CONNECT_TIMEOUT
:
5498 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5500 if (*size
< sizeof(DWORD
))
5501 return ERROR_INSUFFICIENT_BUFFER
;
5503 *size
= sizeof(DWORD
);
5504 *(DWORD
*)buffer
= ses
->connect_timeout
;
5505 return ERROR_SUCCESS
;
5507 case INTERNET_OPTION_SEND_TIMEOUT
:
5508 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5510 if (*size
< sizeof(DWORD
))
5511 return ERROR_INSUFFICIENT_BUFFER
;
5513 *size
= sizeof(DWORD
);
5514 *(DWORD
*)buffer
= ses
->send_timeout
;
5515 return ERROR_SUCCESS
;
5517 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
5518 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5520 if (*size
< sizeof(DWORD
))
5521 return ERROR_INSUFFICIENT_BUFFER
;
5523 *size
= sizeof(DWORD
);
5524 *(DWORD
*)buffer
= ses
->receive_timeout
;
5525 return ERROR_SUCCESS
;
5528 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
5531 static DWORD
HTTPSESSION_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
5533 http_session_t
*ses
= (http_session_t
*)hdr
;
5536 case INTERNET_OPTION_USERNAME
:
5538 heap_free(ses
->userName
);
5539 if (!(ses
->userName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
5540 return ERROR_SUCCESS
;
5542 case INTERNET_OPTION_PASSWORD
:
5544 heap_free(ses
->password
);
5545 if (!(ses
->password
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
5546 return ERROR_SUCCESS
;
5548 case INTERNET_OPTION_PROXY_USERNAME
:
5550 heap_free(ses
->appInfo
->proxyUsername
);
5551 if (!(ses
->appInfo
->proxyUsername
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
5552 return ERROR_SUCCESS
;
5554 case INTERNET_OPTION_PROXY_PASSWORD
:
5556 heap_free(ses
->appInfo
->proxyPassword
);
5557 if (!(ses
->appInfo
->proxyPassword
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
5558 return ERROR_SUCCESS
;
5560 case INTERNET_OPTION_CONNECT_TIMEOUT
:
5562 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
5563 ses
->connect_timeout
= *(DWORD
*)buffer
;
5564 return ERROR_SUCCESS
;
5566 case INTERNET_OPTION_SEND_TIMEOUT
:
5568 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
5569 ses
->send_timeout
= *(DWORD
*)buffer
;
5570 return ERROR_SUCCESS
;
5572 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
5574 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
5575 ses
->receive_timeout
= *(DWORD
*)buffer
;
5576 return ERROR_SUCCESS
;
5581 return INET_SetOption(hdr
, option
, buffer
, size
);
5584 static const object_vtbl_t HTTPSESSIONVtbl
= {
5585 HTTPSESSION_Destroy
,
5587 HTTPSESSION_QueryOption
,
5588 HTTPSESSION_SetOption
,
5597 /***********************************************************************
5598 * HTTP_Connect (internal)
5600 * Create http session handle
5603 * HINTERNET a session handle on success
5607 DWORD
HTTP_Connect(appinfo_t
*hIC
, LPCWSTR lpszServerName
,
5608 INTERNET_PORT serverPort
, LPCWSTR lpszUserName
,
5609 LPCWSTR lpszPassword
, DWORD dwFlags
, DWORD_PTR dwContext
,
5610 DWORD dwInternalFlags
, HINTERNET
*ret
)
5612 http_session_t
*session
= NULL
;
5616 if (!lpszServerName
|| !lpszServerName
[0])
5617 return ERROR_INVALID_PARAMETER
;
5619 assert( hIC
->hdr
.htype
== WH_HINIT
);
5621 session
= alloc_object(&hIC
->hdr
, &HTTPSESSIONVtbl
, sizeof(http_session_t
));
5623 return ERROR_OUTOFMEMORY
;
5626 * According to my tests. The name is not resolved until a request is sent
5629 session
->hdr
.htype
= WH_HHTTPSESSION
;
5630 session
->hdr
.dwFlags
= dwFlags
;
5631 session
->hdr
.dwContext
= dwContext
;
5632 session
->hdr
.dwInternalFlags
|= dwInternalFlags
;
5634 WININET_AddRef( &hIC
->hdr
);
5635 session
->appInfo
= hIC
;
5636 list_add_head( &hIC
->hdr
.children
, &session
->hdr
.entry
);
5638 if(hIC
->proxy
&& hIC
->accessType
== INTERNET_OPEN_TYPE_PROXY
) {
5639 if(hIC
->proxyBypass
)
5640 FIXME("Proxy bypass is ignored.\n");
5642 session
->hostName
= heap_strdupW(lpszServerName
);
5643 if (lpszUserName
&& lpszUserName
[0])
5644 session
->userName
= heap_strdupW(lpszUserName
);
5645 if (lpszPassword
&& lpszPassword
[0])
5646 session
->password
= heap_strdupW(lpszPassword
);
5647 session
->hostPort
= serverPort
;
5648 session
->connect_timeout
= hIC
->connect_timeout
;
5649 session
->send_timeout
= INFINITE
;
5650 session
->receive_timeout
= INFINITE
;
5652 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5653 if (!(session
->hdr
.dwInternalFlags
& INET_OPENURL
))
5655 INTERNET_SendCallback(&hIC
->hdr
, dwContext
,
5656 INTERNET_STATUS_HANDLE_CREATED
, &session
->hdr
.hInternet
,
5661 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5665 TRACE("%p --> %p\n", hIC
, session
);
5667 *ret
= session
->hdr
.hInternet
;
5668 return ERROR_SUCCESS
;
5671 /***********************************************************************
5672 * HTTP_clear_response_headers (internal)
5674 * clear out any old response headers
5676 static void HTTP_clear_response_headers( http_request_t
*request
)
5680 for( i
=0; i
<request
->nCustHeaders
; i
++)
5682 if( !request
->custHeaders
[i
].lpszField
)
5684 if( !request
->custHeaders
[i
].lpszValue
)
5686 if ( request
->custHeaders
[i
].wFlags
& HDR_ISREQUEST
)
5688 HTTP_DeleteCustomHeader( request
, i
);
5693 /***********************************************************************
5694 * HTTP_GetResponseHeaders (internal)
5696 * Read server response
5703 static INT
HTTP_GetResponseHeaders(http_request_t
*request
, BOOL clear
)
5706 WCHAR buffer
[MAX_REPLY_LEN
];
5707 DWORD buflen
= MAX_REPLY_LEN
;
5708 BOOL bSuccess
= FALSE
;
5710 char bufferA
[MAX_REPLY_LEN
];
5711 LPWSTR status_code
= NULL
, status_text
= NULL
;
5712 DWORD cchMaxRawHeaders
= 1024;
5713 LPWSTR lpszRawHeaders
= NULL
;
5715 DWORD cchRawHeaders
= 0;
5716 BOOL codeHundred
= FALSE
;
5720 if(!request
->netconn
)
5723 NETCON_set_timeout( request
->netconn
, FALSE
, request
->receive_timeout
);
5726 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5728 buflen
= MAX_REPLY_LEN
;
5729 if (!read_line(request
, bufferA
, &buflen
))
5732 /* clear old response headers (eg. from a redirect response) */
5734 HTTP_clear_response_headers( request
);
5739 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
5740 /* check is this a status code line? */
5741 if (!strncmpW(buffer
, g_szHttp1_0
, 4))
5743 /* split the version from the status code */
5744 status_code
= strchrW( buffer
, ' ' );
5749 /* split the status code from the status text */
5750 status_text
= strchrW( status_code
, ' ' );
5755 request
->status_code
= atoiW(status_code
);
5757 TRACE("version [%s] status code [%s] status text [%s]\n",
5758 debugstr_w(buffer
), debugstr_w(status_code
), debugstr_w(status_text
) );
5760 codeHundred
= request
->status_code
== HTTP_STATUS_CONTINUE
;
5762 else if (!codeHundred
)
5764 WARN("No status line at head of response (%s)\n", debugstr_w(buffer
));
5766 heap_free(request
->version
);
5767 heap_free(request
->statusText
);
5769 request
->status_code
= HTTP_STATUS_OK
;
5770 request
->version
= heap_strdupW(g_szHttp1_0
);
5771 request
->statusText
= heap_strdupW(szOK
);
5773 heap_free(request
->rawHeaders
);
5774 request
->rawHeaders
= heap_strdupW(szDefaultHeader
);
5779 } while (codeHundred
);
5781 /* Add status code */
5782 HTTP_ProcessHeader(request
, szStatus
, status_code
,
5783 HTTP_ADDHDR_FLAG_REPLACE
);
5785 heap_free(request
->version
);
5786 heap_free(request
->statusText
);
5788 request
->version
= heap_strdupW(buffer
);
5789 request
->statusText
= heap_strdupW(status_text
);
5791 /* Restore the spaces */
5792 *(status_code
-1) = ' ';
5793 *(status_text
-1) = ' ';
5795 /* regenerate raw headers */
5796 lpszRawHeaders
= heap_alloc((cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
5797 if (!lpszRawHeaders
) goto lend
;
5799 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5800 cchMaxRawHeaders
*= 2;
5801 temp
= heap_realloc(lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
5802 if (temp
== NULL
) goto lend
;
5803 lpszRawHeaders
= temp
;
5804 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
5805 cchRawHeaders
+= (buflen
-1);
5806 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
5807 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
5808 lpszRawHeaders
[cchRawHeaders
] = '\0';
5810 /* Parse each response line */
5813 buflen
= MAX_REPLY_LEN
;
5814 if (read_line(request
, bufferA
, &buflen
))
5816 LPWSTR
* pFieldAndValue
;
5818 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA
));
5820 if (!bufferA
[0]) break;
5821 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
5823 pFieldAndValue
= HTTP_InterpretHttpHeader(buffer
);
5826 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5827 cchMaxRawHeaders
*= 2;
5828 temp
= heap_realloc(lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
5829 if (temp
== NULL
) goto lend
;
5830 lpszRawHeaders
= temp
;
5831 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
5832 cchRawHeaders
+= (buflen
-1);
5833 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
5834 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
5835 lpszRawHeaders
[cchRawHeaders
] = '\0';
5837 HTTP_ProcessHeader(request
, pFieldAndValue
[0], pFieldAndValue
[1],
5838 HTTP_ADDREQ_FLAG_ADD
);
5840 HTTP_FreeTokens(pFieldAndValue
);
5851 /* make sure the response header is terminated with an empty line. Some apps really
5852 truly care about that empty line being there for some reason. Just add it to the
5854 if (cchRawHeaders
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5856 cchMaxRawHeaders
= cchRawHeaders
+ strlenW(szCrLf
);
5857 temp
= heap_realloc(lpszRawHeaders
, (cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
5858 if (temp
== NULL
) goto lend
;
5859 lpszRawHeaders
= temp
;
5862 memcpy(&lpszRawHeaders
[cchRawHeaders
], szCrLf
, sizeof(szCrLf
));
5864 heap_free(request
->rawHeaders
);
5865 request
->rawHeaders
= lpszRawHeaders
;
5866 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders
));
5876 heap_free(lpszRawHeaders
);
5881 /***********************************************************************
5882 * HTTP_InterpretHttpHeader (internal)
5884 * Parse server response
5888 * Pointer to array of field, value, NULL on success.
5891 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
)
5893 LPWSTR
* pTokenPair
;
5897 pTokenPair
= heap_alloc_zero(sizeof(*pTokenPair
)*3);
5899 pszColon
= strchrW(buffer
, ':');
5900 /* must have two tokens */
5903 HTTP_FreeTokens(pTokenPair
);
5905 TRACE("No ':' in line: %s\n", debugstr_w(buffer
));
5909 pTokenPair
[0] = heap_alloc((pszColon
- buffer
+ 1) * sizeof(WCHAR
));
5912 HTTP_FreeTokens(pTokenPair
);
5915 memcpy(pTokenPair
[0], buffer
, (pszColon
- buffer
) * sizeof(WCHAR
));
5916 pTokenPair
[0][pszColon
- buffer
] = '\0';
5920 len
= strlenW(pszColon
);
5921 pTokenPair
[1] = heap_alloc((len
+ 1) * sizeof(WCHAR
));
5924 HTTP_FreeTokens(pTokenPair
);
5927 memcpy(pTokenPair
[1], pszColon
, (len
+ 1) * sizeof(WCHAR
));
5929 strip_spaces(pTokenPair
[0]);
5930 strip_spaces(pTokenPair
[1]);
5932 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair
[0]), debugstr_w(pTokenPair
[1]));
5936 /***********************************************************************
5937 * HTTP_ProcessHeader (internal)
5939 * Stuff header into header tables according to <dwModifier>
5943 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5945 static DWORD
HTTP_ProcessHeader(http_request_t
*request
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
)
5947 LPHTTPHEADERW lphttpHdr
= NULL
;
5949 BOOL request_only
= dwModifier
& HTTP_ADDHDR_FLAG_REQ
;
5950 DWORD res
= ERROR_HTTP_INVALID_HEADER
;
5952 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field
), debugstr_w(value
), dwModifier
);
5954 /* REPLACE wins out over ADD */
5955 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
5956 dwModifier
&= ~HTTP_ADDHDR_FLAG_ADD
;
5958 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD
)
5961 index
= HTTP_GetCustomHeaderIndex(request
, field
, 0, request_only
);
5965 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD_IF_NEW
)
5966 return ERROR_HTTP_INVALID_HEADER
;
5967 lphttpHdr
= &request
->custHeaders
[index
];
5973 hdr
.lpszField
= (LPWSTR
)field
;
5974 hdr
.lpszValue
= (LPWSTR
)value
;
5975 hdr
.wFlags
= hdr
.wCount
= 0;
5977 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5978 hdr
.wFlags
|= HDR_ISREQUEST
;
5980 return HTTP_InsertCustomHeader(request
, &hdr
);
5982 /* no value to delete */
5983 else return ERROR_SUCCESS
;
5985 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5986 lphttpHdr
->wFlags
|= HDR_ISREQUEST
;
5988 lphttpHdr
->wFlags
&= ~HDR_ISREQUEST
;
5990 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
5992 HTTP_DeleteCustomHeader( request
, index
);
5998 hdr
.lpszField
= (LPWSTR
)field
;
5999 hdr
.lpszValue
= (LPWSTR
)value
;
6000 hdr
.wFlags
= hdr
.wCount
= 0;
6002 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
6003 hdr
.wFlags
|= HDR_ISREQUEST
;
6005 return HTTP_InsertCustomHeader(request
, &hdr
);
6008 return ERROR_SUCCESS
;
6010 else if (dwModifier
& COALESCEFLAGS
)
6015 INT origlen
= strlenW(lphttpHdr
->lpszValue
);
6016 INT valuelen
= strlenW(value
);
6018 if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
)
6021 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
6023 else if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON
)
6026 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
6029 len
= origlen
+ valuelen
+ ((ch
> 0) ? 2 : 0);
6031 lpsztmp
= heap_realloc(lphttpHdr
->lpszValue
, (len
+1)*sizeof(WCHAR
));
6034 lphttpHdr
->lpszValue
= lpsztmp
;
6035 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
6038 lphttpHdr
->lpszValue
[origlen
] = ch
;
6040 lphttpHdr
->lpszValue
[origlen
] = ' ';
6044 memcpy(&lphttpHdr
->lpszValue
[origlen
], value
, valuelen
*sizeof(WCHAR
));
6045 lphttpHdr
->lpszValue
[len
] = '\0';
6046 res
= ERROR_SUCCESS
;
6050 WARN("heap_realloc (%d bytes) failed\n",len
+1);
6051 res
= ERROR_OUTOFMEMORY
;
6054 TRACE("<-- %d\n", res
);
6058 /***********************************************************************
6059 * HTTP_GetCustomHeaderIndex (internal)
6061 * Return index of custom header from header array
6064 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*request
, LPCWSTR lpszField
,
6065 int requested_index
, BOOL request_only
)
6069 TRACE("%s, %d, %d\n", debugstr_w(lpszField
), requested_index
, request_only
);
6071 for (index
= 0; index
< request
->nCustHeaders
; index
++)
6073 if (strcmpiW(request
->custHeaders
[index
].lpszField
, lpszField
))
6076 if (request_only
&& !(request
->custHeaders
[index
].wFlags
& HDR_ISREQUEST
))
6079 if (!request_only
&& (request
->custHeaders
[index
].wFlags
& HDR_ISREQUEST
))
6082 if (requested_index
== 0)
6087 if (index
>= request
->nCustHeaders
)
6090 TRACE("Return: %d\n", index
);
6095 /***********************************************************************
6096 * HTTP_InsertCustomHeader (internal)
6098 * Insert header into array
6101 static DWORD
HTTP_InsertCustomHeader(http_request_t
*request
, LPHTTPHEADERW lpHdr
)
6104 LPHTTPHEADERW lph
= NULL
;
6106 TRACE("--> %s: %s\n", debugstr_w(lpHdr
->lpszField
), debugstr_w(lpHdr
->lpszValue
));
6107 count
= request
->nCustHeaders
+ 1;
6109 lph
= heap_realloc_zero(request
->custHeaders
, sizeof(HTTPHEADERW
) * count
);
6111 lph
= heap_alloc_zero(sizeof(HTTPHEADERW
) * count
);
6114 return ERROR_OUTOFMEMORY
;
6116 request
->custHeaders
= lph
;
6117 request
->custHeaders
[count
-1].lpszField
= heap_strdupW(lpHdr
->lpszField
);
6118 request
->custHeaders
[count
-1].lpszValue
= heap_strdupW(lpHdr
->lpszValue
);
6119 request
->custHeaders
[count
-1].wFlags
= lpHdr
->wFlags
;
6120 request
->custHeaders
[count
-1].wCount
= lpHdr
->wCount
;
6121 request
->nCustHeaders
++;
6123 return ERROR_SUCCESS
;
6127 /***********************************************************************
6128 * HTTP_DeleteCustomHeader (internal)
6130 * Delete header from array
6131 * If this function is called, the indexs may change.
6133 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*request
, DWORD index
)
6135 if( request
->nCustHeaders
<= 0 )
6137 if( index
>= request
->nCustHeaders
)
6139 request
->nCustHeaders
--;
6141 heap_free(request
->custHeaders
[index
].lpszField
);
6142 heap_free(request
->custHeaders
[index
].lpszValue
);
6144 memmove( &request
->custHeaders
[index
], &request
->custHeaders
[index
+1],
6145 (request
->nCustHeaders
- index
)* sizeof(HTTPHEADERW
) );
6146 memset( &request
->custHeaders
[request
->nCustHeaders
], 0, sizeof(HTTPHEADERW
) );
6152 /***********************************************************************
6153 * HTTP_VerifyValidHeader (internal)
6155 * Verify the given header is not invalid for the given http request
6158 static BOOL
HTTP_VerifyValidHeader(http_request_t
*request
, LPCWSTR field
)
6160 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6161 if (!strcmpW(request
->version
, g_szHttp1_0
) && !strcmpiW(field
, szAccept_Encoding
))
6162 return ERROR_HTTP_INVALID_HEADER
;
6164 return ERROR_SUCCESS
;
6167 /***********************************************************************
6168 * IsHostInProxyBypassList (@)
6173 BOOL WINAPI
IsHostInProxyBypassList(DWORD flags
, LPCSTR szHost
, DWORD length
)
6175 FIXME("STUB: flags=%d host=%s length=%d\n",flags
,szHost
,length
);