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
69 #include "cryptuiapi.h"
72 #include "wine/debug.h"
73 #include "wine/exception.h"
74 #include "wine/unicode.h"
76 WINE_DEFAULT_DEBUG_CHANNEL(wininet
);
78 static const WCHAR g_szHttp1_0
[] = {'H','T','T','P','/','1','.','0',0};
79 static const WCHAR g_szHttp1_1
[] = {'H','T','T','P','/','1','.','1',0};
80 static const WCHAR szOK
[] = {'O','K',0};
81 static const WCHAR szDefaultHeader
[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
82 static const WCHAR hostW
[] = { 'H','o','s','t',0 };
83 static const WCHAR szAuthorization
[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
84 static const WCHAR szProxy_Authorization
[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
85 static const WCHAR szStatus
[] = { 'S','t','a','t','u','s',0 };
86 static const WCHAR szKeepAlive
[] = {'K','e','e','p','-','A','l','i','v','e',0};
87 static const WCHAR szGET
[] = { 'G','E','T', 0 };
88 static const WCHAR szHEAD
[] = { 'H','E','A','D', 0 };
89 static const WCHAR szCrLf
[] = {'\r','\n', 0};
91 static const WCHAR szAccept
[] = { 'A','c','c','e','p','t',0 };
92 static const WCHAR szAccept_Charset
[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
93 static const WCHAR szAccept_Encoding
[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
94 static const WCHAR szAccept_Language
[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
95 static const WCHAR szAccept_Ranges
[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
96 static const WCHAR szAge
[] = { 'A','g','e',0 };
97 static const WCHAR szAllow
[] = { 'A','l','l','o','w',0 };
98 static const WCHAR szCache_Control
[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
99 static const WCHAR szConnection
[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
100 static const WCHAR szContent_Base
[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
101 static const WCHAR szContent_Disposition
[] = { 'C','o','n','t','e','n','t','-','D','i','s','p','o','s','i','t','i','o','n',0 };
102 static const WCHAR szContent_Encoding
[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
103 static const WCHAR szContent_ID
[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
104 static const WCHAR szContent_Language
[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
105 static const WCHAR szContent_Length
[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
106 static const WCHAR szContent_Location
[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
107 static const WCHAR szContent_MD5
[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
108 static const WCHAR szContent_Range
[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
109 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 };
110 static const WCHAR szContent_Type
[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
111 static const WCHAR szCookie
[] = { 'C','o','o','k','i','e',0 };
112 static const WCHAR szDate
[] = { 'D','a','t','e',0 };
113 static const WCHAR szFrom
[] = { 'F','r','o','m',0 };
114 static const WCHAR szETag
[] = { 'E','T','a','g',0 };
115 static const WCHAR szExpect
[] = { 'E','x','p','e','c','t',0 };
116 static const WCHAR szExpires
[] = { 'E','x','p','i','r','e','s',0 };
117 static const WCHAR szIf_Match
[] = { 'I','f','-','M','a','t','c','h',0 };
118 static const WCHAR szIf_Modified_Since
[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
119 static const WCHAR szIf_None_Match
[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
120 static const WCHAR szIf_Range
[] = { 'I','f','-','R','a','n','g','e',0 };
121 static const WCHAR szIf_Unmodified_Since
[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
122 static const WCHAR szLast_Modified
[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
123 static const WCHAR szLocation
[] = { 'L','o','c','a','t','i','o','n',0 };
124 static const WCHAR szMax_Forwards
[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
125 static const WCHAR szMime_Version
[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
126 static const WCHAR szPragma
[] = { 'P','r','a','g','m','a',0 };
127 static const WCHAR szProxy_Authenticate
[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
128 static const WCHAR szProxy_Connection
[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
129 static const WCHAR szPublic
[] = { 'P','u','b','l','i','c',0 };
130 static const WCHAR szRange
[] = { 'R','a','n','g','e',0 };
131 static const WCHAR szReferer
[] = { 'R','e','f','e','r','e','r',0 };
132 static const WCHAR szRetry_After
[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
133 static const WCHAR szServer
[] = { 'S','e','r','v','e','r',0 };
134 static const WCHAR szSet_Cookie
[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
135 static const WCHAR szTransfer_Encoding
[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
136 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 };
137 static const WCHAR szUpgrade
[] = { 'U','p','g','r','a','d','e',0 };
138 static const WCHAR szURI
[] = { 'U','R','I',0 };
139 static const WCHAR szUser_Agent
[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
140 static const WCHAR szVary
[] = { 'V','a','r','y',0 };
141 static const WCHAR szVia
[] = { 'V','i','a',0 };
142 static const WCHAR szWarning
[] = { 'W','a','r','n','i','n','g',0 };
143 static const WCHAR szWWW_Authenticate
[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
145 #define HTTP_REFERER szReferer
146 #define HTTP_ACCEPT szAccept
147 #define HTTP_USERAGENT szUser_Agent
149 #define HTTP_ADDHDR_FLAG_ADD 0x20000000
150 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW 0x10000000
151 #define HTTP_ADDHDR_FLAG_COALESCE 0x40000000
152 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA 0x40000000
153 #define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
154 #define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
155 #define HTTP_ADDHDR_FLAG_REQ 0x02000000
157 #define COLLECT_TIME 60000
159 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
170 unsigned int auth_data_len
;
171 BOOL finished
; /* finished authenticating */
175 typedef struct _basicAuthorizationData
182 UINT authorizationLen
;
183 } basicAuthorizationData
;
185 typedef struct _authorizationData
199 static struct list basicAuthorizationCache
= LIST_INIT(basicAuthorizationCache
);
200 static struct list authorizationCache
= LIST_INIT(authorizationCache
);
202 static CRITICAL_SECTION authcache_cs
;
203 static CRITICAL_SECTION_DEBUG critsect_debug
=
206 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
207 0, 0, { (DWORD_PTR
)(__FILE__
": authcache_cs") }
209 static CRITICAL_SECTION authcache_cs
= { &critsect_debug
, -1, 0, 0, 0, 0 };
211 static BOOL
HTTP_GetResponseHeaders(http_request_t
*req
, BOOL clear
);
212 static DWORD
HTTP_ProcessHeader(http_request_t
*req
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
);
213 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
);
214 static DWORD
HTTP_InsertCustomHeader(http_request_t
*req
, LPHTTPHEADERW lpHdr
);
215 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*req
, LPCWSTR lpszField
, INT index
, BOOL Request
);
216 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*req
, DWORD index
);
217 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
);
218 static DWORD
HTTP_HttpQueryInfoW(http_request_t
*, DWORD
, LPVOID
, LPDWORD
, LPDWORD
);
219 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*req
, LPCWSTR lpszUrl
);
220 static UINT
HTTP_DecodeBase64(LPCWSTR base64
, LPSTR bin
);
221 static BOOL
HTTP_VerifyValidHeader(http_request_t
*req
, LPCWSTR field
);
222 static BOOL
drain_content(http_request_t
*,BOOL
);
224 static CRITICAL_SECTION connection_pool_cs
;
225 static CRITICAL_SECTION_DEBUG connection_pool_debug
=
227 0, 0, &connection_pool_cs
,
228 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
229 0, 0, { (DWORD_PTR
)(__FILE__
": connection_pool_cs") }
231 static CRITICAL_SECTION connection_pool_cs
= { &connection_pool_debug
, -1, 0, 0, 0, 0 };
233 static struct list connection_pool
= LIST_INIT(connection_pool
);
234 static BOOL collector_running
;
236 void server_addref(server_t
*server
)
238 InterlockedIncrement(&server
->ref
);
241 void server_release(server_t
*server
)
243 if(InterlockedDecrement(&server
->ref
))
246 list_remove(&server
->entry
);
248 if(server
->cert_chain
)
249 CertFreeCertificateChain(server
->cert_chain
);
250 heap_free(server
->name
);
251 heap_free(server
->scheme_host_port
);
255 static BOOL
process_host_port(server_t
*server
)
261 static const WCHAR httpW
[] = {'h','t','t','p',0};
262 static const WCHAR httpsW
[] = {'h','t','t','p','s',0};
263 static const WCHAR formatW
[] = {'%','s',':','/','/','%','s',':','%','u',0};
265 name_len
= strlenW(server
->name
);
266 buf
= heap_alloc((name_len
+ 10 /* strlen("://:<port>") */)*sizeof(WCHAR
) + sizeof(httpsW
));
270 sprintfW(buf
, formatW
, server
->is_https
? httpsW
: httpW
, server
->name
, server
->port
);
271 server
->scheme_host_port
= buf
;
273 server
->host_port
= server
->scheme_host_port
+ 7 /* strlen("http://") */;
277 default_port
= server
->port
== (server
->is_https
? INTERNET_DEFAULT_HTTPS_PORT
: INTERNET_DEFAULT_HTTP_PORT
);
278 server
->canon_host_port
= default_port
? server
->name
: server
->host_port
;
282 server_t
*get_server(const WCHAR
*name
, INTERNET_PORT port
, BOOL is_https
, BOOL do_create
)
284 server_t
*iter
, *server
= NULL
;
286 if(port
== INTERNET_INVALID_PORT_NUMBER
)
287 port
= is_https
? INTERNET_DEFAULT_HTTPS_PORT
: INTERNET_DEFAULT_HTTP_PORT
;
289 EnterCriticalSection(&connection_pool_cs
);
291 LIST_FOR_EACH_ENTRY(iter
, &connection_pool
, server_t
, entry
) {
292 if(iter
->port
== port
&& !strcmpW(iter
->name
, name
) && iter
->is_https
== is_https
) {
294 server_addref(server
);
299 if(!server
&& do_create
) {
300 server
= heap_alloc_zero(sizeof(*server
));
302 server
->ref
= 2; /* list reference and return */
304 server
->is_https
= is_https
;
305 list_init(&server
->conn_pool
);
306 server
->name
= heap_strdupW(name
);
307 if(server
->name
&& process_host_port(server
)) {
308 list_add_head(&connection_pool
, &server
->entry
);
316 LeaveCriticalSection(&connection_pool_cs
);
321 BOOL
collect_connections(collect_type_t collect_type
)
323 netconn_t
*netconn
, *netconn_safe
;
324 server_t
*server
, *server_safe
;
325 BOOL remaining
= FALSE
;
328 now
= GetTickCount64();
330 LIST_FOR_EACH_ENTRY_SAFE(server
, server_safe
, &connection_pool
, server_t
, entry
) {
331 LIST_FOR_EACH_ENTRY_SAFE(netconn
, netconn_safe
, &server
->conn_pool
, netconn_t
, pool_entry
) {
332 if(collect_type
> COLLECT_TIMEOUT
|| netconn
->keep_until
< now
) {
333 TRACE("freeing %p\n", netconn
);
334 list_remove(&netconn
->pool_entry
);
335 free_netconn(netconn
);
341 if(collect_type
== COLLECT_CLEANUP
) {
342 list_remove(&server
->entry
);
343 list_init(&server
->entry
);
344 server_release(server
);
351 static DWORD WINAPI
collect_connections_proc(void *arg
)
353 BOOL remaining_conns
;
356 /* FIXME: Use more sophisticated method */
359 EnterCriticalSection(&connection_pool_cs
);
361 remaining_conns
= collect_connections(COLLECT_TIMEOUT
);
363 collector_running
= FALSE
;
365 LeaveCriticalSection(&connection_pool_cs
);
366 }while(remaining_conns
);
368 FreeLibraryAndExitThread(WININET_hModule
, 0);
371 static LPHTTPHEADERW
HTTP_GetHeader(http_request_t
*req
, LPCWSTR head
)
374 HeaderIndex
= HTTP_GetCustomHeaderIndex(req
, head
, 0, TRUE
);
375 if (HeaderIndex
== -1)
378 return &req
->custHeaders
[HeaderIndex
];
387 struct data_stream_vtbl_t
{
388 DWORD (*get_avail_data
)(data_stream_t
*,http_request_t
*);
389 BOOL (*end_of_data
)(data_stream_t
*,http_request_t
*);
390 DWORD (*read
)(data_stream_t
*,http_request_t
*,BYTE
*,DWORD
,DWORD
*,read_mode_t
);
391 BOOL (*drain_content
)(data_stream_t
*,http_request_t
*);
392 void (*destroy
)(data_stream_t
*);
396 data_stream_t data_stream
;
398 BYTE buf
[READ_BUFFER_SIZE
];
404 static inline void destroy_data_stream(data_stream_t
*stream
)
406 stream
->vtbl
->destroy(stream
);
409 static void reset_data_stream(http_request_t
*req
)
411 destroy_data_stream(req
->data_stream
);
412 req
->data_stream
= &req
->netconn_stream
.data_stream
;
413 req
->read_pos
= req
->read_size
= req
->netconn_stream
.content_read
= 0;
414 req
->read_chunked
= req
->read_gzip
= FALSE
;
420 data_stream_t stream
;
421 data_stream_t
*parent_stream
;
423 BYTE buf
[READ_BUFFER_SIZE
];
429 static DWORD
gzip_get_avail_data(data_stream_t
*stream
, http_request_t
*req
)
431 /* Allow reading only from read buffer */
435 static BOOL
gzip_end_of_data(data_stream_t
*stream
, http_request_t
*req
)
437 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
438 return gzip_stream
->end_of_data
;
441 static DWORD
gzip_read(data_stream_t
*stream
, http_request_t
*req
, BYTE
*buf
, DWORD size
,
442 DWORD
*read
, read_mode_t read_mode
)
444 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
445 z_stream
*zstream
= &gzip_stream
->zstream
;
446 DWORD current_read
, ret_read
= 0;
449 DWORD res
= ERROR_SUCCESS
;
451 while(size
&& !gzip_stream
->end_of_data
) {
452 end
= gzip_stream
->parent_stream
->vtbl
->end_of_data(gzip_stream
->parent_stream
, req
);
454 if(gzip_stream
->buf_size
<= 64 && !end
) {
455 if(gzip_stream
->buf_pos
) {
456 if(gzip_stream
->buf_size
)
457 memmove(gzip_stream
->buf
, gzip_stream
->buf
+gzip_stream
->buf_pos
, gzip_stream
->buf_size
);
458 gzip_stream
->buf_pos
= 0;
460 res
= gzip_stream
->parent_stream
->vtbl
->read(gzip_stream
->parent_stream
, req
, gzip_stream
->buf
+gzip_stream
->buf_size
,
461 sizeof(gzip_stream
->buf
)-gzip_stream
->buf_size
, ¤t_read
, read_mode
);
462 gzip_stream
->buf_size
+= current_read
;
463 if(res
!= ERROR_SUCCESS
)
465 end
= gzip_stream
->parent_stream
->vtbl
->end_of_data(gzip_stream
->parent_stream
, req
);
466 if(!current_read
&& !end
) {
467 if(read_mode
!= READMODE_NOBLOCK
) {
468 WARN("unexpected end of data\n");
469 gzip_stream
->end_of_data
= TRUE
;
473 if(gzip_stream
->buf_size
<= 64 && !end
)
477 zstream
->next_in
= gzip_stream
->buf
+gzip_stream
->buf_pos
;
478 zstream
->avail_in
= gzip_stream
->buf_size
-(end
? 0 : 64);
479 zstream
->next_out
= buf
+ret_read
;
480 zstream
->avail_out
= size
;
481 zres
= inflate(&gzip_stream
->zstream
, 0);
482 current_read
= size
- zstream
->avail_out
;
483 size
-= current_read
;
484 ret_read
+= current_read
;
485 gzip_stream
->buf_size
-= zstream
->next_in
- (gzip_stream
->buf
+gzip_stream
->buf_pos
);
486 gzip_stream
->buf_pos
= zstream
->next_in
-gzip_stream
->buf
;
487 if(zres
== Z_STREAM_END
) {
488 TRACE("end of data\n");
489 gzip_stream
->end_of_data
= TRUE
;
491 }else if(zres
!= Z_OK
) {
492 WARN("inflate failed %d: %s\n", zres
, debugstr_a(zstream
->msg
));
494 res
= ERROR_INTERNET_DECODING_FAILED
;
498 if(ret_read
&& read_mode
== READMODE_ASYNC
)
499 read_mode
= READMODE_NOBLOCK
;
502 TRACE("read %u bytes\n", ret_read
);
507 static BOOL
gzip_drain_content(data_stream_t
*stream
, http_request_t
*req
)
509 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
510 return gzip_stream
->parent_stream
->vtbl
->drain_content(gzip_stream
->parent_stream
, req
);
513 static void gzip_destroy(data_stream_t
*stream
)
515 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
517 destroy_data_stream(gzip_stream
->parent_stream
);
519 if(!gzip_stream
->end_of_data
)
520 inflateEnd(&gzip_stream
->zstream
);
521 heap_free(gzip_stream
);
524 static const data_stream_vtbl_t gzip_stream_vtbl
= {
532 static voidpf
wininet_zalloc(voidpf opaque
, uInt items
, uInt size
)
534 return heap_alloc(items
*size
);
537 static void wininet_zfree(voidpf opaque
, voidpf address
)
542 static DWORD
init_gzip_stream(http_request_t
*req
)
544 gzip_stream_t
*gzip_stream
;
547 gzip_stream
= heap_alloc_zero(sizeof(gzip_stream_t
));
549 return ERROR_OUTOFMEMORY
;
551 gzip_stream
->stream
.vtbl
= &gzip_stream_vtbl
;
552 gzip_stream
->zstream
.zalloc
= wininet_zalloc
;
553 gzip_stream
->zstream
.zfree
= wininet_zfree
;
555 zres
= inflateInit2(&gzip_stream
->zstream
, 0x1f);
557 ERR("inflateInit failed: %d\n", zres
);
558 heap_free(gzip_stream
);
559 return ERROR_OUTOFMEMORY
;
562 index
= HTTP_GetCustomHeaderIndex(req
, szContent_Length
, 0, FALSE
);
564 HTTP_DeleteCustomHeader(req
, index
);
567 memcpy(gzip_stream
->buf
, req
->read_buf
+req
->read_pos
, req
->read_size
);
568 gzip_stream
->buf_size
= req
->read_size
;
569 req
->read_pos
= req
->read_size
= 0;
572 req
->read_gzip
= TRUE
;
573 gzip_stream
->parent_stream
= req
->data_stream
;
574 req
->data_stream
= &gzip_stream
->stream
;
575 return ERROR_SUCCESS
;
580 static DWORD
init_gzip_stream(http_request_t
*req
)
582 ERR("gzip stream not supported, missing zlib.\n");
583 return ERROR_SUCCESS
;
588 /***********************************************************************
589 * HTTP_Tokenize (internal)
591 * Tokenize a string, allocating memory for the tokens.
593 static LPWSTR
* HTTP_Tokenize(LPCWSTR string
, LPCWSTR token_string
)
595 LPWSTR
* token_array
;
602 /* empty string has no tokens */
606 for (i
= 0; string
[i
]; i
++)
608 if (!strncmpW(string
+i
, token_string
, strlenW(token_string
)))
612 /* we want to skip over separators, but not the null terminator */
613 for (j
= 0; j
< strlenW(token_string
) - 1; j
++)
621 /* add 1 for terminating NULL */
622 token_array
= heap_alloc((tokens
+1) * sizeof(*token_array
));
623 token_array
[tokens
] = NULL
;
626 for (i
= 0; i
< tokens
; i
++)
629 next_token
= strstrW(string
, token_string
);
630 if (!next_token
) next_token
= string
+strlenW(string
);
631 len
= next_token
- string
;
632 token_array
[i
] = heap_alloc((len
+1)*sizeof(WCHAR
));
633 memcpy(token_array
[i
], string
, len
*sizeof(WCHAR
));
634 token_array
[i
][len
] = '\0';
635 string
= next_token
+strlenW(token_string
);
640 /***********************************************************************
641 * HTTP_FreeTokens (internal)
643 * Frees memory returned from HTTP_Tokenize.
645 static void HTTP_FreeTokens(LPWSTR
* token_array
)
648 for (i
= 0; token_array
[i
]; i
++) heap_free(token_array
[i
]);
649 heap_free(token_array
);
652 static void HTTP_FixURL(http_request_t
*request
)
654 static const WCHAR szSlash
[] = { '/',0 };
655 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/', 0 };
657 /* If we don't have a path we set it to root */
658 if (NULL
== request
->path
)
659 request
->path
= heap_strdupW(szSlash
);
660 else /* remove \r and \n*/
662 int nLen
= strlenW(request
->path
);
663 while ((nLen
>0 ) && ((request
->path
[nLen
-1] == '\r')||(request
->path
[nLen
-1] == '\n')))
666 request
->path
[nLen
]='\0';
668 /* Replace '\' with '/' */
671 if (request
->path
[nLen
] == '\\') request
->path
[nLen
]='/';
675 if(CSTR_EQUAL
!= CompareStringW( LOCALE_INVARIANT
, NORM_IGNORECASE
,
676 request
->path
, strlenW(request
->path
), szHttp
, strlenW(szHttp
) )
677 && request
->path
[0] != '/') /* not an absolute path ?? --> fix it !! */
679 WCHAR
*fixurl
= heap_alloc((strlenW(request
->path
) + 2)*sizeof(WCHAR
));
681 strcpyW(fixurl
+ 1, request
->path
);
682 heap_free( request
->path
);
683 request
->path
= fixurl
;
687 static LPWSTR
HTTP_BuildHeaderRequestString( http_request_t
*request
, LPCWSTR verb
, LPCWSTR path
, LPCWSTR version
)
689 LPWSTR requestString
;
695 static const WCHAR szSpace
[] = { ' ',0 };
696 static const WCHAR szColon
[] = { ':',' ',0 };
697 static const WCHAR sztwocrlf
[] = {'\r','\n','\r','\n', 0};
699 /* allocate space for an array of all the string pointers to be added */
700 len
= (request
->nCustHeaders
)*4 + 10;
701 req
= heap_alloc(len
*sizeof(LPCWSTR
));
703 /* add the verb, path and HTTP version string */
711 /* Append custom request headers */
712 for (i
= 0; i
< request
->nCustHeaders
; i
++)
714 if (request
->custHeaders
[i
].wFlags
& HDR_ISREQUEST
)
717 req
[n
++] = request
->custHeaders
[i
].lpszField
;
719 req
[n
++] = request
->custHeaders
[i
].lpszValue
;
721 TRACE("Adding custom header %s (%s)\n",
722 debugstr_w(request
->custHeaders
[i
].lpszField
),
723 debugstr_w(request
->custHeaders
[i
].lpszValue
));
728 ERR("oops. buffer overrun\n");
731 requestString
= HTTP_build_req( req
, 4 );
735 * Set (header) termination string for request
736 * Make sure there's exactly two new lines at the end of the request
738 p
= &requestString
[strlenW(requestString
)-1];
739 while ( (*p
== '\n') || (*p
== '\r') )
741 strcpyW( p
+1, sztwocrlf
);
743 return requestString
;
746 static void HTTP_ProcessCookies( http_request_t
*request
)
750 LPHTTPHEADERW setCookieHeader
;
752 if(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
)
755 while((HeaderIndex
= HTTP_GetCustomHeaderIndex(request
, szSet_Cookie
, numCookies
++, FALSE
)) != -1)
761 setCookieHeader
= &request
->custHeaders
[HeaderIndex
];
763 if (!setCookieHeader
->lpszValue
)
766 host
= HTTP_GetHeader(request
, hostW
);
770 data
= strchrW(setCookieHeader
->lpszValue
, '=');
774 name
= heap_strndupW(setCookieHeader
->lpszValue
, data
-setCookieHeader
->lpszValue
);
779 set_cookie(host
->lpszValue
, request
->path
, name
, data
);
784 static void strip_spaces(LPWSTR start
)
789 while (*str
== ' ' && *str
!= '\0')
793 memmove(start
, str
, sizeof(WCHAR
) * (strlenW(str
) + 1));
795 end
= start
+ strlenW(start
) - 1;
796 while (end
>= start
&& *end
== ' ')
803 static inline BOOL
is_basic_auth_value( LPCWSTR pszAuthValue
, LPWSTR
*pszRealm
)
805 static const WCHAR szBasic
[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
806 static const WCHAR szRealm
[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
808 is_basic
= !strncmpiW(pszAuthValue
, szBasic
, ARRAYSIZE(szBasic
)) &&
809 ((pszAuthValue
[ARRAYSIZE(szBasic
)] == ' ') || !pszAuthValue
[ARRAYSIZE(szBasic
)]);
810 if (is_basic
&& pszRealm
)
813 LPCWSTR ptr
= &pszAuthValue
[ARRAYSIZE(szBasic
)];
817 token
= strchrW(ptr
,'=');
821 while (*realm
== ' ' && *realm
!= '\0')
823 if(!strncmpiW(realm
, szRealm
, ARRAYSIZE(szRealm
)) &&
824 (realm
[ARRAYSIZE(szRealm
)] == ' ' || realm
[ARRAYSIZE(szRealm
)] == '='))
827 while (*token
== ' ' && *token
!= '\0')
831 *pszRealm
= heap_strdupW(token
);
832 strip_spaces(*pszRealm
);
839 static void destroy_authinfo( struct HttpAuthInfo
*authinfo
)
841 if (!authinfo
) return;
843 if (SecIsValidHandle(&authinfo
->ctx
))
844 DeleteSecurityContext(&authinfo
->ctx
);
845 if (SecIsValidHandle(&authinfo
->cred
))
846 FreeCredentialsHandle(&authinfo
->cred
);
848 heap_free(authinfo
->auth_data
);
849 heap_free(authinfo
->scheme
);
853 static UINT
retrieve_cached_basic_authorization(LPWSTR host
, LPWSTR realm
, LPSTR
*auth_data
)
855 basicAuthorizationData
*ad
;
858 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host
),debugstr_w(realm
));
860 EnterCriticalSection(&authcache_cs
);
861 LIST_FOR_EACH_ENTRY(ad
, &basicAuthorizationCache
, basicAuthorizationData
, entry
)
863 if (!strcmpiW(host
,ad
->host
) && !strcmpW(realm
,ad
->realm
))
865 TRACE("Authorization found in cache\n");
866 *auth_data
= heap_alloc(ad
->authorizationLen
);
867 memcpy(*auth_data
,ad
->authorization
,ad
->authorizationLen
);
868 rc
= ad
->authorizationLen
;
872 LeaveCriticalSection(&authcache_cs
);
876 static void cache_basic_authorization(LPWSTR host
, LPWSTR realm
, LPSTR auth_data
, UINT auth_data_len
)
879 basicAuthorizationData
* ad
= NULL
;
881 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host
),debugstr_w(realm
),debugstr_an(auth_data
,auth_data_len
));
883 EnterCriticalSection(&authcache_cs
);
884 LIST_FOR_EACH(cursor
, &basicAuthorizationCache
)
886 basicAuthorizationData
*check
= LIST_ENTRY(cursor
,basicAuthorizationData
,entry
);
887 if (!strcmpiW(host
,check
->host
) && !strcmpW(realm
,check
->realm
))
896 TRACE("Found match in cache, replacing\n");
897 heap_free(ad
->authorization
);
898 ad
->authorization
= heap_alloc(auth_data_len
);
899 memcpy(ad
->authorization
, auth_data
, auth_data_len
);
900 ad
->authorizationLen
= auth_data_len
;
904 ad
= heap_alloc(sizeof(basicAuthorizationData
));
905 ad
->host
= heap_strdupW(host
);
906 ad
->realm
= heap_strdupW(realm
);
907 ad
->authorization
= heap_alloc(auth_data_len
);
908 memcpy(ad
->authorization
, auth_data
, auth_data_len
);
909 ad
->authorizationLen
= auth_data_len
;
910 list_add_head(&basicAuthorizationCache
,&ad
->entry
);
911 TRACE("authorization cached\n");
913 LeaveCriticalSection(&authcache_cs
);
916 static BOOL
retrieve_cached_authorization(LPWSTR host
, LPWSTR scheme
,
917 SEC_WINNT_AUTH_IDENTITY_W
*nt_auth_identity
)
919 authorizationData
*ad
;
921 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host
), debugstr_w(scheme
));
923 EnterCriticalSection(&authcache_cs
);
924 LIST_FOR_EACH_ENTRY(ad
, &authorizationCache
, authorizationData
, entry
) {
925 if(!strcmpiW(host
, ad
->host
) && !strcmpiW(scheme
, ad
->scheme
)) {
926 TRACE("Authorization found in cache\n");
928 nt_auth_identity
->User
= heap_strdupW(ad
->user
);
929 nt_auth_identity
->Password
= heap_strdupW(ad
->password
);
930 nt_auth_identity
->Domain
= heap_alloc(sizeof(WCHAR
)*ad
->domain_len
);
931 if(!nt_auth_identity
->User
|| !nt_auth_identity
->Password
||
932 (!nt_auth_identity
->Domain
&& ad
->domain_len
)) {
933 heap_free(nt_auth_identity
->User
);
934 heap_free(nt_auth_identity
->Password
);
935 heap_free(nt_auth_identity
->Domain
);
939 nt_auth_identity
->Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
940 nt_auth_identity
->UserLength
= ad
->user_len
;
941 nt_auth_identity
->PasswordLength
= ad
->password_len
;
942 memcpy(nt_auth_identity
->Domain
, ad
->domain
, sizeof(WCHAR
)*ad
->domain_len
);
943 nt_auth_identity
->DomainLength
= ad
->domain_len
;
944 LeaveCriticalSection(&authcache_cs
);
948 LeaveCriticalSection(&authcache_cs
);
953 static void cache_authorization(LPWSTR host
, LPWSTR scheme
,
954 SEC_WINNT_AUTH_IDENTITY_W
*nt_auth_identity
)
956 authorizationData
*ad
;
959 TRACE("Caching authorization for %s:%s\n", debugstr_w(host
), debugstr_w(scheme
));
961 EnterCriticalSection(&authcache_cs
);
962 LIST_FOR_EACH_ENTRY(ad
, &authorizationCache
, authorizationData
, entry
)
963 if(!strcmpiW(host
, ad
->host
) && !strcmpiW(scheme
, ad
->scheme
)) {
970 heap_free(ad
->password
);
971 heap_free(ad
->domain
);
973 ad
= heap_alloc(sizeof(authorizationData
));
975 LeaveCriticalSection(&authcache_cs
);
979 ad
->host
= heap_strdupW(host
);
980 ad
->scheme
= heap_strdupW(scheme
);
981 list_add_head(&authorizationCache
, &ad
->entry
);
984 ad
->user
= heap_strndupW(nt_auth_identity
->User
, nt_auth_identity
->UserLength
);
985 ad
->password
= heap_strndupW(nt_auth_identity
->Password
, nt_auth_identity
->PasswordLength
);
986 ad
->domain
= heap_strndupW(nt_auth_identity
->Domain
, nt_auth_identity
->DomainLength
);
987 ad
->user_len
= nt_auth_identity
->UserLength
;
988 ad
->password_len
= nt_auth_identity
->PasswordLength
;
989 ad
->domain_len
= nt_auth_identity
->DomainLength
;
991 if(!ad
->host
|| !ad
->scheme
|| !ad
->user
|| !ad
->password
992 || (nt_auth_identity
->Domain
&& !ad
->domain
)) {
994 heap_free(ad
->scheme
);
996 heap_free(ad
->password
);
997 heap_free(ad
->domain
);
998 list_remove(&ad
->entry
);
1002 LeaveCriticalSection(&authcache_cs
);
1005 static BOOL
HTTP_DoAuthorization( http_request_t
*request
, LPCWSTR pszAuthValue
,
1006 struct HttpAuthInfo
**ppAuthInfo
,
1007 LPWSTR domain_and_username
, LPWSTR password
,
1010 SECURITY_STATUS sec_status
;
1011 struct HttpAuthInfo
*pAuthInfo
= *ppAuthInfo
;
1013 LPWSTR szRealm
= NULL
;
1015 TRACE("%s\n", debugstr_w(pszAuthValue
));
1022 pAuthInfo
= heap_alloc(sizeof(*pAuthInfo
));
1026 SecInvalidateHandle(&pAuthInfo
->cred
);
1027 SecInvalidateHandle(&pAuthInfo
->ctx
);
1028 memset(&pAuthInfo
->exp
, 0, sizeof(pAuthInfo
->exp
));
1029 pAuthInfo
->attr
= 0;
1030 pAuthInfo
->auth_data
= NULL
;
1031 pAuthInfo
->auth_data_len
= 0;
1032 pAuthInfo
->finished
= FALSE
;
1034 if (is_basic_auth_value(pszAuthValue
,NULL
))
1036 static const WCHAR szBasic
[] = {'B','a','s','i','c',0};
1037 pAuthInfo
->scheme
= heap_strdupW(szBasic
);
1038 if (!pAuthInfo
->scheme
)
1040 heap_free(pAuthInfo
);
1047 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity
;
1049 pAuthInfo
->scheme
= heap_strdupW(pszAuthValue
);
1050 if (!pAuthInfo
->scheme
)
1052 heap_free(pAuthInfo
);
1056 if (domain_and_username
)
1058 WCHAR
*user
= strchrW(domain_and_username
, '\\');
1059 WCHAR
*domain
= domain_and_username
;
1061 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1063 pAuthData
= &nt_auth_identity
;
1068 user
= domain_and_username
;
1072 nt_auth_identity
.Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
1073 nt_auth_identity
.User
= user
;
1074 nt_auth_identity
.UserLength
= strlenW(nt_auth_identity
.User
);
1075 nt_auth_identity
.Domain
= domain
;
1076 nt_auth_identity
.DomainLength
= domain
? user
- domain
- 1 : 0;
1077 nt_auth_identity
.Password
= password
;
1078 nt_auth_identity
.PasswordLength
= strlenW(nt_auth_identity
.Password
);
1080 cache_authorization(host
, pAuthInfo
->scheme
, &nt_auth_identity
);
1082 else if(retrieve_cached_authorization(host
, pAuthInfo
->scheme
, &nt_auth_identity
))
1083 pAuthData
= &nt_auth_identity
;
1085 /* use default credentials */
1088 sec_status
= AcquireCredentialsHandleW(NULL
, pAuthInfo
->scheme
,
1089 SECPKG_CRED_OUTBOUND
, NULL
,
1091 NULL
, &pAuthInfo
->cred
,
1094 if(pAuthData
&& !domain_and_username
) {
1095 heap_free(nt_auth_identity
.User
);
1096 heap_free(nt_auth_identity
.Domain
);
1097 heap_free(nt_auth_identity
.Password
);
1100 if (sec_status
== SEC_E_OK
)
1102 PSecPkgInfoW sec_pkg_info
;
1103 sec_status
= QuerySecurityPackageInfoW(pAuthInfo
->scheme
, &sec_pkg_info
);
1104 if (sec_status
== SEC_E_OK
)
1106 pAuthInfo
->max_token
= sec_pkg_info
->cbMaxToken
;
1107 FreeContextBuffer(sec_pkg_info
);
1110 if (sec_status
!= SEC_E_OK
)
1112 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1113 debugstr_w(pAuthInfo
->scheme
), sec_status
);
1114 heap_free(pAuthInfo
->scheme
);
1115 heap_free(pAuthInfo
);
1119 *ppAuthInfo
= pAuthInfo
;
1121 else if (pAuthInfo
->finished
)
1124 if ((strlenW(pszAuthValue
) < strlenW(pAuthInfo
->scheme
)) ||
1125 strncmpiW(pszAuthValue
, pAuthInfo
->scheme
, strlenW(pAuthInfo
->scheme
)))
1127 ERR("authentication scheme changed from %s to %s\n",
1128 debugstr_w(pAuthInfo
->scheme
), debugstr_w(pszAuthValue
));
1132 if (is_basic_auth_value(pszAuthValue
,&szRealm
))
1136 char *auth_data
= NULL
;
1137 UINT auth_data_len
= 0;
1139 TRACE("basic authentication realm %s\n",debugstr_w(szRealm
));
1141 if (!domain_and_username
)
1143 if (host
&& szRealm
)
1144 auth_data_len
= retrieve_cached_basic_authorization(host
, szRealm
,&auth_data
);
1145 if (auth_data_len
== 0)
1153 userlen
= WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, lstrlenW(domain_and_username
), NULL
, 0, NULL
, NULL
);
1154 passlen
= WideCharToMultiByte(CP_UTF8
, 0, password
, lstrlenW(password
), NULL
, 0, NULL
, NULL
);
1156 /* length includes a nul terminator, which will be re-used for the ':' */
1157 auth_data
= heap_alloc(userlen
+ 1 + passlen
);
1164 WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, -1, auth_data
, userlen
, NULL
, NULL
);
1165 auth_data
[userlen
] = ':';
1166 WideCharToMultiByte(CP_UTF8
, 0, password
, -1, &auth_data
[userlen
+1], passlen
, NULL
, NULL
);
1167 auth_data_len
= userlen
+ 1 + passlen
;
1168 if (host
&& szRealm
)
1169 cache_basic_authorization(host
, szRealm
, auth_data
, auth_data_len
);
1172 pAuthInfo
->auth_data
= auth_data
;
1173 pAuthInfo
->auth_data_len
= auth_data_len
;
1174 pAuthInfo
->finished
= TRUE
;
1180 LPCWSTR pszAuthData
;
1181 SecBufferDesc out_desc
, in_desc
;
1183 unsigned char *buffer
;
1184 ULONG context_req
= ISC_REQ_CONNECTION
| ISC_REQ_USE_DCE_STYLE
|
1185 ISC_REQ_MUTUAL_AUTH
| ISC_REQ_DELEGATE
;
1187 in
.BufferType
= SECBUFFER_TOKEN
;
1191 in_desc
.ulVersion
= 0;
1192 in_desc
.cBuffers
= 1;
1193 in_desc
.pBuffers
= &in
;
1195 pszAuthData
= pszAuthValue
+ strlenW(pAuthInfo
->scheme
);
1196 if (*pszAuthData
== ' ')
1199 in
.cbBuffer
= HTTP_DecodeBase64(pszAuthData
, NULL
);
1200 in
.pvBuffer
= heap_alloc(in
.cbBuffer
);
1201 HTTP_DecodeBase64(pszAuthData
, in
.pvBuffer
);
1204 buffer
= heap_alloc(pAuthInfo
->max_token
);
1206 out
.BufferType
= SECBUFFER_TOKEN
;
1207 out
.cbBuffer
= pAuthInfo
->max_token
;
1208 out
.pvBuffer
= buffer
;
1210 out_desc
.ulVersion
= 0;
1211 out_desc
.cBuffers
= 1;
1212 out_desc
.pBuffers
= &out
;
1214 sec_status
= InitializeSecurityContextW(first
? &pAuthInfo
->cred
: NULL
,
1215 first
? NULL
: &pAuthInfo
->ctx
,
1216 first
? request
->server
->name
: NULL
,
1217 context_req
, 0, SECURITY_NETWORK_DREP
,
1218 in
.pvBuffer
? &in_desc
: NULL
,
1219 0, &pAuthInfo
->ctx
, &out_desc
,
1220 &pAuthInfo
->attr
, &pAuthInfo
->exp
);
1221 if (sec_status
== SEC_E_OK
)
1223 pAuthInfo
->finished
= TRUE
;
1224 pAuthInfo
->auth_data
= out
.pvBuffer
;
1225 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
1226 TRACE("sending last auth packet\n");
1228 else if (sec_status
== SEC_I_CONTINUE_NEEDED
)
1230 pAuthInfo
->auth_data
= out
.pvBuffer
;
1231 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
1232 TRACE("sending next auth packet\n");
1236 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status
);
1237 heap_free(out
.pvBuffer
);
1238 destroy_authinfo(pAuthInfo
);
1247 /***********************************************************************
1248 * HTTP_HttpAddRequestHeadersW (internal)
1250 static DWORD
HTTP_HttpAddRequestHeadersW(http_request_t
*request
,
1251 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1256 DWORD len
, res
= ERROR_HTTP_INVALID_HEADER
;
1258 TRACE("copying header: %s\n", debugstr_wn(lpszHeader
, dwHeaderLength
));
1260 if( dwHeaderLength
== ~0U )
1261 len
= strlenW(lpszHeader
);
1263 len
= dwHeaderLength
;
1264 buffer
= heap_alloc(sizeof(WCHAR
)*(len
+1));
1265 lstrcpynW( buffer
, lpszHeader
, len
+ 1);
1271 LPWSTR
* pFieldAndValue
;
1273 lpszEnd
= lpszStart
;
1275 while (*lpszEnd
!= '\0')
1277 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
1282 if (*lpszStart
== '\0')
1285 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
1288 lpszEnd
++; /* Jump over newline */
1290 TRACE("interpreting header %s\n", debugstr_w(lpszStart
));
1291 if (*lpszStart
== '\0')
1293 /* Skip 0-length headers */
1294 lpszStart
= lpszEnd
;
1295 res
= ERROR_SUCCESS
;
1298 pFieldAndValue
= HTTP_InterpretHttpHeader(lpszStart
);
1301 res
= HTTP_VerifyValidHeader(request
, pFieldAndValue
[0]);
1302 if (res
== ERROR_SUCCESS
)
1303 res
= HTTP_ProcessHeader(request
, pFieldAndValue
[0],
1304 pFieldAndValue
[1], dwModifier
| HTTP_ADDHDR_FLAG_REQ
);
1305 HTTP_FreeTokens(pFieldAndValue
);
1308 lpszStart
= lpszEnd
;
1309 } while (res
== ERROR_SUCCESS
);
1315 /***********************************************************************
1316 * HttpAddRequestHeadersW (WININET.@)
1318 * Adds one or more HTTP header to the request handler
1321 * On Windows if dwHeaderLength includes the trailing '\0', then
1322 * HttpAddRequestHeadersW() adds it too. However this results in an
1323 * invalid HTTP header which is rejected by some servers so we probably
1324 * don't need to match Windows on that point.
1331 BOOL WINAPI
HttpAddRequestHeadersW(HINTERNET hHttpRequest
,
1332 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1334 http_request_t
*request
;
1335 DWORD res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
1337 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_wn(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
1342 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
1343 if (request
&& request
->hdr
.htype
== WH_HHTTPREQ
)
1344 res
= HTTP_HttpAddRequestHeadersW( request
, lpszHeader
, dwHeaderLength
, dwModifier
);
1346 WININET_Release( &request
->hdr
);
1348 if(res
!= ERROR_SUCCESS
)
1350 return res
== ERROR_SUCCESS
;
1353 /***********************************************************************
1354 * HttpAddRequestHeadersA (WININET.@)
1356 * Adds one or more HTTP header to the request handler
1363 BOOL WINAPI
HttpAddRequestHeadersA(HINTERNET hHttpRequest
,
1364 LPCSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1370 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_an(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
1372 len
= MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, NULL
, 0 );
1373 hdr
= heap_alloc(len
*sizeof(WCHAR
));
1374 MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, hdr
, len
);
1375 if( dwHeaderLength
!= ~0U )
1376 dwHeaderLength
= len
;
1378 r
= HttpAddRequestHeadersW( hHttpRequest
, hdr
, dwHeaderLength
, dwModifier
);
1384 static void free_accept_types( WCHAR
**accept_types
)
1386 WCHAR
*ptr
, **types
= accept_types
;
1389 while ((ptr
= *types
))
1394 heap_free( accept_types
);
1397 static WCHAR
**convert_accept_types( const char **accept_types
)
1400 const char **types
= accept_types
;
1402 BOOL invalid_pointer
= FALSE
;
1404 if (!types
) return NULL
;
1410 /* find out how many there are */
1411 if (*types
&& **types
)
1413 TRACE("accept type: %s\n", debugstr_a(*types
));
1419 WARN("invalid accept type pointer\n");
1420 invalid_pointer
= TRUE
;
1425 if (invalid_pointer
) return NULL
;
1426 if (!(typesW
= heap_alloc( sizeof(WCHAR
*) * (count
+ 1) ))) return NULL
;
1428 types
= accept_types
;
1431 if (*types
&& **types
) typesW
[count
++] = heap_strdupAtoW( *types
);
1434 typesW
[count
] = NULL
;
1438 /***********************************************************************
1439 * HttpOpenRequestA (WININET.@)
1441 * Open a HTTP request handle
1444 * HINTERNET a HTTP request handle on success
1448 HINTERNET WINAPI
HttpOpenRequestA(HINTERNET hHttpSession
,
1449 LPCSTR lpszVerb
, LPCSTR lpszObjectName
, LPCSTR lpszVersion
,
1450 LPCSTR lpszReferrer
, LPCSTR
*lpszAcceptTypes
,
1451 DWORD dwFlags
, DWORD_PTR dwContext
)
1453 LPWSTR szVerb
= NULL
, szObjectName
= NULL
;
1454 LPWSTR szVersion
= NULL
, szReferrer
= NULL
, *szAcceptTypes
= NULL
;
1455 HINTERNET rc
= FALSE
;
1457 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
1458 debugstr_a(lpszVerb
), debugstr_a(lpszObjectName
),
1459 debugstr_a(lpszVersion
), debugstr_a(lpszReferrer
), lpszAcceptTypes
,
1460 dwFlags
, dwContext
);
1464 szVerb
= heap_strdupAtoW(lpszVerb
);
1471 szObjectName
= heap_strdupAtoW(lpszObjectName
);
1472 if ( !szObjectName
)
1478 szVersion
= heap_strdupAtoW(lpszVersion
);
1485 szReferrer
= heap_strdupAtoW(lpszReferrer
);
1490 szAcceptTypes
= convert_accept_types( lpszAcceptTypes
);
1491 rc
= HttpOpenRequestW(hHttpSession
, szVerb
, szObjectName
, szVersion
, szReferrer
,
1492 (const WCHAR
**)szAcceptTypes
, dwFlags
, dwContext
);
1495 free_accept_types(szAcceptTypes
);
1496 heap_free(szReferrer
);
1497 heap_free(szVersion
);
1498 heap_free(szObjectName
);
1503 /***********************************************************************
1506 static UINT
HTTP_EncodeBase64( LPCSTR bin
, unsigned int len
, LPWSTR base64
)
1509 static const CHAR HTTP_Base64Enc
[] =
1510 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1514 /* first 6 bits, all from bin[0] */
1515 base64
[n
++] = HTTP_Base64Enc
[(bin
[0] & 0xfc) >> 2];
1516 x
= (bin
[0] & 3) << 4;
1518 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1521 base64
[n
++] = HTTP_Base64Enc
[x
];
1526 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[1]&0xf0) >> 4 ) ];
1527 x
= ( bin
[1] & 0x0f ) << 2;
1529 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1532 base64
[n
++] = HTTP_Base64Enc
[x
];
1536 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[2]&0xc0 ) >> 6 ) ];
1538 /* last 6 bits, all from bin [2] */
1539 base64
[n
++] = HTTP_Base64Enc
[ bin
[2] & 0x3f ];
1547 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1548 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1549 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1550 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1551 static const signed char HTTP_Base64Dec
[256] =
1553 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1554 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1555 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1556 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1557 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1558 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1559 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1560 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1561 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1562 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1563 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1564 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1565 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1566 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1567 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1568 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1569 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1570 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1571 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1572 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1573 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1574 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1575 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1576 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1577 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1578 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1582 /***********************************************************************
1585 static UINT
HTTP_DecodeBase64( LPCWSTR base64
, LPSTR bin
)
1593 if (base64
[0] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1594 ((in
[0] = HTTP_Base64Dec
[base64
[0]]) == -1) ||
1595 base64
[1] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1596 ((in
[1] = HTTP_Base64Dec
[base64
[1]]) == -1))
1598 WARN("invalid base64: %s\n", debugstr_w(base64
));
1602 bin
[n
] = (unsigned char) (in
[0] << 2 | in
[1] >> 4);
1605 if ((base64
[2] == '=') && (base64
[3] == '='))
1607 if (base64
[2] > ARRAYSIZE(HTTP_Base64Dec
) ||
1608 ((in
[2] = HTTP_Base64Dec
[base64
[2]]) == -1))
1610 WARN("invalid base64: %s\n", debugstr_w(&base64
[2]));
1614 bin
[n
] = (unsigned char) (in
[1] << 4 | in
[2] >> 2);
1617 if (base64
[3] == '=')
1619 if (base64
[3] > ARRAYSIZE(HTTP_Base64Dec
) ||
1620 ((in
[3] = HTTP_Base64Dec
[base64
[3]]) == -1))
1622 WARN("invalid base64: %s\n", debugstr_w(&base64
[3]));
1626 bin
[n
] = (unsigned char) (((in
[2] << 6) & 0xc0) | in
[3]);
1635 /***********************************************************************
1636 * HTTP_InsertAuthorization
1638 * Insert or delete the authorization field in the request header.
1640 static BOOL
HTTP_InsertAuthorization( http_request_t
*request
, struct HttpAuthInfo
*pAuthInfo
, LPCWSTR header
)
1644 static const WCHAR wszSpace
[] = {' ',0};
1645 static const WCHAR wszBasic
[] = {'B','a','s','i','c',0};
1647 WCHAR
*authorization
= NULL
;
1649 if (pAuthInfo
->auth_data_len
)
1651 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1652 len
= strlenW(pAuthInfo
->scheme
)+1+((pAuthInfo
->auth_data_len
+2)*4)/3;
1653 authorization
= heap_alloc((len
+1)*sizeof(WCHAR
));
1657 strcpyW(authorization
, pAuthInfo
->scheme
);
1658 strcatW(authorization
, wszSpace
);
1659 HTTP_EncodeBase64(pAuthInfo
->auth_data
,
1660 pAuthInfo
->auth_data_len
,
1661 authorization
+strlenW(authorization
));
1663 /* clear the data as it isn't valid now that it has been sent to the
1664 * server, unless it's Basic authentication which doesn't do
1665 * connection tracking */
1666 if (strcmpiW(pAuthInfo
->scheme
, wszBasic
))
1668 heap_free(pAuthInfo
->auth_data
);
1669 pAuthInfo
->auth_data
= NULL
;
1670 pAuthInfo
->auth_data_len
= 0;
1674 TRACE("Inserting authorization: %s\n", debugstr_w(authorization
));
1676 HTTP_ProcessHeader(request
, header
, authorization
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
1677 heap_free(authorization
);
1682 static WCHAR
*build_proxy_path_url(http_request_t
*req
)
1687 len
= strlenW(req
->server
->scheme_host_port
);
1688 size
= len
+ strlenW(req
->path
) + 1;
1689 if(*req
->path
!= '/')
1691 url
= heap_alloc(size
* sizeof(WCHAR
));
1695 memcpy(url
, req
->server
->scheme_host_port
, len
*sizeof(WCHAR
));
1696 if(*req
->path
!= '/')
1699 strcpyW(url
+len
, req
->path
);
1701 TRACE("url=%s\n", debugstr_w(url
));
1705 /***********************************************************************
1706 * HTTP_DealWithProxy
1708 static BOOL
HTTP_DealWithProxy(appinfo_t
*hIC
, http_session_t
*session
, http_request_t
*request
)
1710 WCHAR buf
[INTERNET_MAX_HOST_NAME_LENGTH
];
1711 WCHAR protoProxy
[INTERNET_MAX_URL_LENGTH
];
1712 DWORD protoProxyLen
= INTERNET_MAX_URL_LENGTH
;
1713 WCHAR proxy
[INTERNET_MAX_URL_LENGTH
];
1714 static WCHAR szNul
[] = { 0 };
1715 URL_COMPONENTSW UrlComponents
;
1716 server_t
*new_server
;
1717 static const WCHAR protoHttp
[] = { 'h','t','t','p',0 };
1718 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/',0 };
1719 static const WCHAR szFormat
[] = { 'h','t','t','p',':','/','/','%','s',0 };
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 new_server
= get_server(UrlComponents
.lpszHostName
, UrlComponents
.nPort
, UrlComponents
.nScheme
== INTERNET_SCHEME_HTTPS
, TRUE
);
1745 request
->proxy
= new_server
;
1747 TRACE("proxy server=%s port=%d\n", debugstr_w(new_server
->name
), new_server
->port
);
1751 static DWORD
HTTP_ResolveName(http_request_t
*request
)
1753 server_t
*server
= request
->proxy
? request
->proxy
: request
->server
;
1757 if(server
->addr_len
)
1758 return ERROR_SUCCESS
;
1760 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
1761 INTERNET_STATUS_RESOLVING_NAME
,
1763 (strlenW(server
->name
)+1) * sizeof(WCHAR
));
1765 addr_len
= sizeof(server
->addr
);
1766 if (!GetAddress(server
->name
, server
->port
, (struct sockaddr
*)&server
->addr
, &addr_len
))
1767 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
1769 switch(server
->addr
.ss_family
) {
1771 addr
= &((struct sockaddr_in
*)&server
->addr
)->sin_addr
;
1774 addr
= &((struct sockaddr_in6
*)&server
->addr
)->sin6_addr
;
1777 WARN("unsupported family %d\n", server
->addr
.ss_family
);
1778 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
1781 server
->addr_len
= addr_len
;
1782 inet_ntop(server
->addr
.ss_family
, addr
, server
->addr_str
, sizeof(server
->addr_str
));
1783 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
1784 INTERNET_STATUS_NAME_RESOLVED
,
1785 server
->addr_str
, strlen(server
->addr_str
)+1);
1787 TRACE("resolved %s to %s\n", debugstr_w(server
->name
), server
->addr_str
);
1788 return ERROR_SUCCESS
;
1791 static BOOL
HTTP_GetRequestURL(http_request_t
*req
, LPWSTR buf
)
1793 static const WCHAR http
[] = { 'h','t','t','p',':','/','/',0 };
1794 static const WCHAR https
[] = { 'h','t','t','p','s',':','/','/',0 };
1795 static const WCHAR slash
[] = { '/',0 };
1796 LPHTTPHEADERW host_header
;
1799 host_header
= HTTP_GetHeader(req
, hostW
);
1803 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1807 strcpyW(buf
, scheme
);
1808 strcatW(buf
, host_header
->lpszValue
);
1809 if (req
->path
[0] != '/')
1810 strcatW(buf
, slash
);
1811 strcatW(buf
, req
->path
);
1816 /***********************************************************************
1817 * HTTPREQ_Destroy (internal)
1819 * Deallocate request handle
1822 static void HTTPREQ_Destroy(object_header_t
*hdr
)
1824 http_request_t
*request
= (http_request_t
*) hdr
;
1829 if(request
->hCacheFile
) {
1830 CloseHandle(request
->hCacheFile
);
1831 DeleteFileW(request
->cacheFile
);
1833 heap_free(request
->cacheFile
);
1835 request
->read_section
.DebugInfo
->Spare
[0] = 0;
1836 DeleteCriticalSection( &request
->read_section
);
1837 WININET_Release(&request
->session
->hdr
);
1839 destroy_authinfo(request
->authInfo
);
1840 destroy_authinfo(request
->proxyAuthInfo
);
1843 server_release(request
->server
);
1845 server_release(request
->proxy
);
1847 heap_free(request
->path
);
1848 heap_free(request
->verb
);
1849 heap_free(request
->rawHeaders
);
1850 heap_free(request
->version
);
1851 heap_free(request
->statusText
);
1853 for (i
= 0; i
< request
->nCustHeaders
; i
++)
1855 heap_free(request
->custHeaders
[i
].lpszField
);
1856 heap_free(request
->custHeaders
[i
].lpszValue
);
1858 destroy_data_stream(request
->data_stream
);
1859 heap_free(request
->custHeaders
);
1862 static void http_release_netconn(http_request_t
*req
, BOOL reuse
)
1864 TRACE("%p %p\n",req
, req
->netconn
);
1869 if(reuse
&& req
->netconn
->keep_alive
) {
1872 EnterCriticalSection(&connection_pool_cs
);
1874 list_add_head(&req
->netconn
->server
->conn_pool
, &req
->netconn
->pool_entry
);
1875 req
->netconn
->keep_until
= GetTickCount64() + COLLECT_TIME
;
1876 req
->netconn
= NULL
;
1878 run_collector
= !collector_running
;
1879 collector_running
= TRUE
;
1881 LeaveCriticalSection(&connection_pool_cs
);
1884 HANDLE thread
= NULL
;
1887 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
, (const WCHAR
*)WININET_hModule
, &module
);
1889 thread
= CreateThread(NULL
, 0, collect_connections_proc
, NULL
, 0, NULL
);
1891 EnterCriticalSection(&connection_pool_cs
);
1892 collector_running
= FALSE
;
1893 LeaveCriticalSection(&connection_pool_cs
);
1896 FreeLibrary(module
);
1899 CloseHandle(thread
);
1904 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
1905 INTERNET_STATUS_CLOSING_CONNECTION
, 0, 0);
1907 free_netconn(req
->netconn
);
1908 req
->netconn
= NULL
;
1910 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
1911 INTERNET_STATUS_CONNECTION_CLOSED
, 0, 0);
1914 static BOOL
HTTP_KeepAlive(http_request_t
*request
)
1916 WCHAR szVersion
[10];
1917 WCHAR szConnectionResponse
[20];
1918 DWORD dwBufferSize
= sizeof(szVersion
);
1919 BOOL keepalive
= FALSE
;
1921 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1922 * the connection is keep-alive by default */
1923 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_VERSION
, szVersion
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1924 && !strcmpiW(szVersion
, g_szHttp1_1
))
1929 dwBufferSize
= sizeof(szConnectionResponse
);
1930 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_PROXY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1931 || HTTP_HttpQueryInfoW(request
, HTTP_QUERY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
)
1933 keepalive
= !strcmpiW(szConnectionResponse
, szKeepAlive
);
1939 static void HTTPREQ_CloseConnection(object_header_t
*hdr
)
1941 http_request_t
*req
= (http_request_t
*)hdr
;
1943 http_release_netconn(req
, drain_content(req
, FALSE
));
1946 static DWORD
HTTPREQ_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
1948 http_request_t
*req
= (http_request_t
*)hdr
;
1951 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO
:
1953 INTERNET_DIAGNOSTIC_SOCKET_INFO
*info
= buffer
;
1955 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1957 if (*size
< sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
))
1958 return ERROR_INSUFFICIENT_BUFFER
;
1959 *size
= sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
);
1960 /* FIXME: can't get a SOCKET from our connection since we don't use
1964 /* FIXME: get source port from req->netConnection */
1965 info
->SourcePort
= 0;
1966 info
->DestPort
= req
->server
->port
;
1968 if (HTTP_KeepAlive(req
))
1969 info
->Flags
|= IDSI_FLAG_KEEP_ALIVE
;
1971 info
->Flags
|= IDSI_FLAG_PROXY
;
1972 if (req
->netconn
->useSSL
)
1973 info
->Flags
|= IDSI_FLAG_SECURE
;
1975 return ERROR_SUCCESS
;
1979 TRACE("Queried undocumented option 98, forwarding to INTERNET_OPTION_SECURITY_FLAGS\n");
1981 case INTERNET_OPTION_SECURITY_FLAGS
:
1985 if (*size
< sizeof(ULONG
))
1986 return ERROR_INSUFFICIENT_BUFFER
;
1988 *size
= sizeof(DWORD
);
1989 flags
= req
->netconn
? req
->netconn
->security_flags
: req
->security_flags
| req
->server
->security_flags
;
1990 *(DWORD
*)buffer
= flags
;
1992 TRACE("INTERNET_OPTION_SECURITY_FLAGS %x\n", flags
);
1993 return ERROR_SUCCESS
;
1996 case INTERNET_OPTION_HANDLE_TYPE
:
1997 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1999 if (*size
< sizeof(ULONG
))
2000 return ERROR_INSUFFICIENT_BUFFER
;
2002 *size
= sizeof(DWORD
);
2003 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_HTTP_REQUEST
;
2004 return ERROR_SUCCESS
;
2006 case INTERNET_OPTION_URL
: {
2007 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
2012 static const WCHAR httpW
[] = {'h','t','t','p',':','/','/',0};
2014 TRACE("INTERNET_OPTION_URL\n");
2016 host
= HTTP_GetHeader(req
, hostW
);
2017 strcpyW(url
, httpW
);
2018 strcatW(url
, host
->lpszValue
);
2019 if (NULL
!= (pch
= strchrW(url
+ strlenW(httpW
), ':')))
2021 strcatW(url
, req
->path
);
2023 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url
));
2026 len
= (strlenW(url
)+1) * sizeof(WCHAR
);
2028 return ERROR_INSUFFICIENT_BUFFER
;
2031 strcpyW(buffer
, url
);
2032 return ERROR_SUCCESS
;
2034 len
= WideCharToMultiByte(CP_ACP
, 0, url
, -1, buffer
, *size
, NULL
, NULL
);
2036 return ERROR_INSUFFICIENT_BUFFER
;
2039 return ERROR_SUCCESS
;
2043 case INTERNET_OPTION_CACHE_TIMESTAMPS
: {
2044 INTERNET_CACHE_ENTRY_INFOW
*info
;
2045 INTERNET_CACHE_TIMESTAMPS
*ts
= buffer
;
2046 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
2047 DWORD nbytes
, error
;
2050 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2052 if (*size
< sizeof(*ts
))
2054 *size
= sizeof(*ts
);
2055 return ERROR_INSUFFICIENT_BUFFER
;
2058 HTTP_GetRequestURL(req
, url
);
2059 ret
= GetUrlCacheEntryInfoW(url
, NULL
, &nbytes
);
2060 error
= GetLastError();
2061 if (!ret
&& error
== ERROR_INSUFFICIENT_BUFFER
)
2063 if (!(info
= heap_alloc(nbytes
)))
2064 return ERROR_OUTOFMEMORY
;
2066 GetUrlCacheEntryInfoW(url
, info
, &nbytes
);
2068 ts
->ftExpires
= info
->ExpireTime
;
2069 ts
->ftLastModified
= info
->LastModifiedTime
;
2072 *size
= sizeof(*ts
);
2073 return ERROR_SUCCESS
;
2078 case INTERNET_OPTION_DATAFILE_NAME
: {
2081 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2083 if(!req
->cacheFile
) {
2085 return ERROR_INTERNET_ITEM_NOT_FOUND
;
2089 req_size
= (lstrlenW(req
->cacheFile
)+1) * sizeof(WCHAR
);
2090 if(*size
< req_size
)
2091 return ERROR_INSUFFICIENT_BUFFER
;
2094 memcpy(buffer
, req
->cacheFile
, *size
);
2095 return ERROR_SUCCESS
;
2097 req_size
= WideCharToMultiByte(CP_ACP
, 0, req
->cacheFile
, -1, NULL
, 0, NULL
, NULL
);
2098 if (req_size
> *size
)
2099 return ERROR_INSUFFICIENT_BUFFER
;
2101 *size
= WideCharToMultiByte(CP_ACP
, 0, req
->cacheFile
,
2102 -1, buffer
, *size
, NULL
, NULL
);
2103 return ERROR_SUCCESS
;
2107 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT
: {
2108 PCCERT_CONTEXT context
;
2110 if(*size
< sizeof(INTERNET_CERTIFICATE_INFOA
)) {
2111 *size
= sizeof(INTERNET_CERTIFICATE_INFOA
);
2112 return ERROR_INSUFFICIENT_BUFFER
;
2115 context
= (PCCERT_CONTEXT
)NETCON_GetCert(req
->netconn
);
2117 INTERNET_CERTIFICATE_INFOA
*info
= (INTERNET_CERTIFICATE_INFOA
*)buffer
;
2120 memset(info
, 0, sizeof(*info
));
2121 info
->ftExpiry
= context
->pCertInfo
->NotAfter
;
2122 info
->ftStart
= context
->pCertInfo
->NotBefore
;
2123 len
= CertNameToStrA(context
->dwCertEncodingType
,
2124 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
|CERT_NAME_STR_CRLF_FLAG
, NULL
, 0);
2125 info
->lpszSubjectInfo
= LocalAlloc(0, len
);
2126 if(info
->lpszSubjectInfo
)
2127 CertNameToStrA(context
->dwCertEncodingType
,
2128 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
|CERT_NAME_STR_CRLF_FLAG
,
2129 info
->lpszSubjectInfo
, len
);
2130 len
= CertNameToStrA(context
->dwCertEncodingType
,
2131 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
|CERT_NAME_STR_CRLF_FLAG
, NULL
, 0);
2132 info
->lpszIssuerInfo
= LocalAlloc(0, len
);
2133 if(info
->lpszIssuerInfo
)
2134 CertNameToStrA(context
->dwCertEncodingType
,
2135 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
|CERT_NAME_STR_CRLF_FLAG
,
2136 info
->lpszIssuerInfo
, len
);
2137 info
->dwKeySize
= NETCON_GetCipherStrength(req
->netconn
);
2138 CertFreeCertificateContext(context
);
2139 return ERROR_SUCCESS
;
2141 return ERROR_NOT_SUPPORTED
;
2143 case INTERNET_OPTION_CONNECT_TIMEOUT
:
2144 if (*size
< sizeof(DWORD
))
2145 return ERROR_INSUFFICIENT_BUFFER
;
2147 *size
= sizeof(DWORD
);
2148 *(DWORD
*)buffer
= req
->connect_timeout
;
2149 return ERROR_SUCCESS
;
2150 case INTERNET_OPTION_REQUEST_FLAGS
: {
2153 if (*size
< sizeof(DWORD
))
2154 return ERROR_INSUFFICIENT_BUFFER
;
2156 /* FIXME: Add support for:
2157 * INTERNET_REQFLAG_FROM_CACHE
2158 * INTERNET_REQFLAG_CACHE_WRITE_DISABLED
2162 flags
|= INTERNET_REQFLAG_VIA_PROXY
;
2163 if(!req
->rawHeaders
)
2164 flags
|= INTERNET_REQFLAG_NO_HEADERS
;
2166 TRACE("INTERNET_OPTION_REQUEST_FLAGS returning %x\n", flags
);
2168 *size
= sizeof(DWORD
);
2169 *(DWORD
*)buffer
= flags
;
2170 return ERROR_SUCCESS
;
2174 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
2177 static DWORD
HTTPREQ_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
2179 http_request_t
*req
= (http_request_t
*)hdr
;
2182 case 99: /* Undocumented, seems to be INTERNET_OPTION_SECURITY_FLAGS with argument validation */
2183 TRACE("Undocumented option 99\n");
2185 if (!buffer
|| size
!= sizeof(DWORD
))
2186 return ERROR_INVALID_PARAMETER
;
2187 if(*(DWORD
*)buffer
& ~SECURITY_SET_MASK
)
2188 return ERROR_INTERNET_OPTION_NOT_SETTABLE
;
2191 case INTERNET_OPTION_SECURITY_FLAGS
:
2195 if (!buffer
|| size
!= sizeof(DWORD
))
2196 return ERROR_INVALID_PARAMETER
;
2197 flags
= *(DWORD
*)buffer
;
2198 TRACE("INTERNET_OPTION_SECURITY_FLAGS %08x\n", flags
);
2199 flags
&= SECURITY_SET_MASK
;
2200 req
->security_flags
|= flags
;
2202 req
->netconn
->security_flags
|= flags
;
2203 return ERROR_SUCCESS
;
2205 case INTERNET_OPTION_CONNECT_TIMEOUT
:
2206 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
2207 req
->connect_timeout
= *(DWORD
*)buffer
;
2208 return ERROR_SUCCESS
;
2210 case INTERNET_OPTION_SEND_TIMEOUT
:
2211 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
2212 req
->send_timeout
= *(DWORD
*)buffer
;
2213 return ERROR_SUCCESS
;
2215 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
2216 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
2217 req
->receive_timeout
= *(DWORD
*)buffer
;
2218 return ERROR_SUCCESS
;
2220 case INTERNET_OPTION_USERNAME
:
2221 heap_free(req
->session
->userName
);
2222 if (!(req
->session
->userName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
2223 return ERROR_SUCCESS
;
2225 case INTERNET_OPTION_PASSWORD
:
2226 heap_free(req
->session
->password
);
2227 if (!(req
->session
->password
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
2228 return ERROR_SUCCESS
;
2229 case INTERNET_OPTION_HTTP_DECODING
:
2230 if(size
!= sizeof(BOOL
))
2231 return ERROR_INVALID_PARAMETER
;
2232 req
->decoding
= *(BOOL
*)buffer
;
2233 return ERROR_SUCCESS
;
2236 return INET_SetOption(hdr
, option
, buffer
, size
);
2239 static void commit_cache_entry(http_request_t
*req
)
2241 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
2245 CloseHandle(req
->hCacheFile
);
2246 req
->hCacheFile
= NULL
;
2248 if(HTTP_GetRequestURL(req
, url
)) {
2251 headersLen
= req
->rawHeaders
? strlenW(req
->rawHeaders
) : 0;
2252 CommitUrlCacheEntryW(url
, req
->cacheFile
, req
->expires
,
2253 req
->last_modified
, NORMAL_CACHE_ENTRY
,
2254 req
->rawHeaders
, headersLen
, NULL
, 0);
2258 static void create_cache_entry(http_request_t
*req
)
2260 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
2261 WCHAR file_name
[MAX_PATH
+1];
2264 /* FIXME: We should free previous cache file earlier */
2265 heap_free(req
->cacheFile
);
2266 CloseHandle(req
->hCacheFile
);
2267 req
->hCacheFile
= NULL
;
2269 b
= HTTP_GetRequestURL(req
, url
);
2271 WARN("Could not get URL\n");
2275 b
= CreateUrlCacheEntryW(url
, req
->contentLength
, NULL
, file_name
, 0);
2277 WARN("Could not create cache entry: %08x\n", GetLastError());
2281 req
->cacheFile
= heap_strdupW(file_name
);
2282 req
->hCacheFile
= CreateFileW(req
->cacheFile
, GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
2283 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
2284 if(req
->hCacheFile
== INVALID_HANDLE_VALUE
) {
2285 WARN("Could not create file: %u\n", GetLastError());
2286 req
->hCacheFile
= NULL
;
2290 if(req
->read_size
) {
2293 b
= WriteFile(req
->hCacheFile
, req
->read_buf
+req
->read_pos
, req
->read_size
, &written
, NULL
);
2295 FIXME("WriteFile failed: %u\n", GetLastError());
2297 if(req
->data_stream
->vtbl
->end_of_data(req
->data_stream
, req
))
2298 commit_cache_entry(req
);
2302 /* read some more data into the read buffer (the read section must be held) */
2303 static DWORD
read_more_data( http_request_t
*req
, int maxlen
)
2310 /* move existing data to the start of the buffer */
2312 memmove( req
->read_buf
, req
->read_buf
+ req
->read_pos
, req
->read_size
);
2316 if (maxlen
== -1) maxlen
= sizeof(req
->read_buf
);
2318 res
= NETCON_recv( req
->netconn
, req
->read_buf
+ req
->read_size
,
2319 maxlen
- req
->read_size
, 0, &len
);
2320 if(res
== ERROR_SUCCESS
)
2321 req
->read_size
+= len
;
2326 /* remove some amount of data from the read buffer (the read section must be held) */
2327 static void remove_data( http_request_t
*req
, int count
)
2329 if (!(req
->read_size
-= count
)) req
->read_pos
= 0;
2330 else req
->read_pos
+= count
;
2333 static BOOL
read_line( http_request_t
*req
, LPSTR buffer
, DWORD
*len
)
2335 int count
, bytes_read
, pos
= 0;
2338 EnterCriticalSection( &req
->read_section
);
2341 BYTE
*eol
= memchr( req
->read_buf
+ req
->read_pos
, '\n', req
->read_size
);
2345 count
= eol
- (req
->read_buf
+ req
->read_pos
);
2346 bytes_read
= count
+ 1;
2348 else count
= bytes_read
= req
->read_size
;
2350 count
= min( count
, *len
- pos
);
2351 memcpy( buffer
+ pos
, req
->read_buf
+ req
->read_pos
, count
);
2353 remove_data( req
, bytes_read
);
2356 if ((res
= read_more_data( req
, -1 )) != ERROR_SUCCESS
|| !req
->read_size
)
2359 TRACE( "returning empty string %u\n", res
);
2360 LeaveCriticalSection( &req
->read_section
);
2361 INTERNET_SetLastError(res
);
2365 LeaveCriticalSection( &req
->read_section
);
2369 if (pos
&& buffer
[pos
- 1] == '\r') pos
--;
2372 buffer
[*len
- 1] = 0;
2373 TRACE( "returning %s\n", debugstr_a(buffer
));
2377 /* check if we have reached the end of the data to read (the read section must be held) */
2378 static BOOL
end_of_read_data( http_request_t
*req
)
2380 return !req
->read_size
&& req
->data_stream
->vtbl
->end_of_data(req
->data_stream
, req
);
2383 static DWORD
read_http_stream(http_request_t
*req
, BYTE
*buf
, DWORD size
, DWORD
*read
, read_mode_t read_mode
)
2387 res
= req
->data_stream
->vtbl
->read(req
->data_stream
, req
, buf
, size
, read
, read_mode
);
2388 assert(*read
<= size
);
2390 if(req
->hCacheFile
) {
2395 bres
= WriteFile(req
->hCacheFile
, buf
, *read
, &written
, NULL
);
2397 FIXME("WriteFile failed: %u\n", GetLastError());
2400 if(req
->data_stream
->vtbl
->end_of_data(req
->data_stream
, req
))
2401 commit_cache_entry(req
);
2407 /* fetch some more data into the read buffer (the read section must be held) */
2408 static DWORD
refill_read_buffer(http_request_t
*req
, read_mode_t read_mode
, DWORD
*read_bytes
)
2412 if(req
->read_size
== sizeof(req
->read_buf
))
2413 return ERROR_SUCCESS
;
2417 memmove(req
->read_buf
, req
->read_buf
+req
->read_pos
, req
->read_size
);
2421 res
= read_http_stream(req
, req
->read_buf
+req
->read_size
, sizeof(req
->read_buf
) - req
->read_size
,
2423 req
->read_size
+= read
;
2425 TRACE("read %u bytes, read_size %u\n", read
, req
->read_size
);
2431 /* return the size of data available to be read immediately (the read section must be held) */
2432 static DWORD
get_avail_data( http_request_t
*req
)
2434 return req
->read_size
+ req
->data_stream
->vtbl
->get_avail_data(req
->data_stream
, req
);
2437 static DWORD
netconn_get_avail_data(data_stream_t
*stream
, http_request_t
*req
)
2439 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2443 NETCON_query_data_available(req
->netconn
, &avail
);
2444 return netconn_stream
->content_length
== ~0u
2446 : min(avail
, netconn_stream
->content_length
-netconn_stream
->content_read
);
2449 static BOOL
netconn_end_of_data(data_stream_t
*stream
, http_request_t
*req
)
2451 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2452 return netconn_stream
->content_read
== netconn_stream
->content_length
|| !req
->netconn
;
2455 static DWORD
netconn_read(data_stream_t
*stream
, http_request_t
*req
, BYTE
*buf
, DWORD size
,
2456 DWORD
*read
, read_mode_t read_mode
)
2458 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2461 size
= min(size
, netconn_stream
->content_length
-netconn_stream
->content_read
);
2463 if(read_mode
== READMODE_NOBLOCK
) {
2464 DWORD avail
= netconn_get_avail_data(stream
, req
);
2469 if(size
&& req
->netconn
) {
2470 if(NETCON_recv(req
->netconn
, buf
, size
, read_mode
== READMODE_SYNC
? MSG_WAITALL
: 0, &len
) != ERROR_SUCCESS
)
2473 netconn_stream
->content_length
= netconn_stream
->content_read
;
2476 netconn_stream
->content_read
+= *read
= len
;
2477 TRACE("read %u bytes\n", len
);
2478 return ERROR_SUCCESS
;
2481 static BOOL
netconn_drain_content(data_stream_t
*stream
, http_request_t
*req
)
2483 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2488 if(netconn_end_of_data(stream
, req
))
2492 avail
= netconn_get_avail_data(stream
, req
);
2496 if(NETCON_recv(req
->netconn
, buf
, min(avail
, sizeof(buf
)), 0, &len
) != ERROR_SUCCESS
)
2499 netconn_stream
->content_read
+= len
;
2500 }while(netconn_stream
->content_read
< netconn_stream
->content_length
);
2505 static void netconn_destroy(data_stream_t
*stream
)
2509 static const data_stream_vtbl_t netconn_stream_vtbl
= {
2510 netconn_get_avail_data
,
2511 netconn_end_of_data
,
2513 netconn_drain_content
,
2517 /* read some more data into the read buffer (the read section must be held) */
2518 static DWORD
read_more_chunked_data(chunked_stream_t
*stream
, http_request_t
*req
, int maxlen
)
2523 if (stream
->buf_pos
)
2525 /* move existing data to the start of the buffer */
2526 if(stream
->buf_size
)
2527 memmove(stream
->buf
, stream
->buf
+ stream
->buf_pos
, stream
->buf_size
);
2528 stream
->buf_pos
= 0;
2531 if (maxlen
== -1) maxlen
= sizeof(stream
->buf
);
2533 res
= NETCON_recv( req
->netconn
, stream
->buf
+ stream
->buf_size
,
2534 maxlen
- stream
->buf_size
, 0, &len
);
2535 if(res
== ERROR_SUCCESS
)
2536 stream
->buf_size
+= len
;
2541 /* remove some amount of data from the read buffer (the read section must be held) */
2542 static void remove_chunked_data(chunked_stream_t
*stream
, int count
)
2544 if (!(stream
->buf_size
-= count
)) stream
->buf_pos
= 0;
2545 else stream
->buf_pos
+= count
;
2548 /* discard data contents until we reach end of line (the read section must be held) */
2549 static DWORD
discard_chunked_eol(chunked_stream_t
*stream
, http_request_t
*req
)
2555 BYTE
*eol
= memchr(stream
->buf
+ stream
->buf_pos
, '\n', stream
->buf_size
);
2558 remove_chunked_data(stream
, (eol
+ 1) - (stream
->buf
+ stream
->buf_pos
));
2561 stream
->buf_pos
= stream
->buf_size
= 0; /* discard everything */
2562 if ((res
= read_more_chunked_data(stream
, req
, -1)) != ERROR_SUCCESS
) return res
;
2563 } while (stream
->buf_size
);
2564 return ERROR_SUCCESS
;
2567 /* read the size of the next chunk (the read section must be held) */
2568 static DWORD
start_next_chunk(chunked_stream_t
*stream
, http_request_t
*req
)
2571 DWORD chunk_size
= 0, res
;
2573 if(stream
->chunk_size
!= ~0u && (res
= discard_chunked_eol(stream
, req
)) != ERROR_SUCCESS
)
2578 while (stream
->buf_size
)
2580 char ch
= stream
->buf
[stream
->buf_pos
];
2581 if (ch
>= '0' && ch
<= '9') chunk_size
= chunk_size
* 16 + ch
- '0';
2582 else if (ch
>= 'a' && ch
<= 'f') chunk_size
= chunk_size
* 16 + ch
- 'a' + 10;
2583 else if (ch
>= 'A' && ch
<= 'F') chunk_size
= chunk_size
* 16 + ch
- 'A' + 10;
2584 else if (ch
== ';' || ch
== '\r' || ch
== '\n')
2586 TRACE( "reading %u byte chunk\n", chunk_size
);
2587 stream
->chunk_size
= chunk_size
;
2588 req
->contentLength
+= chunk_size
;
2589 return discard_chunked_eol(stream
, req
);
2591 remove_chunked_data(stream
, 1);
2593 if ((res
= read_more_chunked_data(stream
, req
, -1)) != ERROR_SUCCESS
) return res
;
2594 if (!stream
->buf_size
)
2596 stream
->chunk_size
= 0;
2597 return ERROR_SUCCESS
;
2602 static DWORD
chunked_get_avail_data(data_stream_t
*stream
, http_request_t
*req
)
2604 /* Allow reading only from read buffer */
2608 static BOOL
chunked_end_of_data(data_stream_t
*stream
, http_request_t
*req
)
2610 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2611 return !chunked_stream
->chunk_size
;
2614 static DWORD
chunked_read(data_stream_t
*stream
, http_request_t
*req
, BYTE
*buf
, DWORD size
,
2615 DWORD
*read
, read_mode_t read_mode
)
2617 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2618 DWORD read_bytes
= 0, ret_read
= 0, res
= ERROR_SUCCESS
;
2620 if(chunked_stream
->chunk_size
== ~0u) {
2621 res
= start_next_chunk(chunked_stream
, req
);
2622 if(res
!= ERROR_SUCCESS
)
2626 while(size
&& chunked_stream
->chunk_size
) {
2627 if(chunked_stream
->buf_size
) {
2628 read_bytes
= min(size
, min(chunked_stream
->buf_size
, chunked_stream
->chunk_size
));
2630 /* this could block */
2631 if(read_mode
== READMODE_NOBLOCK
&& read_bytes
== chunked_stream
->chunk_size
)
2634 memcpy(buf
+ret_read
, chunked_stream
->buf
+chunked_stream
->buf_pos
, read_bytes
);
2635 remove_chunked_data(chunked_stream
, read_bytes
);
2637 read_bytes
= min(size
, chunked_stream
->chunk_size
);
2639 if(read_mode
== READMODE_NOBLOCK
) {
2642 if(!NETCON_query_data_available(req
->netconn
, &avail
) || !avail
)
2644 if(read_bytes
> avail
)
2647 /* this could block */
2648 if(read_bytes
== chunked_stream
->chunk_size
)
2652 res
= NETCON_recv(req
->netconn
, (char *)buf
+ret_read
, read_bytes
, 0, (int*)&read_bytes
);
2653 if(res
!= ERROR_SUCCESS
)
2657 chunked_stream
->chunk_size
-= read_bytes
;
2659 ret_read
+= read_bytes
;
2660 if(!chunked_stream
->chunk_size
) {
2661 assert(read_mode
!= READMODE_NOBLOCK
);
2662 res
= start_next_chunk(chunked_stream
, req
);
2663 if(res
!= ERROR_SUCCESS
)
2667 if(read_mode
== READMODE_ASYNC
)
2668 read_mode
= READMODE_NOBLOCK
;
2671 TRACE("read %u bytes\n", ret_read
);
2676 static BOOL
chunked_drain_content(data_stream_t
*stream
, http_request_t
*req
)
2678 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2680 /* FIXME: we can do better */
2681 return !chunked_stream
->chunk_size
;
2684 static void chunked_destroy(data_stream_t
*stream
)
2686 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2687 heap_free(chunked_stream
);
2690 static const data_stream_vtbl_t chunked_stream_vtbl
= {
2691 chunked_get_avail_data
,
2692 chunked_end_of_data
,
2694 chunked_drain_content
,
2698 /* set the request content length based on the headers */
2699 static DWORD
set_content_length(http_request_t
*request
)
2701 static const WCHAR szChunked
[] = {'c','h','u','n','k','e','d',0};
2705 if(request
->status_code
== HTTP_STATUS_NO_CONTENT
) {
2706 request
->contentLength
= request
->netconn_stream
.content_length
= 0;
2707 return ERROR_SUCCESS
;
2710 size
= sizeof(request
->contentLength
);
2711 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_CONTENT_LENGTH
,
2712 &request
->contentLength
, &size
, NULL
) != ERROR_SUCCESS
)
2713 request
->contentLength
= ~0u;
2714 request
->netconn_stream
.content_length
= request
->contentLength
;
2715 request
->netconn_stream
.content_read
= request
->read_size
;
2717 size
= sizeof(encoding
);
2718 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_TRANSFER_ENCODING
, encoding
, &size
, NULL
) == ERROR_SUCCESS
&&
2719 !strcmpiW(encoding
, szChunked
))
2721 chunked_stream_t
*chunked_stream
;
2723 chunked_stream
= heap_alloc(sizeof(*chunked_stream
));
2725 return ERROR_OUTOFMEMORY
;
2727 chunked_stream
->data_stream
.vtbl
= &chunked_stream_vtbl
;
2728 chunked_stream
->buf_size
= chunked_stream
->buf_pos
= 0;
2729 chunked_stream
->chunk_size
= ~0u;
2731 if(request
->read_size
) {
2732 memcpy(chunked_stream
->buf
, request
->read_buf
+request
->read_pos
, request
->read_size
);
2733 chunked_stream
->buf_size
= request
->read_size
;
2734 request
->read_size
= request
->read_pos
= 0;
2737 request
->data_stream
= &chunked_stream
->data_stream
;
2738 request
->contentLength
= ~0u;
2739 request
->read_chunked
= TRUE
;
2742 if(request
->decoding
) {
2745 static const WCHAR gzipW
[] = {'g','z','i','p',0};
2747 encoding_idx
= HTTP_GetCustomHeaderIndex(request
, szContent_Encoding
, 0, FALSE
);
2748 if(encoding_idx
!= -1 && !strcmpiW(request
->custHeaders
[encoding_idx
].lpszValue
, gzipW
))
2749 return init_gzip_stream(request
);
2752 return ERROR_SUCCESS
;
2755 static void send_request_complete(http_request_t
*req
, DWORD_PTR result
, DWORD error
)
2757 INTERNET_ASYNC_RESULT iar
;
2759 iar
.dwResult
= result
;
2760 iar
.dwError
= error
;
2762 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2763 sizeof(INTERNET_ASYNC_RESULT
));
2766 static void HTTP_ReceiveRequestData(http_request_t
*req
, BOOL first_notif
)
2768 DWORD res
, read
= 0, avail
= 0;
2773 EnterCriticalSection( &req
->read_section
);
2775 mode
= first_notif
&& req
->read_size
? READMODE_NOBLOCK
: READMODE_ASYNC
;
2776 res
= refill_read_buffer(req
, mode
, &read
);
2777 if(res
== ERROR_SUCCESS
&& !first_notif
)
2778 avail
= get_avail_data(req
);
2780 LeaveCriticalSection( &req
->read_section
);
2782 if(res
!= ERROR_SUCCESS
|| (mode
!= READMODE_NOBLOCK
&& !read
)) {
2783 WARN("res %u read %u, closing connection\n", res
, read
);
2784 http_release_netconn(req
, FALSE
);
2787 if(res
== ERROR_SUCCESS
)
2788 send_request_complete(req
, req
->session
->hdr
.dwInternalFlags
& INET_OPENURL
? (DWORD_PTR
)req
->hdr
.hInternet
: 1, avail
);
2790 send_request_complete(req
, 0, res
);
2793 /* read data from the http connection (the read section must be held) */
2794 static DWORD
HTTPREQ_Read(http_request_t
*req
, void *buffer
, DWORD size
, DWORD
*read
, BOOL sync
)
2796 DWORD current_read
= 0, ret_read
= 0;
2797 read_mode_t read_mode
;
2798 DWORD res
= ERROR_SUCCESS
;
2800 read_mode
= req
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
? READMODE_ASYNC
: READMODE_SYNC
;
2802 EnterCriticalSection( &req
->read_section
);
2804 if(req
->read_size
) {
2805 ret_read
= min(size
, req
->read_size
);
2806 memcpy(buffer
, req
->read_buf
+req
->read_pos
, ret_read
);
2807 req
->read_size
-= ret_read
;
2808 req
->read_pos
+= ret_read
;
2809 if(read_mode
== READMODE_ASYNC
)
2810 read_mode
= READMODE_NOBLOCK
;
2813 if(ret_read
< size
) {
2814 res
= read_http_stream(req
, (BYTE
*)buffer
+ret_read
, size
-ret_read
, ¤t_read
, read_mode
);
2815 ret_read
+= current_read
;
2818 LeaveCriticalSection( &req
->read_section
);
2821 TRACE( "retrieved %u bytes (%u)\n", ret_read
, req
->contentLength
);
2823 if(size
&& !ret_read
)
2824 http_release_netconn(req
, res
== ERROR_SUCCESS
);
2829 static BOOL
drain_content(http_request_t
*req
, BOOL blocking
)
2833 if(!req
->netconn
|| req
->contentLength
== -1)
2836 if(!strcmpW(req
->verb
, szHEAD
))
2840 return req
->data_stream
->vtbl
->drain_content(req
->data_stream
, req
);
2842 EnterCriticalSection( &req
->read_section
);
2845 DWORD bytes_read
, res
;
2848 res
= HTTPREQ_Read(req
, buf
, sizeof(buf
), &bytes_read
, TRUE
);
2849 if(res
!= ERROR_SUCCESS
) {
2859 LeaveCriticalSection( &req
->read_section
);
2863 static DWORD
HTTPREQ_ReadFile(object_header_t
*hdr
, void *buffer
, DWORD size
, DWORD
*read
)
2865 http_request_t
*req
= (http_request_t
*)hdr
;
2868 EnterCriticalSection( &req
->read_section
);
2869 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2870 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2872 res
= HTTPREQ_Read(req
, buffer
, size
, read
, TRUE
);
2873 if(res
== ERROR_SUCCESS
)
2875 LeaveCriticalSection( &req
->read_section
);
2885 } read_file_ex_task_t
;
2887 static void AsyncReadFileExProc(task_header_t
*hdr
)
2889 read_file_ex_task_t
*task
= (read_file_ex_task_t
*)hdr
;
2890 http_request_t
*req
= (http_request_t
*)task
->hdr
.hdr
;
2893 TRACE("INTERNETREADFILEEXW %p\n", task
->hdr
.hdr
);
2895 res
= HTTPREQ_Read(req
, task
->buf
, task
->size
, task
->ret_read
, TRUE
);
2896 send_request_complete(req
, res
== ERROR_SUCCESS
, res
);
2899 static DWORD
HTTPREQ_ReadFileEx(object_header_t
*hdr
, void *buf
, DWORD size
, DWORD
*ret_read
,
2900 DWORD flags
, DWORD_PTR context
)
2903 http_request_t
*req
= (http_request_t
*)hdr
;
2904 DWORD res
, read
, cread
, error
= ERROR_SUCCESS
;
2906 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2907 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2909 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2911 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2913 read_file_ex_task_t
*task
;
2915 if (TryEnterCriticalSection( &req
->read_section
))
2917 if (get_avail_data(req
))
2919 res
= HTTPREQ_Read(req
, buf
, size
, &read
, FALSE
);
2920 LeaveCriticalSection( &req
->read_section
);
2923 LeaveCriticalSection( &req
->read_section
);
2926 task
= alloc_async_task(&req
->hdr
, AsyncReadFileExProc
, sizeof(*task
));
2929 task
->ret_read
= ret_read
;
2931 INTERNET_AsyncCall(&task
->hdr
);
2933 return ERROR_IO_PENDING
;
2938 EnterCriticalSection( &req
->read_section
);
2939 if(hdr
->dwError
== ERROR_SUCCESS
)
2940 hdr
->dwError
= INTERNET_HANDLE_IN_USE
;
2941 else if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2942 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2945 res
= HTTPREQ_Read(req
, (char*)buf
+read
, size
-read
, &cread
, !(flags
& IRF_NO_WAIT
));
2946 if(res
!= ERROR_SUCCESS
)
2950 if(read
== size
|| end_of_read_data(req
))
2953 LeaveCriticalSection( &req
->read_section
);
2955 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2956 &cread
, sizeof(cread
));
2957 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2958 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2960 EnterCriticalSection( &req
->read_section
);
2963 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2964 hdr
->dwError
= ERROR_SUCCESS
;
2966 error
= hdr
->dwError
;
2968 LeaveCriticalSection( &req
->read_section
);
2972 if (res
== ERROR_SUCCESS
) {
2973 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2974 &read
, sizeof(read
));
2977 return res
==ERROR_SUCCESS
? error
: res
;
2980 static DWORD
HTTPREQ_WriteFile(object_header_t
*hdr
, const void *buffer
, DWORD size
, DWORD
*written
)
2983 http_request_t
*request
= (http_request_t
*)hdr
;
2985 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
2988 res
= NETCON_send(request
->netconn
, buffer
, size
, 0, (LPINT
)written
);
2989 if (res
== ERROR_SUCCESS
)
2990 request
->bytesWritten
+= *written
;
2992 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_SENT
, written
, sizeof(DWORD
));
2996 static void AsyncQueryDataAvailableProc(WORKREQUEST
*workRequest
)
2998 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
3000 HTTP_ReceiveRequestData(req
, FALSE
);
3003 static DWORD
HTTPREQ_QueryDataAvailable(object_header_t
*hdr
, DWORD
*available
, DWORD flags
, DWORD_PTR ctx
)
3005 http_request_t
*req
= (http_request_t
*)hdr
;
3007 TRACE("(%p %p %x %lx)\n", req
, available
, flags
, ctx
);
3009 if (req
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
3011 task_header_t
*task
;
3013 /* never wait, if we can't enter the section we queue an async request right away */
3014 if (TryEnterCriticalSection( &req
->read_section
))
3016 refill_read_buffer(req
, READMODE_NOBLOCK
, NULL
);
3017 if ((*available
= get_avail_data( req
))) goto done
;
3018 if (end_of_read_data( req
)) goto done
;
3019 LeaveCriticalSection( &req
->read_section
);
3022 task
= alloc_async_task(&req
->hdr
, AsyncQueryDataAvailableProc
, sizeof(*task
));
3023 INTERNET_AsyncCall(task
);
3024 return ERROR_IO_PENDING
;
3027 EnterCriticalSection( &req
->read_section
);
3029 if (!(*available
= get_avail_data( req
)) && !end_of_read_data( req
))
3031 refill_read_buffer( req
, READMODE_ASYNC
, NULL
);
3032 *available
= get_avail_data( req
);
3036 LeaveCriticalSection( &req
->read_section
);
3038 TRACE( "returning %u\n", *available
);
3039 return ERROR_SUCCESS
;
3042 static const object_vtbl_t HTTPREQVtbl
= {
3044 HTTPREQ_CloseConnection
,
3045 HTTPREQ_QueryOption
,
3050 HTTPREQ_QueryDataAvailable
,
3054 /***********************************************************************
3055 * HTTP_HttpOpenRequestW (internal)
3057 * Open a HTTP request handle
3060 * HINTERNET a HTTP request handle on success
3064 static DWORD
HTTP_HttpOpenRequestW(http_session_t
*session
,
3065 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
3066 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
3067 DWORD dwFlags
, DWORD_PTR dwContext
, HINTERNET
*ret
)
3069 appinfo_t
*hIC
= session
->appInfo
;
3070 http_request_t
*request
;
3071 DWORD len
, res
= ERROR_SUCCESS
;
3075 request
= alloc_object(&session
->hdr
, &HTTPREQVtbl
, sizeof(http_request_t
));
3077 return ERROR_OUTOFMEMORY
;
3079 request
->hdr
.htype
= WH_HHTTPREQ
;
3080 request
->hdr
.dwFlags
= dwFlags
;
3081 request
->hdr
.dwContext
= dwContext
;
3082 request
->contentLength
= ~0u;
3084 request
->netconn_stream
.data_stream
.vtbl
= &netconn_stream_vtbl
;
3085 request
->data_stream
= &request
->netconn_stream
.data_stream
;
3086 request
->connect_timeout
= session
->connect_timeout
;
3087 request
->send_timeout
= session
->send_timeout
;
3088 request
->receive_timeout
= session
->receive_timeout
;
3090 InitializeCriticalSection( &request
->read_section
);
3091 request
->read_section
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": http_request_t.read_section");
3093 WININET_AddRef( &session
->hdr
);
3094 request
->session
= session
;
3095 list_add_head( &session
->hdr
.children
, &request
->hdr
.entry
);
3097 request
->server
= get_server(session
->hostName
, session
->hostPort
, (dwFlags
& INTERNET_FLAG_SECURE
) != 0, TRUE
);
3098 if(!request
->server
) {
3099 WININET_Release(&request
->hdr
);
3100 return ERROR_OUTOFMEMORY
;
3103 if (dwFlags
& INTERNET_FLAG_IGNORE_CERT_CN_INVALID
)
3104 request
->security_flags
|= SECURITY_FLAG_IGNORE_CERT_CN_INVALID
;
3105 if (dwFlags
& INTERNET_FLAG_IGNORE_CERT_DATE_INVALID
)
3106 request
->security_flags
|= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
;
3108 if (lpszObjectName
&& *lpszObjectName
) {
3112 rc
= UrlEscapeW(lpszObjectName
, NULL
, &len
, URL_ESCAPE_SPACES_ONLY
);
3113 if (rc
!= E_POINTER
)
3114 len
= strlenW(lpszObjectName
)+1;
3115 request
->path
= heap_alloc(len
*sizeof(WCHAR
));
3116 rc
= UrlEscapeW(lpszObjectName
, request
->path
, &len
,
3117 URL_ESCAPE_SPACES_ONLY
);
3120 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName
),rc
);
3121 strcpyW(request
->path
,lpszObjectName
);
3124 static const WCHAR slashW
[] = {'/',0};
3126 request
->path
= heap_strdupW(slashW
);
3129 if (lpszReferrer
&& *lpszReferrer
)
3130 HTTP_ProcessHeader(request
, HTTP_REFERER
, lpszReferrer
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
3132 if (lpszAcceptTypes
)
3135 for (i
= 0; lpszAcceptTypes
[i
]; i
++)
3137 if (!*lpszAcceptTypes
[i
]) continue;
3138 HTTP_ProcessHeader(request
, HTTP_ACCEPT
, lpszAcceptTypes
[i
],
3139 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
|
3140 HTTP_ADDHDR_FLAG_REQ
|
3141 (i
== 0 ? HTTP_ADDHDR_FLAG_REPLACE
: 0));
3145 request
->verb
= heap_strdupW(lpszVerb
&& *lpszVerb
? lpszVerb
: szGET
);
3146 request
->version
= heap_strdupW(lpszVersion
&& *lpszVersion
? lpszVersion
: g_szHttp1_1
);
3148 HTTP_ProcessHeader(request
, hostW
, request
->server
->canon_host_port
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
3150 if (session
->hostPort
== INTERNET_INVALID_PORT_NUMBER
)
3151 session
->hostPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
3152 INTERNET_DEFAULT_HTTPS_PORT
:
3153 INTERNET_DEFAULT_HTTP_PORT
);
3155 if (hIC
->proxy
&& hIC
->proxy
[0])
3156 HTTP_DealWithProxy( hIC
, session
, request
);
3158 INTERNET_SendCallback(&session
->hdr
, dwContext
,
3159 INTERNET_STATUS_HANDLE_CREATED
, &request
->hdr
.hInternet
,
3162 TRACE("<-- %u (%p)\n", res
, request
);
3164 if(res
!= ERROR_SUCCESS
) {
3165 WININET_Release( &request
->hdr
);
3170 *ret
= request
->hdr
.hInternet
;
3171 return ERROR_SUCCESS
;
3174 /***********************************************************************
3175 * HttpOpenRequestW (WININET.@)
3177 * Open a HTTP request handle
3180 * HINTERNET a HTTP request handle on success
3184 HINTERNET WINAPI
HttpOpenRequestW(HINTERNET hHttpSession
,
3185 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
3186 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
3187 DWORD dwFlags
, DWORD_PTR dwContext
)
3189 http_session_t
*session
;
3190 HINTERNET handle
= NULL
;
3193 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
3194 debugstr_w(lpszVerb
), debugstr_w(lpszObjectName
),
3195 debugstr_w(lpszVersion
), debugstr_w(lpszReferrer
), lpszAcceptTypes
,
3196 dwFlags
, dwContext
);
3197 if(lpszAcceptTypes
!=NULL
)
3200 for(i
=0;lpszAcceptTypes
[i
]!=NULL
;i
++)
3201 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes
[i
]));
3204 session
= (http_session_t
*) get_handle_object( hHttpSession
);
3205 if (NULL
== session
|| session
->hdr
.htype
!= WH_HHTTPSESSION
)
3207 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
3212 * My tests seem to show that the windows version does not
3213 * become asynchronous until after this point. And anyhow
3214 * if this call was asynchronous then how would you get the
3215 * necessary HINTERNET pointer returned by this function.
3218 res
= HTTP_HttpOpenRequestW(session
, lpszVerb
, lpszObjectName
,
3219 lpszVersion
, lpszReferrer
, lpszAcceptTypes
,
3220 dwFlags
, dwContext
, &handle
);
3223 WININET_Release( &session
->hdr
);
3224 TRACE("returning %p\n", handle
);
3225 if(res
!= ERROR_SUCCESS
)
3230 static const LPCWSTR header_lookup
[] = {
3231 szMime_Version
, /* HTTP_QUERY_MIME_VERSION = 0 */
3232 szContent_Type
, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3233 szContent_Transfer_Encoding
,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3234 szContent_ID
, /* HTTP_QUERY_CONTENT_ID = 3 */
3235 NULL
, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3236 szContent_Length
, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3237 szContent_Language
, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3238 szAllow
, /* HTTP_QUERY_ALLOW = 7 */
3239 szPublic
, /* HTTP_QUERY_PUBLIC = 8 */
3240 szDate
, /* HTTP_QUERY_DATE = 9 */
3241 szExpires
, /* HTTP_QUERY_EXPIRES = 10 */
3242 szLast_Modified
, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3243 NULL
, /* HTTP_QUERY_MESSAGE_ID = 12 */
3244 szURI
, /* HTTP_QUERY_URI = 13 */
3245 szFrom
, /* HTTP_QUERY_DERIVED_FROM = 14 */
3246 NULL
, /* HTTP_QUERY_COST = 15 */
3247 NULL
, /* HTTP_QUERY_LINK = 16 */
3248 szPragma
, /* HTTP_QUERY_PRAGMA = 17 */
3249 NULL
, /* HTTP_QUERY_VERSION = 18 */
3250 szStatus
, /* HTTP_QUERY_STATUS_CODE = 19 */
3251 NULL
, /* HTTP_QUERY_STATUS_TEXT = 20 */
3252 NULL
, /* HTTP_QUERY_RAW_HEADERS = 21 */
3253 NULL
, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3254 szConnection
, /* HTTP_QUERY_CONNECTION = 23 */
3255 szAccept
, /* HTTP_QUERY_ACCEPT = 24 */
3256 szAccept_Charset
, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3257 szAccept_Encoding
, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3258 szAccept_Language
, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3259 szAuthorization
, /* HTTP_QUERY_AUTHORIZATION = 28 */
3260 szContent_Encoding
, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3261 NULL
, /* HTTP_QUERY_FORWARDED = 30 */
3262 NULL
, /* HTTP_QUERY_FROM = 31 */
3263 szIf_Modified_Since
, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3264 szLocation
, /* HTTP_QUERY_LOCATION = 33 */
3265 NULL
, /* HTTP_QUERY_ORIG_URI = 34 */
3266 szReferer
, /* HTTP_QUERY_REFERER = 35 */
3267 szRetry_After
, /* HTTP_QUERY_RETRY_AFTER = 36 */
3268 szServer
, /* HTTP_QUERY_SERVER = 37 */
3269 NULL
, /* HTTP_TITLE = 38 */
3270 szUser_Agent
, /* HTTP_QUERY_USER_AGENT = 39 */
3271 szWWW_Authenticate
, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3272 szProxy_Authenticate
, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3273 szAccept_Ranges
, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3274 szSet_Cookie
, /* HTTP_QUERY_SET_COOKIE = 43 */
3275 szCookie
, /* HTTP_QUERY_COOKIE = 44 */
3276 NULL
, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3277 NULL
, /* HTTP_QUERY_REFRESH = 46 */
3278 szContent_Disposition
, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3279 szAge
, /* HTTP_QUERY_AGE = 48 */
3280 szCache_Control
, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3281 szContent_Base
, /* HTTP_QUERY_CONTENT_BASE = 50 */
3282 szContent_Location
, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3283 szContent_MD5
, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3284 szContent_Range
, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3285 szETag
, /* HTTP_QUERY_ETAG = 54 */
3286 hostW
, /* HTTP_QUERY_HOST = 55 */
3287 szIf_Match
, /* HTTP_QUERY_IF_MATCH = 56 */
3288 szIf_None_Match
, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3289 szIf_Range
, /* HTTP_QUERY_IF_RANGE = 58 */
3290 szIf_Unmodified_Since
, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3291 szMax_Forwards
, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3292 szProxy_Authorization
, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3293 szRange
, /* HTTP_QUERY_RANGE = 62 */
3294 szTransfer_Encoding
, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3295 szUpgrade
, /* HTTP_QUERY_UPGRADE = 64 */
3296 szVary
, /* HTTP_QUERY_VARY = 65 */
3297 szVia
, /* HTTP_QUERY_VIA = 66 */
3298 szWarning
, /* HTTP_QUERY_WARNING = 67 */
3299 szExpect
, /* HTTP_QUERY_EXPECT = 68 */
3300 szProxy_Connection
, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3301 szUnless_Modified_Since
, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3304 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3306 /***********************************************************************
3307 * HTTP_HttpQueryInfoW (internal)
3309 static DWORD
HTTP_HttpQueryInfoW(http_request_t
*request
, DWORD dwInfoLevel
,
3310 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3312 LPHTTPHEADERW lphttpHdr
= NULL
;
3313 BOOL request_only
= dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
;
3314 INT requested_index
= lpdwIndex
? *lpdwIndex
: 0;
3315 DWORD level
= (dwInfoLevel
& ~HTTP_QUERY_MODIFIER_FLAGS_MASK
);
3318 /* Find requested header structure */
3321 case HTTP_QUERY_CUSTOM
:
3322 if (!lpBuffer
) return ERROR_INVALID_PARAMETER
;
3323 index
= HTTP_GetCustomHeaderIndex(request
, lpBuffer
, requested_index
, request_only
);
3325 case HTTP_QUERY_RAW_HEADERS_CRLF
:
3329 DWORD res
= ERROR_INVALID_PARAMETER
;
3332 headers
= HTTP_BuildHeaderRequestString(request
, request
->verb
, request
->path
, request
->version
);
3334 headers
= request
->rawHeaders
;
3337 len
= strlenW(headers
) * sizeof(WCHAR
);
3339 if (len
+ sizeof(WCHAR
) > *lpdwBufferLength
)
3341 len
+= sizeof(WCHAR
);
3342 res
= ERROR_INSUFFICIENT_BUFFER
;
3347 memcpy(lpBuffer
, headers
, len
+ sizeof(WCHAR
));
3350 len
= strlenW(szCrLf
) * sizeof(WCHAR
);
3351 memcpy(lpBuffer
, szCrLf
, sizeof(szCrLf
));
3353 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
/ sizeof(WCHAR
)));
3354 res
= ERROR_SUCCESS
;
3356 *lpdwBufferLength
= len
;
3358 if (request_only
) heap_free(headers
);
3361 case HTTP_QUERY_RAW_HEADERS
:
3363 LPWSTR
* ppszRawHeaderLines
= HTTP_Tokenize(request
->rawHeaders
, szCrLf
);
3365 LPWSTR pszString
= lpBuffer
;
3367 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
3368 size
+= strlenW(ppszRawHeaderLines
[i
]) + 1;
3370 if (size
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3372 HTTP_FreeTokens(ppszRawHeaderLines
);
3373 *lpdwBufferLength
= (size
+ 1) * sizeof(WCHAR
);
3374 return ERROR_INSUFFICIENT_BUFFER
;
3378 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
3380 DWORD len
= strlenW(ppszRawHeaderLines
[i
]);
3381 memcpy(pszString
, ppszRawHeaderLines
[i
], (len
+1)*sizeof(WCHAR
));
3385 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, size
));
3387 *lpdwBufferLength
= size
* sizeof(WCHAR
);
3388 HTTP_FreeTokens(ppszRawHeaderLines
);
3390 return ERROR_SUCCESS
;
3392 case HTTP_QUERY_STATUS_TEXT
:
3393 if (request
->statusText
)
3395 DWORD len
= strlenW(request
->statusText
);
3396 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3398 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
3399 return ERROR_INSUFFICIENT_BUFFER
;
3403 memcpy(lpBuffer
, request
->statusText
, (len
+ 1) * sizeof(WCHAR
));
3404 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
3406 *lpdwBufferLength
= len
* sizeof(WCHAR
);
3407 return ERROR_SUCCESS
;
3410 case HTTP_QUERY_VERSION
:
3411 if (request
->version
)
3413 DWORD len
= strlenW(request
->version
);
3414 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3416 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
3417 return ERROR_INSUFFICIENT_BUFFER
;
3421 memcpy(lpBuffer
, request
->version
, (len
+ 1) * sizeof(WCHAR
));
3422 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
3424 *lpdwBufferLength
= len
* sizeof(WCHAR
);
3425 return ERROR_SUCCESS
;
3428 case HTTP_QUERY_CONTENT_ENCODING
:
3429 index
= HTTP_GetCustomHeaderIndex(request
, header_lookup
[request
->read_gzip
? HTTP_QUERY_CONTENT_TYPE
: level
],
3430 requested_index
,request_only
);
3432 case HTTP_QUERY_STATUS_CODE
: {
3433 DWORD res
= ERROR_SUCCESS
;
3436 return ERROR_HTTP_INVALID_QUERY_REQUEST
;
3441 if(dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
) {
3442 if(*lpdwBufferLength
>= sizeof(DWORD
))
3443 *(DWORD
*)lpBuffer
= request
->status_code
;
3445 res
= ERROR_INSUFFICIENT_BUFFER
;
3446 *lpdwBufferLength
= sizeof(DWORD
);
3450 static const WCHAR formatW
[] = {'%','u',0};
3452 size
= sprintfW(buf
, formatW
, request
->status_code
) * sizeof(WCHAR
);
3454 if(size
<= *lpdwBufferLength
) {
3455 memcpy(lpBuffer
, buf
, size
+sizeof(WCHAR
));
3457 size
+= sizeof(WCHAR
);
3458 res
= ERROR_INSUFFICIENT_BUFFER
;
3461 *lpdwBufferLength
= size
;
3466 assert (LAST_TABLE_HEADER
== (HTTP_QUERY_UNLESS_MODIFIED_SINCE
+ 1));
3468 if (level
< LAST_TABLE_HEADER
&& header_lookup
[level
])
3469 index
= HTTP_GetCustomHeaderIndex(request
, header_lookup
[level
],
3470 requested_index
,request_only
);
3474 lphttpHdr
= &request
->custHeaders
[index
];
3476 /* Ensure header satisfies requested attributes */
3478 ((dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
) &&
3479 (~lphttpHdr
->wFlags
& HDR_ISREQUEST
)))
3481 return ERROR_HTTP_HEADER_NOT_FOUND
;
3484 if (lpdwIndex
) (*lpdwIndex
)++;
3486 /* coalesce value to requested type */
3487 if (dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
&& lpBuffer
)
3489 *(int *)lpBuffer
= atoiW(lphttpHdr
->lpszValue
);
3490 TRACE(" returning number: %d\n", *(int *)lpBuffer
);
3492 else if (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
&& lpBuffer
)
3498 tmpTime
= ConvertTimeString(lphttpHdr
->lpszValue
);
3500 tmpTM
= *gmtime(&tmpTime
);
3501 STHook
= (SYSTEMTIME
*)lpBuffer
;
3502 STHook
->wDay
= tmpTM
.tm_mday
;
3503 STHook
->wHour
= tmpTM
.tm_hour
;
3504 STHook
->wMilliseconds
= 0;
3505 STHook
->wMinute
= tmpTM
.tm_min
;
3506 STHook
->wDayOfWeek
= tmpTM
.tm_wday
;
3507 STHook
->wMonth
= tmpTM
.tm_mon
+ 1;
3508 STHook
->wSecond
= tmpTM
.tm_sec
;
3509 STHook
->wYear
= tmpTM
.tm_year
;
3511 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3512 STHook
->wYear
, STHook
->wMonth
, STHook
->wDay
, STHook
->wDayOfWeek
,
3513 STHook
->wHour
, STHook
->wMinute
, STHook
->wSecond
, STHook
->wMilliseconds
);
3515 else if (lphttpHdr
->lpszValue
)
3517 DWORD len
= (strlenW(lphttpHdr
->lpszValue
) + 1) * sizeof(WCHAR
);
3519 if (len
> *lpdwBufferLength
)
3521 *lpdwBufferLength
= len
;
3522 return ERROR_INSUFFICIENT_BUFFER
;
3526 memcpy(lpBuffer
, lphttpHdr
->lpszValue
, len
);
3527 TRACE("! returning string: %s\n", debugstr_w(lpBuffer
));
3529 *lpdwBufferLength
= len
- sizeof(WCHAR
);
3531 return ERROR_SUCCESS
;
3534 /***********************************************************************
3535 * HttpQueryInfoW (WININET.@)
3537 * Queries for information about an HTTP request
3544 BOOL WINAPI
HttpQueryInfoW(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3545 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3547 http_request_t
*request
;
3550 if (TRACE_ON(wininet
)) {
3551 #define FE(x) { x, #x }
3552 static const wininet_flag_info query_flags
[] = {
3553 FE(HTTP_QUERY_MIME_VERSION
),
3554 FE(HTTP_QUERY_CONTENT_TYPE
),
3555 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING
),
3556 FE(HTTP_QUERY_CONTENT_ID
),
3557 FE(HTTP_QUERY_CONTENT_DESCRIPTION
),
3558 FE(HTTP_QUERY_CONTENT_LENGTH
),
3559 FE(HTTP_QUERY_CONTENT_LANGUAGE
),
3560 FE(HTTP_QUERY_ALLOW
),
3561 FE(HTTP_QUERY_PUBLIC
),
3562 FE(HTTP_QUERY_DATE
),
3563 FE(HTTP_QUERY_EXPIRES
),
3564 FE(HTTP_QUERY_LAST_MODIFIED
),
3565 FE(HTTP_QUERY_MESSAGE_ID
),
3567 FE(HTTP_QUERY_DERIVED_FROM
),
3568 FE(HTTP_QUERY_COST
),
3569 FE(HTTP_QUERY_LINK
),
3570 FE(HTTP_QUERY_PRAGMA
),
3571 FE(HTTP_QUERY_VERSION
),
3572 FE(HTTP_QUERY_STATUS_CODE
),
3573 FE(HTTP_QUERY_STATUS_TEXT
),
3574 FE(HTTP_QUERY_RAW_HEADERS
),
3575 FE(HTTP_QUERY_RAW_HEADERS_CRLF
),
3576 FE(HTTP_QUERY_CONNECTION
),
3577 FE(HTTP_QUERY_ACCEPT
),
3578 FE(HTTP_QUERY_ACCEPT_CHARSET
),
3579 FE(HTTP_QUERY_ACCEPT_ENCODING
),
3580 FE(HTTP_QUERY_ACCEPT_LANGUAGE
),
3581 FE(HTTP_QUERY_AUTHORIZATION
),
3582 FE(HTTP_QUERY_CONTENT_ENCODING
),
3583 FE(HTTP_QUERY_FORWARDED
),
3584 FE(HTTP_QUERY_FROM
),
3585 FE(HTTP_QUERY_IF_MODIFIED_SINCE
),
3586 FE(HTTP_QUERY_LOCATION
),
3587 FE(HTTP_QUERY_ORIG_URI
),
3588 FE(HTTP_QUERY_REFERER
),
3589 FE(HTTP_QUERY_RETRY_AFTER
),
3590 FE(HTTP_QUERY_SERVER
),
3591 FE(HTTP_QUERY_TITLE
),
3592 FE(HTTP_QUERY_USER_AGENT
),
3593 FE(HTTP_QUERY_WWW_AUTHENTICATE
),
3594 FE(HTTP_QUERY_PROXY_AUTHENTICATE
),
3595 FE(HTTP_QUERY_ACCEPT_RANGES
),
3596 FE(HTTP_QUERY_SET_COOKIE
),
3597 FE(HTTP_QUERY_COOKIE
),
3598 FE(HTTP_QUERY_REQUEST_METHOD
),
3599 FE(HTTP_QUERY_REFRESH
),
3600 FE(HTTP_QUERY_CONTENT_DISPOSITION
),
3602 FE(HTTP_QUERY_CACHE_CONTROL
),
3603 FE(HTTP_QUERY_CONTENT_BASE
),
3604 FE(HTTP_QUERY_CONTENT_LOCATION
),
3605 FE(HTTP_QUERY_CONTENT_MD5
),
3606 FE(HTTP_QUERY_CONTENT_RANGE
),
3607 FE(HTTP_QUERY_ETAG
),
3608 FE(HTTP_QUERY_HOST
),
3609 FE(HTTP_QUERY_IF_MATCH
),
3610 FE(HTTP_QUERY_IF_NONE_MATCH
),
3611 FE(HTTP_QUERY_IF_RANGE
),
3612 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE
),
3613 FE(HTTP_QUERY_MAX_FORWARDS
),
3614 FE(HTTP_QUERY_PROXY_AUTHORIZATION
),
3615 FE(HTTP_QUERY_RANGE
),
3616 FE(HTTP_QUERY_TRANSFER_ENCODING
),
3617 FE(HTTP_QUERY_UPGRADE
),
3618 FE(HTTP_QUERY_VARY
),
3620 FE(HTTP_QUERY_WARNING
),
3621 FE(HTTP_QUERY_CUSTOM
)
3623 static const wininet_flag_info modifier_flags
[] = {
3624 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS
),
3625 FE(HTTP_QUERY_FLAG_SYSTEMTIME
),
3626 FE(HTTP_QUERY_FLAG_NUMBER
),
3627 FE(HTTP_QUERY_FLAG_COALESCE
)
3630 DWORD info_mod
= dwInfoLevel
& HTTP_QUERY_MODIFIER_FLAGS_MASK
;
3631 DWORD info
= dwInfoLevel
& HTTP_QUERY_HEADER_MASK
;
3634 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest
, dwInfoLevel
, info
);
3635 TRACE(" Attribute:");
3636 for (i
= 0; i
< (sizeof(query_flags
) / sizeof(query_flags
[0])); i
++) {
3637 if (query_flags
[i
].val
== info
) {
3638 TRACE(" %s", query_flags
[i
].name
);
3642 if (i
== (sizeof(query_flags
) / sizeof(query_flags
[0]))) {
3643 TRACE(" Unknown (%08x)", info
);
3646 TRACE(" Modifier:");
3647 for (i
= 0; i
< (sizeof(modifier_flags
) / sizeof(modifier_flags
[0])); i
++) {
3648 if (modifier_flags
[i
].val
& info_mod
) {
3649 TRACE(" %s", modifier_flags
[i
].name
);
3650 info_mod
&= ~ modifier_flags
[i
].val
;
3655 TRACE(" Unknown (%08x)", info_mod
);
3660 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
3661 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
3663 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
3667 if (lpBuffer
== NULL
)
3668 *lpdwBufferLength
= 0;
3669 res
= HTTP_HttpQueryInfoW( request
, dwInfoLevel
,
3670 lpBuffer
, lpdwBufferLength
, lpdwIndex
);
3674 WININET_Release( &request
->hdr
);
3676 TRACE("%u <--\n", res
);
3677 if(res
!= ERROR_SUCCESS
)
3679 return res
== ERROR_SUCCESS
;
3682 /***********************************************************************
3683 * HttpQueryInfoA (WININET.@)
3685 * Queries for information about an HTTP request
3692 BOOL WINAPI
HttpQueryInfoA(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3693 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3699 if((dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
) ||
3700 (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
))
3702 return HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, lpBuffer
,
3703 lpdwBufferLength
, lpdwIndex
);
3709 len
= (*lpdwBufferLength
)*sizeof(WCHAR
);
3710 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3712 alloclen
= MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, NULL
, 0 ) * sizeof(WCHAR
);
3718 bufferW
= heap_alloc(alloclen
);
3719 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3720 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3721 MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, bufferW
, alloclen
/ sizeof(WCHAR
) );
3728 result
= HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, bufferW
,
3732 len
= WideCharToMultiByte( CP_ACP
,0, bufferW
, len
/ sizeof(WCHAR
) + 1,
3733 lpBuffer
, *lpdwBufferLength
, NULL
, NULL
);
3734 *lpdwBufferLength
= len
- 1;
3736 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer
));
3739 /* since the strings being returned from HttpQueryInfoW should be
3740 * only ASCII characters, it is reasonable to assume that all of
3741 * the Unicode characters can be reduced to a single byte */
3742 *lpdwBufferLength
= len
/ sizeof(WCHAR
);
3744 heap_free( bufferW
);
3748 /***********************************************************************
3749 * HTTP_GetRedirectURL (internal)
3751 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*request
, LPCWSTR lpszUrl
)
3753 static WCHAR szHttp
[] = {'h','t','t','p',0};
3754 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3755 http_session_t
*session
= request
->session
;
3756 URL_COMPONENTSW urlComponents
;
3757 DWORD url_length
= 0;
3759 LPWSTR combined_url
;
3761 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3762 urlComponents
.lpszScheme
= (request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) ? szHttps
: szHttp
;
3763 urlComponents
.dwSchemeLength
= 0;
3764 urlComponents
.lpszHostName
= session
->hostName
;
3765 urlComponents
.dwHostNameLength
= 0;
3766 urlComponents
.nPort
= session
->hostPort
;
3767 urlComponents
.lpszUserName
= session
->userName
;
3768 urlComponents
.dwUserNameLength
= 0;
3769 urlComponents
.lpszPassword
= NULL
;
3770 urlComponents
.dwPasswordLength
= 0;
3771 urlComponents
.lpszUrlPath
= request
->path
;
3772 urlComponents
.dwUrlPathLength
= 0;
3773 urlComponents
.lpszExtraInfo
= NULL
;
3774 urlComponents
.dwExtraInfoLength
= 0;
3776 if (!InternetCreateUrlW(&urlComponents
, 0, NULL
, &url_length
) &&
3777 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3780 orig_url
= heap_alloc(url_length
);
3782 /* convert from bytes to characters */
3783 url_length
= url_length
/ sizeof(WCHAR
) - 1;
3784 if (!InternetCreateUrlW(&urlComponents
, 0, orig_url
, &url_length
))
3786 heap_free(orig_url
);
3791 if (!InternetCombineUrlW(orig_url
, lpszUrl
, NULL
, &url_length
, ICU_ENCODE_SPACES_ONLY
) &&
3792 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3794 heap_free(orig_url
);
3797 combined_url
= heap_alloc(url_length
* sizeof(WCHAR
));
3799 if (!InternetCombineUrlW(orig_url
, lpszUrl
, combined_url
, &url_length
, ICU_ENCODE_SPACES_ONLY
))
3801 heap_free(orig_url
);
3802 heap_free(combined_url
);
3805 heap_free(orig_url
);
3806 return combined_url
;
3810 /***********************************************************************
3811 * HTTP_HandleRedirect (internal)
3813 static DWORD
HTTP_HandleRedirect(http_request_t
*request
, LPCWSTR lpszUrl
)
3815 http_session_t
*session
= request
->session
;
3816 WCHAR path
[INTERNET_MAX_PATH_LENGTH
];
3821 /* if it's an absolute path, keep the same session info */
3822 lstrcpynW(path
, lpszUrl
, INTERNET_MAX_URL_LENGTH
);
3826 URL_COMPONENTSW urlComponents
;
3827 WCHAR protocol
[INTERNET_MAX_SCHEME_LENGTH
];
3828 WCHAR hostName
[INTERNET_MAX_HOST_NAME_LENGTH
];
3829 WCHAR userName
[INTERNET_MAX_USER_NAME_LENGTH
];
3830 BOOL custom_port
= FALSE
;
3832 static WCHAR httpW
[] = {'h','t','t','p',0};
3833 static WCHAR httpsW
[] = {'h','t','t','p','s',0};
3839 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3840 urlComponents
.lpszScheme
= protocol
;
3841 urlComponents
.dwSchemeLength
= INTERNET_MAX_SCHEME_LENGTH
;
3842 urlComponents
.lpszHostName
= hostName
;
3843 urlComponents
.dwHostNameLength
= INTERNET_MAX_HOST_NAME_LENGTH
;
3844 urlComponents
.lpszUserName
= userName
;
3845 urlComponents
.dwUserNameLength
= INTERNET_MAX_USER_NAME_LENGTH
;
3846 urlComponents
.lpszPassword
= NULL
;
3847 urlComponents
.dwPasswordLength
= 0;
3848 urlComponents
.lpszUrlPath
= path
;
3849 urlComponents
.dwUrlPathLength
= INTERNET_MAX_PATH_LENGTH
;
3850 urlComponents
.lpszExtraInfo
= NULL
;
3851 urlComponents
.dwExtraInfoLength
= 0;
3852 if(!InternetCrackUrlW(lpszUrl
, strlenW(lpszUrl
), 0, &urlComponents
))
3853 return INTERNET_GetLastError();
3855 if(!strcmpiW(protocol
, httpW
)) {
3856 if(request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) {
3857 TRACE("redirect from secure page to non-secure page\n");
3858 /* FIXME: warn about from secure redirect to non-secure page */
3859 request
->hdr
.dwFlags
&= ~INTERNET_FLAG_SECURE
;
3862 if(urlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
3863 urlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
3864 else if(urlComponents
.nPort
!= INTERNET_DEFAULT_HTTP_PORT
)
3866 }else if(!strcmpiW(protocol
, httpsW
)) {
3867 if(!(request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)) {
3868 TRACE("redirect from non-secure page to secure page\n");
3869 /* FIXME: notify about redirect to secure page */
3870 request
->hdr
.dwFlags
|= INTERNET_FLAG_SECURE
;
3873 if(urlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
3874 urlComponents
.nPort
= INTERNET_DEFAULT_HTTPS_PORT
;
3875 else if(urlComponents
.nPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
3879 heap_free(session
->hostName
);
3883 static const WCHAR fmt
[] = {'%','s',':','%','u',0};
3884 len
= lstrlenW(hostName
);
3885 len
+= 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3886 session
->hostName
= heap_alloc(len
*sizeof(WCHAR
));
3887 sprintfW(session
->hostName
, fmt
, hostName
, urlComponents
.nPort
);
3890 session
->hostName
= heap_strdupW(hostName
);
3892 HTTP_ProcessHeader(request
, hostW
, session
->hostName
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDREQ_FLAG_REPLACE
| HTTP_ADDHDR_FLAG_REQ
);
3894 heap_free(session
->userName
);
3895 session
->userName
= NULL
;
3897 session
->userName
= heap_strdupW(userName
);
3899 reset_data_stream(request
);
3901 if(strcmpiW(request
->server
->name
, hostName
) || request
->server
->port
!= urlComponents
.nPort
) {
3902 server_t
*new_server
;
3904 new_server
= get_server(hostName
, urlComponents
.nPort
, urlComponents
.nScheme
== INTERNET_SCHEME_HTTPS
, TRUE
);
3905 server_release(request
->server
);
3906 request
->server
= new_server
;
3909 heap_free(request
->path
);
3916 rc
= UrlEscapeW(path
, NULL
, &needed
, URL_ESCAPE_SPACES_ONLY
);
3917 if (rc
!= E_POINTER
)
3918 needed
= strlenW(path
)+1;
3919 request
->path
= heap_alloc(needed
*sizeof(WCHAR
));
3920 rc
= UrlEscapeW(path
, request
->path
, &needed
,
3921 URL_ESCAPE_SPACES_ONLY
);
3924 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path
),rc
);
3925 strcpyW(request
->path
,path
);
3929 /* Remove custom content-type/length headers on redirects. */
3930 index
= HTTP_GetCustomHeaderIndex(request
, szContent_Type
, 0, TRUE
);
3932 HTTP_DeleteCustomHeader(request
, index
);
3933 index
= HTTP_GetCustomHeaderIndex(request
, szContent_Length
, 0, TRUE
);
3935 HTTP_DeleteCustomHeader(request
, index
);
3937 return ERROR_SUCCESS
;
3940 /***********************************************************************
3941 * HTTP_build_req (internal)
3943 * concatenate all the strings in the request together
3945 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
)
3950 for( t
= list
; *t
; t
++ )
3951 len
+= strlenW( *t
);
3954 str
= heap_alloc(len
*sizeof(WCHAR
));
3957 for( t
= list
; *t
; t
++ )
3963 static DWORD
HTTP_SecureProxyConnect(http_request_t
*request
)
3965 server_t
*server
= request
->server
;
3966 LPWSTR requestString
;
3973 static const WCHAR connectW
[] = {'C','O','N','N','E','C','T',0};
3977 requestString
= HTTP_BuildHeaderRequestString( request
, connectW
, server
->host_port
, g_szHttp1_1
);
3979 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
3980 NULL
, 0, NULL
, NULL
);
3981 len
--; /* the nul terminator isn't needed */
3982 ascii_req
= heap_alloc(len
);
3983 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1, ascii_req
, len
, NULL
, NULL
);
3984 heap_free( requestString
);
3986 TRACE("full request -> %s\n", debugstr_an( ascii_req
, len
) );
3988 NETCON_set_timeout( request
->netconn
, TRUE
, request
->send_timeout
);
3989 res
= NETCON_send( request
->netconn
, ascii_req
, len
, 0, &cnt
);
3990 heap_free( ascii_req
);
3991 if (res
!= ERROR_SUCCESS
)
3994 responseLen
= HTTP_GetResponseHeaders( request
, TRUE
);
3996 return ERROR_HTTP_INVALID_HEADER
;
3998 return ERROR_SUCCESS
;
4001 static void HTTP_InsertCookies(http_request_t
*request
)
4003 DWORD cookie_size
, size
, cnt
= 0;
4007 static const WCHAR cookieW
[] = {'C','o','o','k','i','e',':',' ',0};
4009 host
= HTTP_GetHeader(request
, hostW
);
4013 if(get_cookie(host
->lpszValue
, request
->path
, NULL
, &cookie_size
) != ERROR_SUCCESS
)
4016 size
= sizeof(cookieW
) + cookie_size
* sizeof(WCHAR
) + sizeof(szCrLf
);
4017 if(!(cookies
= heap_alloc(size
)))
4020 cnt
+= sprintfW(cookies
, cookieW
);
4021 get_cookie(host
->lpszValue
, request
->path
, cookies
+cnt
, &cookie_size
);
4022 strcatW(cookies
, szCrLf
);
4024 HTTP_HttpAddRequestHeadersW(request
, cookies
, strlenW(cookies
), HTTP_ADDREQ_FLAG_REPLACE
);
4029 static WORD
HTTP_ParseWkday(LPCWSTR day
)
4031 static const WCHAR days
[7][4] = {{ 's','u','n',0 },
4039 for (i
= 0; i
< sizeof(days
)/sizeof(*days
); i
++)
4040 if (!strcmpiW(day
, days
[i
]))
4047 static WORD
HTTP_ParseMonth(LPCWSTR month
)
4049 static const WCHAR jan
[] = { 'j','a','n',0 };
4050 static const WCHAR feb
[] = { 'f','e','b',0 };
4051 static const WCHAR mar
[] = { 'm','a','r',0 };
4052 static const WCHAR apr
[] = { 'a','p','r',0 };
4053 static const WCHAR may
[] = { 'm','a','y',0 };
4054 static const WCHAR jun
[] = { 'j','u','n',0 };
4055 static const WCHAR jul
[] = { 'j','u','l',0 };
4056 static const WCHAR aug
[] = { 'a','u','g',0 };
4057 static const WCHAR sep
[] = { 's','e','p',0 };
4058 static const WCHAR oct
[] = { 'o','c','t',0 };
4059 static const WCHAR nov
[] = { 'n','o','v',0 };
4060 static const WCHAR dec
[] = { 'd','e','c',0 };
4062 if (!strcmpiW(month
, jan
)) return 1;
4063 if (!strcmpiW(month
, feb
)) return 2;
4064 if (!strcmpiW(month
, mar
)) return 3;
4065 if (!strcmpiW(month
, apr
)) return 4;
4066 if (!strcmpiW(month
, may
)) return 5;
4067 if (!strcmpiW(month
, jun
)) return 6;
4068 if (!strcmpiW(month
, jul
)) return 7;
4069 if (!strcmpiW(month
, aug
)) return 8;
4070 if (!strcmpiW(month
, sep
)) return 9;
4071 if (!strcmpiW(month
, oct
)) return 10;
4072 if (!strcmpiW(month
, nov
)) return 11;
4073 if (!strcmpiW(month
, dec
)) return 12;
4078 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4079 * optionally preceded by whitespace.
4080 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4081 * st, and sets *str to the first character after the time format.
4083 static BOOL
HTTP_ParseTime(SYSTEMTIME
*st
, LPCWSTR
*str
)
4089 while (isspaceW(*ptr
))
4092 num
= strtoulW(ptr
, &nextPtr
, 10);
4093 if (!nextPtr
|| nextPtr
<= ptr
|| *nextPtr
!= ':')
4095 ERR("unexpected time format %s\n", debugstr_w(ptr
));
4100 ERR("unexpected hour in time format %s\n", debugstr_w(ptr
));
4104 st
->wHour
= (WORD
)num
;
4105 num
= strtoulW(ptr
, &nextPtr
, 10);
4106 if (!nextPtr
|| nextPtr
<= ptr
|| *nextPtr
!= ':')
4108 ERR("unexpected time format %s\n", debugstr_w(ptr
));
4113 ERR("unexpected minute in time format %s\n", debugstr_w(ptr
));
4117 st
->wMinute
= (WORD
)num
;
4118 num
= strtoulW(ptr
, &nextPtr
, 10);
4119 if (!nextPtr
|| nextPtr
<= ptr
)
4121 ERR("unexpected time format %s\n", debugstr_w(ptr
));
4126 ERR("unexpected second in time format %s\n", debugstr_w(ptr
));
4131 st
->wSecond
= (WORD
)num
;
4135 static BOOL
HTTP_ParseDateAsAsctime(LPCWSTR value
, FILETIME
*ft
)
4137 static const WCHAR gmt
[]= { 'G','M','T',0 };
4138 WCHAR day
[4], *dayPtr
, month
[4], *monthPtr
, *nextPtr
;
4140 SYSTEMTIME st
= { 0 };
4143 for (ptr
= value
, dayPtr
= day
; *ptr
&& !isspaceW(*ptr
) &&
4144 dayPtr
- day
< sizeof(day
) / sizeof(day
[0]) - 1; ptr
++, dayPtr
++)
4147 st
.wDayOfWeek
= HTTP_ParseWkday(day
);
4148 if (st
.wDayOfWeek
>= 7)
4150 ERR("unexpected weekday %s\n", debugstr_w(day
));
4154 while (isspaceW(*ptr
))
4157 for (monthPtr
= month
; !isspace(*ptr
) &&
4158 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
4162 st
.wMonth
= HTTP_ParseMonth(month
);
4163 if (!st
.wMonth
|| st
.wMonth
> 12)
4165 ERR("unexpected month %s\n", debugstr_w(month
));
4169 while (isspaceW(*ptr
))
4172 num
= strtoulW(ptr
, &nextPtr
, 10);
4173 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
4175 ERR("unexpected day %s\n", debugstr_w(ptr
));
4179 st
.wDay
= (WORD
)num
;
4181 while (isspaceW(*ptr
))
4184 if (!HTTP_ParseTime(&st
, &ptr
))
4187 while (isspaceW(*ptr
))
4190 num
= strtoulW(ptr
, &nextPtr
, 10);
4191 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
4193 ERR("unexpected year %s\n", debugstr_w(ptr
));
4197 st
.wYear
= (WORD
)num
;
4199 while (isspaceW(*ptr
))
4202 /* asctime() doesn't report a timezone, but some web servers do, so accept
4203 * with or without GMT.
4205 if (*ptr
&& strcmpW(ptr
, gmt
))
4207 ERR("unexpected timezone %s\n", debugstr_w(ptr
));
4210 return SystemTimeToFileTime(&st
, ft
);
4213 static BOOL
HTTP_ParseRfc1123Date(LPCWSTR value
, FILETIME
*ft
)
4215 static const WCHAR gmt
[]= { 'G','M','T',0 };
4216 WCHAR
*nextPtr
, day
[4], month
[4], *monthPtr
;
4219 SYSTEMTIME st
= { 0 };
4221 ptr
= strchrW(value
, ',');
4224 if (ptr
- value
!= 3)
4226 WARN("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4229 memcpy(day
, value
, (ptr
- value
) * sizeof(WCHAR
));
4231 st
.wDayOfWeek
= HTTP_ParseWkday(day
);
4232 if (st
.wDayOfWeek
> 6)
4234 WARN("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4239 while (isspaceW(*ptr
))
4242 num
= strtoulW(ptr
, &nextPtr
, 10);
4243 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
4245 WARN("unexpected day %s\n", debugstr_w(value
));
4249 st
.wDay
= (WORD
)num
;
4251 while (isspaceW(*ptr
))
4254 for (monthPtr
= month
; !isspace(*ptr
) &&
4255 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
4259 st
.wMonth
= HTTP_ParseMonth(month
);
4260 if (!st
.wMonth
|| st
.wMonth
> 12)
4262 WARN("unexpected month %s\n", debugstr_w(month
));
4266 while (isspaceW(*ptr
))
4269 num
= strtoulW(ptr
, &nextPtr
, 10);
4270 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
4272 ERR("unexpected year %s\n", debugstr_w(value
));
4276 st
.wYear
= (WORD
)num
;
4278 if (!HTTP_ParseTime(&st
, &ptr
))
4281 while (isspaceW(*ptr
))
4284 if (strcmpW(ptr
, gmt
))
4286 ERR("unexpected time zone %s\n", debugstr_w(ptr
));
4289 return SystemTimeToFileTime(&st
, ft
);
4292 static WORD
HTTP_ParseWeekday(LPCWSTR day
)
4294 static const WCHAR days
[7][10] = {{ 's','u','n','d','a','y',0 },
4295 { 'm','o','n','d','a','y',0 },
4296 { 't','u','e','s','d','a','y',0 },
4297 { 'w','e','d','n','e','s','d','a','y',0 },
4298 { 't','h','u','r','s','d','a','y',0 },
4299 { 'f','r','i','d','a','y',0 },
4300 { 's','a','t','u','r','d','a','y',0 }};
4302 for (i
= 0; i
< sizeof(days
)/sizeof(*days
); i
++)
4303 if (!strcmpiW(day
, days
[i
]))
4310 static BOOL
HTTP_ParseRfc850Date(LPCWSTR value
, FILETIME
*ft
)
4312 static const WCHAR gmt
[]= { 'G','M','T',0 };
4313 WCHAR
*nextPtr
, day
[10], month
[4], *monthPtr
;
4316 SYSTEMTIME st
= { 0 };
4318 ptr
= strchrW(value
, ',');
4321 if (ptr
- value
== 3)
4323 memcpy(day
, value
, (ptr
- value
) * sizeof(WCHAR
));
4325 st
.wDayOfWeek
= HTTP_ParseWkday(day
);
4326 if (st
.wDayOfWeek
> 6)
4328 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4332 else if (ptr
- value
< sizeof(day
) / sizeof(day
[0]))
4334 memcpy(day
, value
, (ptr
- value
) * sizeof(WCHAR
));
4335 day
[ptr
- value
+ 1] = 0;
4336 st
.wDayOfWeek
= HTTP_ParseWeekday(day
);
4337 if (st
.wDayOfWeek
> 6)
4339 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4345 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4350 while (isspaceW(*ptr
))
4353 num
= strtoulW(ptr
, &nextPtr
, 10);
4354 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
4356 ERR("unexpected day %s\n", debugstr_w(value
));
4360 st
.wDay
= (WORD
)num
;
4364 ERR("unexpected month format %s\n", debugstr_w(ptr
));
4369 for (monthPtr
= month
; *ptr
!= '-' &&
4370 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
4374 st
.wMonth
= HTTP_ParseMonth(month
);
4375 if (!st
.wMonth
|| st
.wMonth
> 12)
4377 ERR("unexpected month %s\n", debugstr_w(month
));
4383 ERR("unexpected year format %s\n", debugstr_w(ptr
));
4388 num
= strtoulW(ptr
, &nextPtr
, 10);
4389 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
4391 ERR("unexpected year %s\n", debugstr_w(value
));
4395 st
.wYear
= (WORD
)num
;
4397 if (!HTTP_ParseTime(&st
, &ptr
))
4400 while (isspaceW(*ptr
))
4403 if (strcmpW(ptr
, gmt
))
4405 ERR("unexpected time zone %s\n", debugstr_w(ptr
));
4408 return SystemTimeToFileTime(&st
, ft
);
4411 static BOOL
HTTP_ParseDate(LPCWSTR value
, FILETIME
*ft
)
4413 static const WCHAR zero
[] = { '0',0 };
4416 if (!strcmpW(value
, zero
))
4418 ft
->dwLowDateTime
= ft
->dwHighDateTime
= 0;
4421 else if (strchrW(value
, ','))
4423 ret
= HTTP_ParseRfc1123Date(value
, ft
);
4426 ret
= HTTP_ParseRfc850Date(value
, ft
);
4428 ERR("unexpected date format %s\n", debugstr_w(value
));
4433 ret
= HTTP_ParseDateAsAsctime(value
, ft
);
4435 ERR("unexpected date format %s\n", debugstr_w(value
));
4440 static void HTTP_ProcessExpires(http_request_t
*request
)
4442 BOOL expirationFound
= FALSE
;
4445 /* Look for a Cache-Control header with a max-age directive, as it takes
4446 * precedence over the Expires header.
4448 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szCache_Control
, 0, FALSE
);
4449 if (headerIndex
!= -1)
4451 LPHTTPHEADERW ccHeader
= &request
->custHeaders
[headerIndex
];
4454 for (ptr
= ccHeader
->lpszValue
; ptr
&& *ptr
; )
4456 LPWSTR comma
= strchrW(ptr
, ','), end
, equal
;
4461 end
= ptr
+ strlenW(ptr
);
4462 for (equal
= end
- 1; equal
> ptr
&& *equal
!= '='; equal
--)
4466 static const WCHAR max_age
[] = {
4467 'm','a','x','-','a','g','e',0 };
4469 if (!strncmpiW(ptr
, max_age
, equal
- ptr
- 1))
4474 age
= strtoulW(equal
+ 1, &nextPtr
, 10);
4475 if (nextPtr
> equal
+ 1)
4479 NtQuerySystemTime( &ft
);
4480 /* Age is in seconds, FILETIME resolution is in
4481 * 100 nanosecond intervals.
4483 ft
.QuadPart
+= age
* (ULONGLONG
)1000000;
4484 request
->expires
.dwLowDateTime
= ft
.u
.LowPart
;
4485 request
->expires
.dwHighDateTime
= ft
.u
.HighPart
;
4486 expirationFound
= TRUE
;
4493 while (isspaceW(*ptr
))
4500 if (!expirationFound
)
4502 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szExpires
, 0, FALSE
);
4503 if (headerIndex
!= -1)
4505 LPHTTPHEADERW expiresHeader
= &request
->custHeaders
[headerIndex
];
4508 if (HTTP_ParseDate(expiresHeader
->lpszValue
, &ft
))
4510 expirationFound
= TRUE
;
4511 request
->expires
= ft
;
4515 if (!expirationFound
)
4519 /* With no known age, default to 10 minutes until expiration. */
4520 NtQuerySystemTime( &t
);
4521 t
.QuadPart
+= 10 * 60 * (ULONGLONG
)10000000;
4522 request
->expires
.dwLowDateTime
= t
.u
.LowPart
;
4523 request
->expires
.dwHighDateTime
= t
.u
.HighPart
;
4527 static void HTTP_ProcessLastModified(http_request_t
*request
)
4531 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szLast_Modified
, 0, FALSE
);
4532 if (headerIndex
!= -1)
4534 LPHTTPHEADERW expiresHeader
= &request
->custHeaders
[headerIndex
];
4537 if (HTTP_ParseDate(expiresHeader
->lpszValue
, &ft
))
4538 request
->last_modified
= ft
;
4542 static void http_process_keep_alive(http_request_t
*req
)
4546 index
= HTTP_GetCustomHeaderIndex(req
, szConnection
, 0, FALSE
);
4548 req
->netconn
->keep_alive
= !strcmpiW(req
->custHeaders
[index
].lpszValue
, szKeepAlive
);
4550 req
->netconn
->keep_alive
= !strcmpiW(req
->version
, g_szHttp1_1
);
4553 static DWORD
open_http_connection(http_request_t
*request
, BOOL
*reusing
)
4555 const BOOL is_https
= (request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) != 0;
4556 netconn_t
*netconn
= NULL
;
4559 assert(!request
->netconn
);
4560 reset_data_stream(request
);
4562 res
= HTTP_ResolveName(request
);
4563 if(res
!= ERROR_SUCCESS
)
4566 EnterCriticalSection(&connection_pool_cs
);
4568 while(!list_empty(&request
->server
->conn_pool
)) {
4569 netconn
= LIST_ENTRY(list_head(&request
->server
->conn_pool
), netconn_t
, pool_entry
);
4570 list_remove(&netconn
->pool_entry
);
4572 if(NETCON_is_alive(netconn
))
4575 TRACE("connection %p closed during idle\n", netconn
);
4576 free_netconn(netconn
);
4580 LeaveCriticalSection(&connection_pool_cs
);
4583 TRACE("<-- reusing %p netconn\n", netconn
);
4584 request
->netconn
= netconn
;
4586 return ERROR_SUCCESS
;
4589 TRACE("connecting to %s, proxy %s\n", debugstr_w(request
->server
->name
),
4590 request
->proxy
? debugstr_w(request
->proxy
->name
) : "(null)");
4592 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4593 INTERNET_STATUS_CONNECTING_TO_SERVER
,
4594 request
->server
->addr_str
,
4595 strlen(request
->server
->addr_str
)+1);
4597 res
= create_netconn(is_https
, request
->proxy
? request
->proxy
: request
->server
, request
->security_flags
,
4598 (request
->hdr
.ErrorMask
& INTERNET_ERROR_MASK_COMBINED_SEC_CERT
) != 0,
4599 request
->connect_timeout
, &netconn
);
4600 if(res
!= ERROR_SUCCESS
) {
4601 ERR("create_netconn failed: %u\n", res
);
4605 request
->netconn
= netconn
;
4607 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4608 INTERNET_STATUS_CONNECTED_TO_SERVER
,
4609 request
->server
->addr_str
, strlen(request
->server
->addr_str
)+1);
4612 /* Note: we differ from Microsoft's WinINet here. they seem to have
4613 * a bug that causes no status callbacks to be sent when starting
4614 * a tunnel to a proxy server using the CONNECT verb. i believe our
4615 * behaviour to be more correct and to not cause any incompatibilities
4616 * because using a secure connection through a proxy server is a rare
4617 * case that would be hard for anyone to depend on */
4619 res
= HTTP_SecureProxyConnect(request
);
4620 if(res
== ERROR_SUCCESS
)
4621 res
= NETCON_secure_connect(request
->netconn
, request
->server
);
4624 if(res
!= ERROR_SUCCESS
) {
4625 http_release_netconn(request
, FALSE
);
4630 TRACE("Created connection to %s: %p\n", debugstr_w(request
->server
->name
), netconn
);
4631 return ERROR_SUCCESS
;
4634 /***********************************************************************
4635 * HTTP_HttpSendRequestW (internal)
4637 * Sends the specified request to the HTTP server
4640 * ERROR_SUCCESS on success
4641 * win32 error code on failure
4644 static DWORD
HTTP_HttpSendRequestW(http_request_t
*request
, LPCWSTR lpszHeaders
,
4645 DWORD dwHeaderLength
, LPVOID lpOptional
, DWORD dwOptionalLength
,
4646 DWORD dwContentLength
, BOOL bEndRequest
)
4649 BOOL redirected
= FALSE
;
4650 LPWSTR requestString
= NULL
;
4653 static const WCHAR szPost
[] = { 'P','O','S','T',0 };
4654 static const WCHAR szContentLength
[] =
4655 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4656 WCHAR contentLengthStr
[sizeof szContentLength
/2 /* includes \r\n */ + 20 /* int */ ];
4659 TRACE("--> %p\n", request
);
4661 assert(request
->hdr
.htype
== WH_HHTTPREQ
);
4663 /* if the verb is NULL default to GET */
4665 request
->verb
= heap_strdupW(szGET
);
4667 if (dwContentLength
|| strcmpW(request
->verb
, szGET
))
4669 sprintfW(contentLengthStr
, szContentLength
, dwContentLength
);
4670 HTTP_HttpAddRequestHeadersW(request
, contentLengthStr
, -1L, HTTP_ADDREQ_FLAG_REPLACE
);
4671 request
->bytesToWrite
= dwContentLength
;
4673 if (request
->session
->appInfo
->agent
)
4675 WCHAR
*agent_header
;
4676 static const WCHAR user_agent
[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4679 len
= strlenW(request
->session
->appInfo
->agent
) + strlenW(user_agent
);
4680 agent_header
= heap_alloc(len
* sizeof(WCHAR
));
4681 sprintfW(agent_header
, user_agent
, request
->session
->appInfo
->agent
);
4683 HTTP_HttpAddRequestHeadersW(request
, agent_header
, strlenW(agent_header
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4684 heap_free(agent_header
);
4686 if (request
->hdr
.dwFlags
& INTERNET_FLAG_PRAGMA_NOCACHE
)
4688 static const WCHAR pragma_nocache
[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4689 HTTP_HttpAddRequestHeadersW(request
, pragma_nocache
, strlenW(pragma_nocache
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4691 if ((request
->hdr
.dwFlags
& INTERNET_FLAG_NO_CACHE_WRITE
) && !strcmpW(request
->verb
, szPost
))
4693 static const WCHAR cache_control
[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4694 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4695 HTTP_HttpAddRequestHeadersW(request
, cache_control
, strlenW(cache_control
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4698 /* add the headers the caller supplied */
4699 if( lpszHeaders
&& dwHeaderLength
)
4700 HTTP_HttpAddRequestHeadersW(request
, lpszHeaders
, dwHeaderLength
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REPLACE
);
4705 BOOL reusing_connection
;
4709 reusing_connection
= request
->netconn
!= NULL
;
4712 request
->contentLength
= ~0u;
4713 request
->bytesToWrite
= 0;
4716 if (TRACE_ON(wininet
))
4718 LPHTTPHEADERW Host
= HTTP_GetHeader(request
, hostW
);
4719 TRACE("Going to url %s %s\n", debugstr_w(Host
->lpszValue
), debugstr_w(request
->path
));
4722 HTTP_FixURL(request
);
4723 if (request
->hdr
.dwFlags
& INTERNET_FLAG_KEEP_CONNECTION
)
4725 HTTP_ProcessHeader(request
, szConnection
, szKeepAlive
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
4727 HTTP_InsertAuthorization(request
, request
->authInfo
, szAuthorization
);
4728 HTTP_InsertAuthorization(request
, request
->proxyAuthInfo
, szProxy_Authorization
);
4730 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
))
4731 HTTP_InsertCookies(request
);
4735 WCHAR
*url
= build_proxy_path_url(request
);
4736 requestString
= HTTP_BuildHeaderRequestString(request
, request
->verb
, url
, request
->version
);
4740 requestString
= HTTP_BuildHeaderRequestString(request
, request
->verb
, request
->path
, request
->version
);
4743 TRACE("Request header -> %s\n", debugstr_w(requestString
) );
4745 if (!reusing_connection
&& (res
= open_http_connection(request
, &reusing_connection
)) != ERROR_SUCCESS
)
4748 /* send the request as ASCII, tack on the optional data */
4749 if (!lpOptional
|| redirected
)
4750 dwOptionalLength
= 0;
4751 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
4752 NULL
, 0, NULL
, NULL
);
4753 ascii_req
= heap_alloc(len
+ dwOptionalLength
);
4754 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
4755 ascii_req
, len
, NULL
, NULL
);
4757 memcpy( &ascii_req
[len
-1], lpOptional
, dwOptionalLength
);
4758 len
= (len
+ dwOptionalLength
- 1);
4760 TRACE("full request -> %s\n", debugstr_a(ascii_req
) );
4762 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4763 INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
4765 NETCON_set_timeout( request
->netconn
, TRUE
, request
->send_timeout
);
4766 res
= NETCON_send(request
->netconn
, ascii_req
, len
, 0, &cnt
);
4767 heap_free( ascii_req
);
4768 if(res
!= ERROR_SUCCESS
) {
4769 TRACE("send failed: %u\n", res
);
4770 if(!reusing_connection
)
4772 http_release_netconn(request
, FALSE
);
4777 request
->bytesWritten
= dwOptionalLength
;
4779 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4780 INTERNET_STATUS_REQUEST_SENT
,
4781 &len
, sizeof(DWORD
));
4787 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4788 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
4790 responseLen
= HTTP_GetResponseHeaders(request
, TRUE
);
4791 /* FIXME: We should know that connection is closed before sending
4792 * headers. Otherwise wrong callbacks are executed */
4793 if(!responseLen
&& reusing_connection
) {
4794 TRACE("Connection closed by server, reconnecting\n");
4795 http_release_netconn(request
, FALSE
);
4800 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4801 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
,
4804 http_process_keep_alive(request
);
4805 HTTP_ProcessCookies(request
);
4806 HTTP_ProcessExpires(request
);
4807 HTTP_ProcessLastModified(request
);
4809 res
= set_content_length(request
);
4810 if(res
!= ERROR_SUCCESS
)
4812 if(!request
->contentLength
)
4813 http_release_netconn(request
, TRUE
);
4815 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
) && responseLen
)
4817 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
4818 dwBufferSize
=sizeof(szNewLocation
);
4819 switch(request
->status_code
) {
4820 case HTTP_STATUS_REDIRECT
:
4821 case HTTP_STATUS_MOVED
:
4822 case HTTP_STATUS_REDIRECT_KEEP_VERB
:
4823 case HTTP_STATUS_REDIRECT_METHOD
:
4824 if(HTTP_HttpQueryInfoW(request
,HTTP_QUERY_LOCATION
,szNewLocation
,&dwBufferSize
,NULL
) != ERROR_SUCCESS
)
4827 if (strcmpW(request
->verb
, szGET
) && strcmpW(request
->verb
, szHEAD
) &&
4828 request
->status_code
!= HTTP_STATUS_REDIRECT_KEEP_VERB
)
4830 heap_free(request
->verb
);
4831 request
->verb
= heap_strdupW(szGET
);
4833 http_release_netconn(request
, drain_content(request
, FALSE
));
4834 if ((new_url
= HTTP_GetRedirectURL( request
, szNewLocation
)))
4836 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
4837 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
4838 res
= HTTP_HandleRedirect(request
, new_url
);
4839 if (res
== ERROR_SUCCESS
)
4841 heap_free(requestString
);
4844 heap_free( new_url
);
4849 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTH
) && res
== ERROR_SUCCESS
)
4851 WCHAR szAuthValue
[2048];
4853 if (request
->status_code
== HTTP_STATUS_DENIED
)
4855 LPHTTPHEADERW Host
= HTTP_GetHeader(request
, hostW
);
4857 while (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_WWW_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
4859 if (HTTP_DoAuthorization(request
, szAuthValue
,
4861 request
->session
->userName
,
4862 request
->session
->password
,
4865 heap_free(requestString
);
4866 if(!drain_content(request
, TRUE
)) {
4867 FIXME("Could not drain content\n");
4868 http_release_netconn(request
, FALSE
);
4876 TRACE("Cleaning wrong authorization data\n");
4877 destroy_authinfo(request
->authInfo
);
4878 request
->authInfo
= NULL
;
4881 if (request
->status_code
== HTTP_STATUS_PROXY_AUTH_REQ
)
4884 while (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_PROXY_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
4886 if (HTTP_DoAuthorization(request
, szAuthValue
,
4887 &request
->proxyAuthInfo
,
4888 request
->session
->appInfo
->proxyUsername
,
4889 request
->session
->appInfo
->proxyPassword
,
4892 if(!drain_content(request
, TRUE
)) {
4893 FIXME("Could not drain content\n");
4894 http_release_netconn(request
, FALSE
);
4902 TRACE("Cleaning wrong proxy authorization data\n");
4903 destroy_authinfo(request
->proxyAuthInfo
);
4904 request
->proxyAuthInfo
= NULL
;
4910 res
= ERROR_SUCCESS
;
4915 heap_free(requestString
);
4917 /* TODO: send notification for P3P header */
4919 if(res
== ERROR_SUCCESS
)
4920 create_cache_entry(request
);
4922 if (request
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4924 if (res
== ERROR_SUCCESS
) {
4925 if(bEndRequest
&& request
->contentLength
&& request
->bytesWritten
== request
->bytesToWrite
)
4926 HTTP_ReceiveRequestData(request
, TRUE
);
4928 send_request_complete(request
,
4929 request
->session
->hdr
.dwInternalFlags
& INET_OPENURL
? (DWORD_PTR
)request
->hdr
.hInternet
: 1, 0);
4931 send_request_complete(request
, 0, res
);
4947 } send_request_task_t
;
4949 /***********************************************************************
4951 * Helper functions for the HttpSendRequest(Ex) functions
4954 static void AsyncHttpSendRequestProc(task_header_t
*hdr
)
4956 send_request_task_t
*task
= (send_request_task_t
*)hdr
;
4957 http_request_t
*request
= (http_request_t
*)task
->hdr
.hdr
;
4959 TRACE("%p\n", request
);
4961 HTTP_HttpSendRequestW(request
, task
->headers
, task
->headers_len
, task
->optional
,
4962 task
->optional_len
, task
->content_len
, task
->end_request
);
4964 heap_free(task
->headers
);
4968 static DWORD
HTTP_HttpEndRequestW(http_request_t
*request
, DWORD dwFlags
, DWORD_PTR dwContext
)
4972 DWORD res
= ERROR_SUCCESS
;
4974 if(!request
->netconn
) {
4975 WARN("Not connected\n");
4976 send_request_complete(request
, 0, ERROR_INTERNET_OPERATION_CANCELLED
);
4977 return ERROR_INTERNET_OPERATION_CANCELLED
;
4980 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4981 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
4983 responseLen
= HTTP_GetResponseHeaders(request
, TRUE
);
4985 res
= ERROR_HTTP_HEADER_NOT_FOUND
;
4987 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4988 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
, sizeof(DWORD
));
4990 /* process cookies here. Is this right? */
4991 http_process_keep_alive(request
);
4992 HTTP_ProcessCookies(request
);
4993 HTTP_ProcessExpires(request
);
4994 HTTP_ProcessLastModified(request
);
4996 if ((res
= set_content_length(request
)) == ERROR_SUCCESS
) {
4997 if(!request
->contentLength
)
4998 http_release_netconn(request
, TRUE
);
5001 if (res
== ERROR_SUCCESS
&& !(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
))
5003 switch(request
->status_code
) {
5004 case HTTP_STATUS_REDIRECT
:
5005 case HTTP_STATUS_MOVED
:
5006 case HTTP_STATUS_REDIRECT_METHOD
:
5007 case HTTP_STATUS_REDIRECT_KEEP_VERB
: {
5008 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
5009 dwBufferSize
=sizeof(szNewLocation
);
5010 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_LOCATION
, szNewLocation
, &dwBufferSize
, NULL
) != ERROR_SUCCESS
)
5013 if (strcmpW(request
->verb
, szGET
) && strcmpW(request
->verb
, szHEAD
) &&
5014 request
->status_code
!= HTTP_STATUS_REDIRECT_KEEP_VERB
)
5016 heap_free(request
->verb
);
5017 request
->verb
= heap_strdupW(szGET
);
5019 http_release_netconn(request
, drain_content(request
, FALSE
));
5020 if ((new_url
= HTTP_GetRedirectURL( request
, szNewLocation
)))
5022 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
5023 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
5024 res
= HTTP_HandleRedirect(request
, new_url
);
5025 if (res
== ERROR_SUCCESS
)
5026 res
= HTTP_HttpSendRequestW(request
, NULL
, 0, NULL
, 0, 0, TRUE
);
5027 heap_free( new_url
);
5033 if(res
== ERROR_SUCCESS
)
5034 create_cache_entry(request
);
5036 if (res
== ERROR_SUCCESS
&& request
->contentLength
)
5037 HTTP_ReceiveRequestData(request
, TRUE
);
5039 send_request_complete(request
, res
== ERROR_SUCCESS
, res
);
5044 /***********************************************************************
5045 * HttpEndRequestA (WININET.@)
5047 * Ends an HTTP request that was started by HttpSendRequestEx
5050 * TRUE if successful
5054 BOOL WINAPI
HttpEndRequestA(HINTERNET hRequest
,
5055 LPINTERNET_BUFFERSA lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
5057 TRACE("(%p, %p, %08x, %08lx)\n", hRequest
, lpBuffersOut
, dwFlags
, dwContext
);
5061 SetLastError(ERROR_INVALID_PARAMETER
);
5065 return HttpEndRequestW(hRequest
, NULL
, dwFlags
, dwContext
);
5068 static void AsyncHttpEndRequestProc(WORKREQUEST
*work
)
5070 struct WORKREQ_HTTPENDREQUESTW
const *req
= &work
->u
.HttpEndRequestW
;
5071 http_request_t
*request
= (http_request_t
*)work
->hdr
;
5073 TRACE("%p\n", request
);
5075 HTTP_HttpEndRequestW(request
, req
->dwFlags
, req
->dwContext
);
5078 /***********************************************************************
5079 * HttpEndRequestW (WININET.@)
5081 * Ends an HTTP request that was started by HttpSendRequestEx
5084 * TRUE if successful
5088 BOOL WINAPI
HttpEndRequestW(HINTERNET hRequest
,
5089 LPINTERNET_BUFFERSW lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
5091 http_request_t
*request
;
5094 TRACE("%p %p %x %lx -->\n", hRequest
, lpBuffersOut
, dwFlags
, dwContext
);
5098 SetLastError(ERROR_INVALID_PARAMETER
);
5102 request
= (http_request_t
*) get_handle_object( hRequest
);
5104 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
5106 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
5108 WININET_Release( &request
->hdr
);
5111 request
->hdr
.dwFlags
|= dwFlags
;
5113 if (request
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
5116 struct WORKREQ_HTTPENDREQUESTW
*work_endrequest
;
5118 task
= alloc_async_task(&request
->hdr
, AsyncHttpEndRequestProc
, sizeof(*task
));
5119 work_endrequest
= &task
->u
.HttpEndRequestW
;
5120 work_endrequest
->dwFlags
= dwFlags
;
5121 work_endrequest
->dwContext
= dwContext
;
5123 INTERNET_AsyncCall(task
);
5124 res
= ERROR_IO_PENDING
;
5127 res
= HTTP_HttpEndRequestW(request
, dwFlags
, dwContext
);
5129 WININET_Release( &request
->hdr
);
5130 TRACE("%u <--\n", res
);
5131 if(res
!= ERROR_SUCCESS
)
5133 return res
== ERROR_SUCCESS
;
5136 /***********************************************************************
5137 * HttpSendRequestExA (WININET.@)
5139 * Sends the specified request to the HTTP server and allows chunked
5144 * Failure: FALSE, call GetLastError() for more information.
5146 BOOL WINAPI
HttpSendRequestExA(HINTERNET hRequest
,
5147 LPINTERNET_BUFFERSA lpBuffersIn
,
5148 LPINTERNET_BUFFERSA lpBuffersOut
,
5149 DWORD dwFlags
, DWORD_PTR dwContext
)
5151 INTERNET_BUFFERSW BuffersInW
;
5154 LPWSTR header
= NULL
;
5156 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
5157 lpBuffersOut
, dwFlags
, dwContext
);
5161 BuffersInW
.dwStructSize
= sizeof(LPINTERNET_BUFFERSW
);
5162 if (lpBuffersIn
->lpcszHeader
)
5164 headerlen
= MultiByteToWideChar(CP_ACP
,0,lpBuffersIn
->lpcszHeader
,
5165 lpBuffersIn
->dwHeadersLength
,0,0);
5166 header
= heap_alloc(headerlen
*sizeof(WCHAR
));
5167 if (!(BuffersInW
.lpcszHeader
= header
))
5169 SetLastError(ERROR_OUTOFMEMORY
);
5172 BuffersInW
.dwHeadersLength
= MultiByteToWideChar(CP_ACP
, 0,
5173 lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
5177 BuffersInW
.lpcszHeader
= NULL
;
5178 BuffersInW
.dwHeadersTotal
= lpBuffersIn
->dwHeadersTotal
;
5179 BuffersInW
.lpvBuffer
= lpBuffersIn
->lpvBuffer
;
5180 BuffersInW
.dwBufferLength
= lpBuffersIn
->dwBufferLength
;
5181 BuffersInW
.dwBufferTotal
= lpBuffersIn
->dwBufferTotal
;
5182 BuffersInW
.Next
= NULL
;
5185 rc
= HttpSendRequestExW(hRequest
, lpBuffersIn
? &BuffersInW
: NULL
, NULL
, dwFlags
, dwContext
);
5191 /***********************************************************************
5192 * HttpSendRequestExW (WININET.@)
5194 * Sends the specified request to the HTTP server and allows chunked
5199 * Failure: FALSE, call GetLastError() for more information.
5201 BOOL WINAPI
HttpSendRequestExW(HINTERNET hRequest
,
5202 LPINTERNET_BUFFERSW lpBuffersIn
,
5203 LPINTERNET_BUFFERSW lpBuffersOut
,
5204 DWORD dwFlags
, DWORD_PTR dwContext
)
5206 http_request_t
*request
;
5207 http_session_t
*session
;
5211 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
5212 lpBuffersOut
, dwFlags
, dwContext
);
5214 request
= (http_request_t
*) get_handle_object( hRequest
);
5216 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
5218 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5222 session
= request
->session
;
5223 assert(session
->hdr
.htype
== WH_HHTTPSESSION
);
5224 hIC
= session
->appInfo
;
5225 assert(hIC
->hdr
.htype
== WH_HINIT
);
5227 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
5229 send_request_task_t
*task
;
5231 task
= alloc_async_task(&request
->hdr
, AsyncHttpSendRequestProc
, sizeof(*task
));
5236 if (lpBuffersIn
->lpcszHeader
)
5238 if (lpBuffersIn
->dwHeadersLength
== ~0u)
5239 size
= (strlenW( lpBuffersIn
->lpcszHeader
) + 1) * sizeof(WCHAR
);
5241 size
= lpBuffersIn
->dwHeadersLength
* sizeof(WCHAR
);
5243 task
->headers
= heap_alloc(size
);
5244 memcpy(task
->headers
, lpBuffersIn
->lpcszHeader
, size
);
5246 else task
->headers
= NULL
;
5248 task
->headers_len
= size
/ sizeof(WCHAR
);
5249 task
->optional
= lpBuffersIn
->lpvBuffer
;
5250 task
->optional_len
= lpBuffersIn
->dwBufferLength
;
5251 task
->content_len
= lpBuffersIn
->dwBufferTotal
;
5255 task
->headers
= NULL
;
5256 task
->headers_len
= 0;
5257 task
->optional
= NULL
;
5258 task
->optional_len
= 0;
5259 task
->content_len
= 0;
5262 task
->end_request
= FALSE
;
5264 INTERNET_AsyncCall(&task
->hdr
);
5265 res
= ERROR_IO_PENDING
;
5270 res
= HTTP_HttpSendRequestW(request
, lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
5271 lpBuffersIn
->lpvBuffer
, lpBuffersIn
->dwBufferLength
,
5272 lpBuffersIn
->dwBufferTotal
, FALSE
);
5274 res
= HTTP_HttpSendRequestW(request
, NULL
, 0, NULL
, 0, 0, FALSE
);
5279 WININET_Release( &request
->hdr
);
5283 return res
== ERROR_SUCCESS
;
5286 /***********************************************************************
5287 * HttpSendRequestW (WININET.@)
5289 * Sends the specified request to the HTTP server
5296 BOOL WINAPI
HttpSendRequestW(HINTERNET hHttpRequest
, LPCWSTR lpszHeaders
,
5297 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
5299 http_request_t
*request
;
5300 http_session_t
*session
= NULL
;
5301 appinfo_t
*hIC
= NULL
;
5302 DWORD res
= ERROR_SUCCESS
;
5304 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest
,
5305 debugstr_wn(lpszHeaders
, dwHeaderLength
), dwHeaderLength
, lpOptional
, dwOptionalLength
);
5307 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
5308 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
5310 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5314 session
= request
->session
;
5315 if (NULL
== session
|| session
->hdr
.htype
!= WH_HHTTPSESSION
)
5317 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5321 hIC
= session
->appInfo
;
5322 if (NULL
== hIC
|| hIC
->hdr
.htype
!= WH_HINIT
)
5324 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5328 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
5330 send_request_task_t
*task
;
5332 task
= alloc_async_task(&request
->hdr
, AsyncHttpSendRequestProc
, sizeof(*task
));
5337 if (dwHeaderLength
== ~0u) size
= (strlenW(lpszHeaders
) + 1) * sizeof(WCHAR
);
5338 else size
= dwHeaderLength
* sizeof(WCHAR
);
5340 task
->headers
= heap_alloc(size
);
5341 memcpy(task
->headers
, lpszHeaders
, size
);
5344 task
->headers
= NULL
;
5345 task
->headers_len
= dwHeaderLength
;
5346 task
->optional
= lpOptional
;
5347 task
->optional_len
= dwOptionalLength
;
5348 task
->content_len
= dwOptionalLength
;
5349 task
->end_request
= TRUE
;
5351 INTERNET_AsyncCall(&task
->hdr
);
5352 res
= ERROR_IO_PENDING
;
5356 res
= HTTP_HttpSendRequestW(request
, lpszHeaders
,
5357 dwHeaderLength
, lpOptional
, dwOptionalLength
,
5358 dwOptionalLength
, TRUE
);
5362 WININET_Release( &request
->hdr
);
5365 return res
== ERROR_SUCCESS
;
5368 /***********************************************************************
5369 * HttpSendRequestA (WININET.@)
5371 * Sends the specified request to the HTTP server
5378 BOOL WINAPI
HttpSendRequestA(HINTERNET hHttpRequest
, LPCSTR lpszHeaders
,
5379 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
5382 LPWSTR szHeaders
=NULL
;
5383 DWORD nLen
=dwHeaderLength
;
5384 if(lpszHeaders
!=NULL
)
5386 nLen
=MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,NULL
,0);
5387 szHeaders
= heap_alloc(nLen
*sizeof(WCHAR
));
5388 MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,szHeaders
,nLen
);
5390 result
= HttpSendRequestW(hHttpRequest
, szHeaders
, nLen
, lpOptional
, dwOptionalLength
);
5391 heap_free(szHeaders
);
5395 /***********************************************************************
5396 * HTTPSESSION_Destroy (internal)
5398 * Deallocate session handle
5401 static void HTTPSESSION_Destroy(object_header_t
*hdr
)
5403 http_session_t
*session
= (http_session_t
*) hdr
;
5405 TRACE("%p\n", session
);
5407 WININET_Release(&session
->appInfo
->hdr
);
5409 heap_free(session
->hostName
);
5410 heap_free(session
->password
);
5411 heap_free(session
->userName
);
5414 static DWORD
HTTPSESSION_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
5416 http_session_t
*ses
= (http_session_t
*)hdr
;
5419 case INTERNET_OPTION_HANDLE_TYPE
:
5420 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5422 if (*size
< sizeof(ULONG
))
5423 return ERROR_INSUFFICIENT_BUFFER
;
5425 *size
= sizeof(DWORD
);
5426 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_CONNECT_HTTP
;
5427 return ERROR_SUCCESS
;
5428 case INTERNET_OPTION_CONNECT_TIMEOUT
:
5429 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5431 if (*size
< sizeof(DWORD
))
5432 return ERROR_INSUFFICIENT_BUFFER
;
5434 *size
= sizeof(DWORD
);
5435 *(DWORD
*)buffer
= ses
->connect_timeout
;
5436 return ERROR_SUCCESS
;
5438 case INTERNET_OPTION_SEND_TIMEOUT
:
5439 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5441 if (*size
< sizeof(DWORD
))
5442 return ERROR_INSUFFICIENT_BUFFER
;
5444 *size
= sizeof(DWORD
);
5445 *(DWORD
*)buffer
= ses
->send_timeout
;
5446 return ERROR_SUCCESS
;
5448 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
5449 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5451 if (*size
< sizeof(DWORD
))
5452 return ERROR_INSUFFICIENT_BUFFER
;
5454 *size
= sizeof(DWORD
);
5455 *(DWORD
*)buffer
= ses
->receive_timeout
;
5456 return ERROR_SUCCESS
;
5459 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
5462 static DWORD
HTTPSESSION_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
5464 http_session_t
*ses
= (http_session_t
*)hdr
;
5467 case INTERNET_OPTION_USERNAME
:
5469 heap_free(ses
->userName
);
5470 if (!(ses
->userName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
5471 return ERROR_SUCCESS
;
5473 case INTERNET_OPTION_PASSWORD
:
5475 heap_free(ses
->password
);
5476 if (!(ses
->password
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
5477 return ERROR_SUCCESS
;
5479 case INTERNET_OPTION_CONNECT_TIMEOUT
:
5481 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
5482 ses
->connect_timeout
= *(DWORD
*)buffer
;
5483 return ERROR_SUCCESS
;
5485 case INTERNET_OPTION_SEND_TIMEOUT
:
5487 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
5488 ses
->send_timeout
= *(DWORD
*)buffer
;
5489 return ERROR_SUCCESS
;
5491 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
5493 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
5494 ses
->receive_timeout
= *(DWORD
*)buffer
;
5495 return ERROR_SUCCESS
;
5500 return INET_SetOption(hdr
, option
, buffer
, size
);
5503 static const object_vtbl_t HTTPSESSIONVtbl
= {
5504 HTTPSESSION_Destroy
,
5506 HTTPSESSION_QueryOption
,
5507 HTTPSESSION_SetOption
,
5516 /***********************************************************************
5517 * HTTP_Connect (internal)
5519 * Create http session handle
5522 * HINTERNET a session handle on success
5526 DWORD
HTTP_Connect(appinfo_t
*hIC
, LPCWSTR lpszServerName
,
5527 INTERNET_PORT serverPort
, LPCWSTR lpszUserName
,
5528 LPCWSTR lpszPassword
, DWORD dwFlags
, DWORD_PTR dwContext
,
5529 DWORD dwInternalFlags
, HINTERNET
*ret
)
5531 http_session_t
*session
= NULL
;
5535 if (!lpszServerName
|| !lpszServerName
[0])
5536 return ERROR_INVALID_PARAMETER
;
5538 assert( hIC
->hdr
.htype
== WH_HINIT
);
5540 session
= alloc_object(&hIC
->hdr
, &HTTPSESSIONVtbl
, sizeof(http_session_t
));
5542 return ERROR_OUTOFMEMORY
;
5545 * According to my tests. The name is not resolved until a request is sent
5548 session
->hdr
.htype
= WH_HHTTPSESSION
;
5549 session
->hdr
.dwFlags
= dwFlags
;
5550 session
->hdr
.dwContext
= dwContext
;
5551 session
->hdr
.dwInternalFlags
|= dwInternalFlags
;
5553 WININET_AddRef( &hIC
->hdr
);
5554 session
->appInfo
= hIC
;
5555 list_add_head( &hIC
->hdr
.children
, &session
->hdr
.entry
);
5557 if(hIC
->proxy
&& hIC
->accessType
== INTERNET_OPEN_TYPE_PROXY
) {
5558 if(hIC
->proxyBypass
)
5559 FIXME("Proxy bypass is ignored.\n");
5561 session
->hostName
= heap_strdupW(lpszServerName
);
5562 if (lpszUserName
&& lpszUserName
[0])
5563 session
->userName
= heap_strdupW(lpszUserName
);
5564 if (lpszPassword
&& lpszPassword
[0])
5565 session
->password
= heap_strdupW(lpszPassword
);
5566 session
->hostPort
= serverPort
;
5567 session
->connect_timeout
= hIC
->connect_timeout
;
5568 session
->send_timeout
= INFINITE
;
5569 session
->receive_timeout
= INFINITE
;
5571 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5572 if (!(session
->hdr
.dwInternalFlags
& INET_OPENURL
))
5574 INTERNET_SendCallback(&hIC
->hdr
, dwContext
,
5575 INTERNET_STATUS_HANDLE_CREATED
, &session
->hdr
.hInternet
,
5580 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5584 TRACE("%p --> %p\n", hIC
, session
);
5586 *ret
= session
->hdr
.hInternet
;
5587 return ERROR_SUCCESS
;
5590 /***********************************************************************
5591 * HTTP_clear_response_headers (internal)
5593 * clear out any old response headers
5595 static void HTTP_clear_response_headers( http_request_t
*request
)
5599 for( i
=0; i
<request
->nCustHeaders
; i
++)
5601 if( !request
->custHeaders
[i
].lpszField
)
5603 if( !request
->custHeaders
[i
].lpszValue
)
5605 if ( request
->custHeaders
[i
].wFlags
& HDR_ISREQUEST
)
5607 HTTP_DeleteCustomHeader( request
, i
);
5612 /***********************************************************************
5613 * HTTP_GetResponseHeaders (internal)
5615 * Read server response
5622 static INT
HTTP_GetResponseHeaders(http_request_t
*request
, BOOL clear
)
5625 WCHAR buffer
[MAX_REPLY_LEN
];
5626 DWORD buflen
= MAX_REPLY_LEN
;
5627 BOOL bSuccess
= FALSE
;
5629 char bufferA
[MAX_REPLY_LEN
];
5630 LPWSTR status_code
= NULL
, status_text
= NULL
;
5631 DWORD cchMaxRawHeaders
= 1024;
5632 LPWSTR lpszRawHeaders
= NULL
;
5634 DWORD cchRawHeaders
= 0;
5635 BOOL codeHundred
= FALSE
;
5639 if(!request
->netconn
)
5642 NETCON_set_timeout( request
->netconn
, FALSE
, request
->receive_timeout
);
5644 static const WCHAR szHundred
[] = {'1','0','0',0};
5646 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5648 buflen
= MAX_REPLY_LEN
;
5649 if (!read_line(request
, bufferA
, &buflen
))
5652 /* clear old response headers (eg. from a redirect response) */
5654 HTTP_clear_response_headers( request
);
5659 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
5660 /* check is this a status code line? */
5661 if (!strncmpW(buffer
, g_szHttp1_0
, 4))
5663 /* split the version from the status code */
5664 status_code
= strchrW( buffer
, ' ' );
5669 /* split the status code from the status text */
5670 status_text
= strchrW( status_code
, ' ' );
5675 request
->status_code
= atoiW(status_code
);
5677 TRACE("version [%s] status code [%s] status text [%s]\n",
5678 debugstr_w(buffer
), debugstr_w(status_code
), debugstr_w(status_text
) );
5680 codeHundred
= (!strcmpW(status_code
, szHundred
));
5682 else if (!codeHundred
)
5684 WARN("No status line at head of response (%s)\n", debugstr_w(buffer
));
5686 heap_free(request
->version
);
5687 heap_free(request
->statusText
);
5689 request
->status_code
= HTTP_STATUS_OK
;
5690 request
->version
= heap_strdupW(g_szHttp1_0
);
5691 request
->statusText
= heap_strdupW(szOK
);
5693 heap_free(request
->rawHeaders
);
5694 request
->rawHeaders
= heap_strdupW(szDefaultHeader
);
5699 } while (codeHundred
);
5701 /* Add status code */
5702 HTTP_ProcessHeader(request
, szStatus
, status_code
,
5703 HTTP_ADDHDR_FLAG_REPLACE
);
5705 heap_free(request
->version
);
5706 heap_free(request
->statusText
);
5708 request
->version
= heap_strdupW(buffer
);
5709 request
->statusText
= heap_strdupW(status_text
);
5711 /* Restore the spaces */
5712 *(status_code
-1) = ' ';
5713 *(status_text
-1) = ' ';
5715 /* regenerate raw headers */
5716 lpszRawHeaders
= heap_alloc((cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
5717 if (!lpszRawHeaders
) goto lend
;
5719 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5720 cchMaxRawHeaders
*= 2;
5721 temp
= heap_realloc(lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
5722 if (temp
== NULL
) goto lend
;
5723 lpszRawHeaders
= temp
;
5724 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
5725 cchRawHeaders
+= (buflen
-1);
5726 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
5727 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
5728 lpszRawHeaders
[cchRawHeaders
] = '\0';
5730 /* Parse each response line */
5733 buflen
= MAX_REPLY_LEN
;
5734 if (read_line(request
, bufferA
, &buflen
))
5736 LPWSTR
* pFieldAndValue
;
5738 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA
));
5740 if (!bufferA
[0]) break;
5741 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
5743 pFieldAndValue
= HTTP_InterpretHttpHeader(buffer
);
5746 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5747 cchMaxRawHeaders
*= 2;
5748 temp
= heap_realloc(lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
5749 if (temp
== NULL
) goto lend
;
5750 lpszRawHeaders
= temp
;
5751 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
5752 cchRawHeaders
+= (buflen
-1);
5753 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
5754 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
5755 lpszRawHeaders
[cchRawHeaders
] = '\0';
5757 HTTP_ProcessHeader(request
, pFieldAndValue
[0], pFieldAndValue
[1],
5758 HTTP_ADDREQ_FLAG_ADD
);
5760 HTTP_FreeTokens(pFieldAndValue
);
5771 /* make sure the response header is terminated with an empty line. Some apps really
5772 truly care about that empty line being there for some reason. Just add it to the
5774 if (cchRawHeaders
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5776 cchMaxRawHeaders
= cchRawHeaders
+ strlenW(szCrLf
);
5777 temp
= heap_realloc(lpszRawHeaders
, (cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
5778 if (temp
== NULL
) goto lend
;
5779 lpszRawHeaders
= temp
;
5782 memcpy(&lpszRawHeaders
[cchRawHeaders
], szCrLf
, sizeof(szCrLf
));
5784 heap_free(request
->rawHeaders
);
5785 request
->rawHeaders
= lpszRawHeaders
;
5786 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders
));
5796 heap_free(lpszRawHeaders
);
5801 /***********************************************************************
5802 * HTTP_InterpretHttpHeader (internal)
5804 * Parse server response
5808 * Pointer to array of field, value, NULL on success.
5811 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
)
5813 LPWSTR
* pTokenPair
;
5817 pTokenPair
= heap_alloc_zero(sizeof(*pTokenPair
)*3);
5819 pszColon
= strchrW(buffer
, ':');
5820 /* must have two tokens */
5823 HTTP_FreeTokens(pTokenPair
);
5825 TRACE("No ':' in line: %s\n", debugstr_w(buffer
));
5829 pTokenPair
[0] = heap_alloc((pszColon
- buffer
+ 1) * sizeof(WCHAR
));
5832 HTTP_FreeTokens(pTokenPair
);
5835 memcpy(pTokenPair
[0], buffer
, (pszColon
- buffer
) * sizeof(WCHAR
));
5836 pTokenPair
[0][pszColon
- buffer
] = '\0';
5840 len
= strlenW(pszColon
);
5841 pTokenPair
[1] = heap_alloc((len
+ 1) * sizeof(WCHAR
));
5844 HTTP_FreeTokens(pTokenPair
);
5847 memcpy(pTokenPair
[1], pszColon
, (len
+ 1) * sizeof(WCHAR
));
5849 strip_spaces(pTokenPair
[0]);
5850 strip_spaces(pTokenPair
[1]);
5852 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair
[0]), debugstr_w(pTokenPair
[1]));
5856 /***********************************************************************
5857 * HTTP_ProcessHeader (internal)
5859 * Stuff header into header tables according to <dwModifier>
5863 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5865 static DWORD
HTTP_ProcessHeader(http_request_t
*request
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
)
5867 LPHTTPHEADERW lphttpHdr
= NULL
;
5869 BOOL request_only
= dwModifier
& HTTP_ADDHDR_FLAG_REQ
;
5870 DWORD res
= ERROR_HTTP_INVALID_HEADER
;
5872 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field
), debugstr_w(value
), dwModifier
);
5874 /* REPLACE wins out over ADD */
5875 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
5876 dwModifier
&= ~HTTP_ADDHDR_FLAG_ADD
;
5878 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD
)
5881 index
= HTTP_GetCustomHeaderIndex(request
, field
, 0, request_only
);
5885 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD_IF_NEW
)
5886 return ERROR_HTTP_INVALID_HEADER
;
5887 lphttpHdr
= &request
->custHeaders
[index
];
5893 hdr
.lpszField
= (LPWSTR
)field
;
5894 hdr
.lpszValue
= (LPWSTR
)value
;
5895 hdr
.wFlags
= hdr
.wCount
= 0;
5897 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5898 hdr
.wFlags
|= HDR_ISREQUEST
;
5900 return HTTP_InsertCustomHeader(request
, &hdr
);
5902 /* no value to delete */
5903 else return ERROR_SUCCESS
;
5905 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5906 lphttpHdr
->wFlags
|= HDR_ISREQUEST
;
5908 lphttpHdr
->wFlags
&= ~HDR_ISREQUEST
;
5910 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
5912 HTTP_DeleteCustomHeader( request
, index
);
5918 hdr
.lpszField
= (LPWSTR
)field
;
5919 hdr
.lpszValue
= (LPWSTR
)value
;
5920 hdr
.wFlags
= hdr
.wCount
= 0;
5922 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5923 hdr
.wFlags
|= HDR_ISREQUEST
;
5925 return HTTP_InsertCustomHeader(request
, &hdr
);
5928 return ERROR_SUCCESS
;
5930 else if (dwModifier
& COALESCEFLAGS
)
5935 INT origlen
= strlenW(lphttpHdr
->lpszValue
);
5936 INT valuelen
= strlenW(value
);
5938 if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
)
5941 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
5943 else if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON
)
5946 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
5949 len
= origlen
+ valuelen
+ ((ch
> 0) ? 2 : 0);
5951 lpsztmp
= heap_realloc(lphttpHdr
->lpszValue
, (len
+1)*sizeof(WCHAR
));
5954 lphttpHdr
->lpszValue
= lpsztmp
;
5955 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
5958 lphttpHdr
->lpszValue
[origlen
] = ch
;
5960 lphttpHdr
->lpszValue
[origlen
] = ' ';
5964 memcpy(&lphttpHdr
->lpszValue
[origlen
], value
, valuelen
*sizeof(WCHAR
));
5965 lphttpHdr
->lpszValue
[len
] = '\0';
5966 res
= ERROR_SUCCESS
;
5970 WARN("heap_realloc (%d bytes) failed\n",len
+1);
5971 res
= ERROR_OUTOFMEMORY
;
5974 TRACE("<-- %d\n", res
);
5978 /***********************************************************************
5979 * HTTP_GetCustomHeaderIndex (internal)
5981 * Return index of custom header from header array
5984 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*request
, LPCWSTR lpszField
,
5985 int requested_index
, BOOL request_only
)
5989 TRACE("%s, %d, %d\n", debugstr_w(lpszField
), requested_index
, request_only
);
5991 for (index
= 0; index
< request
->nCustHeaders
; index
++)
5993 if (strcmpiW(request
->custHeaders
[index
].lpszField
, lpszField
))
5996 if (request_only
&& !(request
->custHeaders
[index
].wFlags
& HDR_ISREQUEST
))
5999 if (!request_only
&& (request
->custHeaders
[index
].wFlags
& HDR_ISREQUEST
))
6002 if (requested_index
== 0)
6007 if (index
>= request
->nCustHeaders
)
6010 TRACE("Return: %d\n", index
);
6015 /***********************************************************************
6016 * HTTP_InsertCustomHeader (internal)
6018 * Insert header into array
6021 static DWORD
HTTP_InsertCustomHeader(http_request_t
*request
, LPHTTPHEADERW lpHdr
)
6024 LPHTTPHEADERW lph
= NULL
;
6026 TRACE("--> %s: %s\n", debugstr_w(lpHdr
->lpszField
), debugstr_w(lpHdr
->lpszValue
));
6027 count
= request
->nCustHeaders
+ 1;
6029 lph
= heap_realloc_zero(request
->custHeaders
, sizeof(HTTPHEADERW
) * count
);
6031 lph
= heap_alloc_zero(sizeof(HTTPHEADERW
) * count
);
6034 return ERROR_OUTOFMEMORY
;
6036 request
->custHeaders
= lph
;
6037 request
->custHeaders
[count
-1].lpszField
= heap_strdupW(lpHdr
->lpszField
);
6038 request
->custHeaders
[count
-1].lpszValue
= heap_strdupW(lpHdr
->lpszValue
);
6039 request
->custHeaders
[count
-1].wFlags
= lpHdr
->wFlags
;
6040 request
->custHeaders
[count
-1].wCount
= lpHdr
->wCount
;
6041 request
->nCustHeaders
++;
6043 return ERROR_SUCCESS
;
6047 /***********************************************************************
6048 * HTTP_DeleteCustomHeader (internal)
6050 * Delete header from array
6051 * If this function is called, the indexs may change.
6053 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*request
, DWORD index
)
6055 if( request
->nCustHeaders
<= 0 )
6057 if( index
>= request
->nCustHeaders
)
6059 request
->nCustHeaders
--;
6061 heap_free(request
->custHeaders
[index
].lpszField
);
6062 heap_free(request
->custHeaders
[index
].lpszValue
);
6064 memmove( &request
->custHeaders
[index
], &request
->custHeaders
[index
+1],
6065 (request
->nCustHeaders
- index
)* sizeof(HTTPHEADERW
) );
6066 memset( &request
->custHeaders
[request
->nCustHeaders
], 0, sizeof(HTTPHEADERW
) );
6072 /***********************************************************************
6073 * HTTP_VerifyValidHeader (internal)
6075 * Verify the given header is not invalid for the given http request
6078 static BOOL
HTTP_VerifyValidHeader(http_request_t
*request
, LPCWSTR field
)
6080 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6081 if (!strcmpW(request
->version
, g_szHttp1_0
) && !strcmpiW(field
, szAccept_Encoding
))
6082 return ERROR_HTTP_INVALID_HEADER
;
6084 return ERROR_SUCCESS
;
6087 /***********************************************************************
6088 * IsHostInProxyBypassList (@)
6093 BOOL WINAPI
IsHostInProxyBypassList(DWORD flags
, LPCSTR szHost
, DWORD length
)
6095 FIXME("STUB: flags=%d host=%s length=%d\n",flags
,szHost
,length
);
6099 /***********************************************************************
6100 * InternetShowSecurityInfoByURLA (@)
6102 BOOL WINAPI
InternetShowSecurityInfoByURLA(LPCSTR url
, HWND window
)
6104 FIXME("stub: %s %p\n", url
, window
);
6108 /***********************************************************************
6109 * InternetShowSecurityInfoByURLW (@)
6111 BOOL WINAPI
InternetShowSecurityInfoByURLW(LPCWSTR url
, HWND window
)
6113 FIXME("stub: %s %p\n", debugstr_w(url
), window
);
6117 /***********************************************************************
6118 * ShowX509EncodedCertificate (@)
6120 DWORD WINAPI
ShowX509EncodedCertificate(HWND parent
, LPBYTE cert
, DWORD len
)
6122 PCCERT_CONTEXT certContext
= CertCreateCertificateContext(X509_ASN_ENCODING
,
6128 CRYPTUI_VIEWCERTIFICATE_STRUCTW view
;
6130 memset(&view
, 0, sizeof(view
));
6131 view
.hwndParent
= parent
;
6132 view
.pCertContext
= certContext
;
6133 if (CryptUIDlgViewCertificateW(&view
, NULL
))
6134 ret
= ERROR_SUCCESS
;
6136 ret
= GetLastError();
6137 CertFreeCertificateContext(certContext
);
6140 ret
= GetLastError();