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
);
254 server_t
*get_server(const WCHAR
*name
, INTERNET_PORT port
, BOOL do_create
)
256 server_t
*iter
, *server
= NULL
;
258 EnterCriticalSection(&connection_pool_cs
);
260 LIST_FOR_EACH_ENTRY(iter
, &connection_pool
, server_t
, entry
) {
261 if(iter
->port
== port
&& !strcmpW(iter
->name
, name
)) {
263 server_addref(server
);
268 if(!server
&& do_create
) {
269 server
= heap_alloc_zero(sizeof(*server
));
271 server
->ref
= 2; /* list reference and return */
273 list_init(&server
->conn_pool
);
274 server
->name
= heap_strdupW(name
);
276 list_add_head(&connection_pool
, &server
->entry
);
284 LeaveCriticalSection(&connection_pool_cs
);
289 BOOL
collect_connections(collect_type_t collect_type
)
291 netconn_t
*netconn
, *netconn_safe
;
292 server_t
*server
, *server_safe
;
293 BOOL remaining
= FALSE
;
296 now
= GetTickCount64();
298 LIST_FOR_EACH_ENTRY_SAFE(server
, server_safe
, &connection_pool
, server_t
, entry
) {
299 LIST_FOR_EACH_ENTRY_SAFE(netconn
, netconn_safe
, &server
->conn_pool
, netconn_t
, pool_entry
) {
300 if(collect_type
> COLLECT_TIMEOUT
|| netconn
->keep_until
< now
) {
301 TRACE("freeing %p\n", netconn
);
302 list_remove(&netconn
->pool_entry
);
303 free_netconn(netconn
);
309 if(collect_type
== COLLECT_CLEANUP
) {
310 list_remove(&server
->entry
);
311 list_init(&server
->entry
);
312 server_release(server
);
319 static DWORD WINAPI
collect_connections_proc(void *arg
)
321 BOOL remaining_conns
;
324 /* FIXME: Use more sophisticated method */
327 EnterCriticalSection(&connection_pool_cs
);
329 remaining_conns
= collect_connections(COLLECT_TIMEOUT
);
331 collector_running
= FALSE
;
333 LeaveCriticalSection(&connection_pool_cs
);
334 }while(remaining_conns
);
336 FreeLibraryAndExitThread(WININET_hModule
, 0);
339 static LPHTTPHEADERW
HTTP_GetHeader(http_request_t
*req
, LPCWSTR head
)
342 HeaderIndex
= HTTP_GetCustomHeaderIndex(req
, head
, 0, TRUE
);
343 if (HeaderIndex
== -1)
346 return &req
->custHeaders
[HeaderIndex
];
355 struct data_stream_vtbl_t
{
356 DWORD (*get_avail_data
)(data_stream_t
*,http_request_t
*);
357 BOOL (*end_of_data
)(data_stream_t
*,http_request_t
*);
358 DWORD (*read
)(data_stream_t
*,http_request_t
*,BYTE
*,DWORD
,DWORD
*,read_mode_t
);
359 BOOL (*drain_content
)(data_stream_t
*,http_request_t
*);
360 void (*destroy
)(data_stream_t
*);
364 data_stream_t data_stream
;
366 BYTE buf
[READ_BUFFER_SIZE
];
372 static inline void destroy_data_stream(data_stream_t
*stream
)
374 stream
->vtbl
->destroy(stream
);
377 static void reset_data_stream(http_request_t
*req
)
379 destroy_data_stream(req
->data_stream
);
380 req
->data_stream
= &req
->netconn_stream
.data_stream
;
381 req
->read_pos
= req
->read_size
= req
->netconn_stream
.content_read
= 0;
382 req
->read_chunked
= req
->read_gzip
= FALSE
;
388 data_stream_t stream
;
389 data_stream_t
*parent_stream
;
391 BYTE buf
[READ_BUFFER_SIZE
];
397 static DWORD
gzip_get_avail_data(data_stream_t
*stream
, http_request_t
*req
)
399 /* Allow reading only from read buffer */
403 static BOOL
gzip_end_of_data(data_stream_t
*stream
, http_request_t
*req
)
405 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
406 return gzip_stream
->end_of_data
;
409 static DWORD
gzip_read(data_stream_t
*stream
, http_request_t
*req
, BYTE
*buf
, DWORD size
,
410 DWORD
*read
, read_mode_t read_mode
)
412 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
413 z_stream
*zstream
= &gzip_stream
->zstream
;
414 DWORD current_read
, ret_read
= 0;
417 DWORD res
= ERROR_SUCCESS
;
419 while(size
&& !gzip_stream
->end_of_data
) {
420 end
= gzip_stream
->parent_stream
->vtbl
->end_of_data(gzip_stream
->parent_stream
, req
);
422 if(gzip_stream
->buf_size
<= 64 && !end
) {
423 if(gzip_stream
->buf_pos
) {
424 if(gzip_stream
->buf_size
)
425 memmove(gzip_stream
->buf
, gzip_stream
->buf
+gzip_stream
->buf_pos
, gzip_stream
->buf_size
);
426 gzip_stream
->buf_pos
= 0;
428 res
= gzip_stream
->parent_stream
->vtbl
->read(gzip_stream
->parent_stream
, req
, gzip_stream
->buf
+gzip_stream
->buf_size
,
429 sizeof(gzip_stream
->buf
)-gzip_stream
->buf_size
, ¤t_read
, read_mode
);
430 gzip_stream
->buf_size
+= current_read
;
431 if(res
!= ERROR_SUCCESS
)
433 end
= gzip_stream
->parent_stream
->vtbl
->end_of_data(gzip_stream
->parent_stream
, req
);
434 if(!current_read
&& !end
) {
435 if(read_mode
!= READMODE_NOBLOCK
) {
436 WARN("unexpected end of data\n");
437 gzip_stream
->end_of_data
= TRUE
;
441 if(gzip_stream
->buf_size
<= 64 && !end
)
445 zstream
->next_in
= gzip_stream
->buf
+gzip_stream
->buf_pos
;
446 zstream
->avail_in
= gzip_stream
->buf_size
-(end
? 0 : 64);
447 zstream
->next_out
= buf
+ret_read
;
448 zstream
->avail_out
= size
;
449 zres
= inflate(&gzip_stream
->zstream
, 0);
450 current_read
= size
- zstream
->avail_out
;
451 size
-= current_read
;
452 ret_read
+= current_read
;
453 gzip_stream
->buf_size
-= zstream
->next_in
- (gzip_stream
->buf
+gzip_stream
->buf_pos
);
454 gzip_stream
->buf_pos
= zstream
->next_in
-gzip_stream
->buf
;
455 if(zres
== Z_STREAM_END
) {
456 TRACE("end of data\n");
457 gzip_stream
->end_of_data
= TRUE
;
459 }else if(zres
!= Z_OK
) {
460 WARN("inflate failed %d: %s\n", zres
, debugstr_a(zstream
->msg
));
462 res
= ERROR_INTERNET_DECODING_FAILED
;
466 if(ret_read
&& read_mode
== READMODE_ASYNC
)
467 read_mode
= READMODE_NOBLOCK
;
470 TRACE("read %u bytes\n", ret_read
);
475 static BOOL
gzip_drain_content(data_stream_t
*stream
, http_request_t
*req
)
477 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
478 return gzip_stream
->parent_stream
->vtbl
->drain_content(gzip_stream
->parent_stream
, req
);
481 static void gzip_destroy(data_stream_t
*stream
)
483 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
485 destroy_data_stream(gzip_stream
->parent_stream
);
487 if(!gzip_stream
->end_of_data
)
488 inflateEnd(&gzip_stream
->zstream
);
489 heap_free(gzip_stream
);
492 static const data_stream_vtbl_t gzip_stream_vtbl
= {
500 static voidpf
wininet_zalloc(voidpf opaque
, uInt items
, uInt size
)
502 return heap_alloc(items
*size
);
505 static void wininet_zfree(voidpf opaque
, voidpf address
)
510 static DWORD
init_gzip_stream(http_request_t
*req
)
512 gzip_stream_t
*gzip_stream
;
515 gzip_stream
= heap_alloc_zero(sizeof(gzip_stream_t
));
517 return ERROR_OUTOFMEMORY
;
519 gzip_stream
->stream
.vtbl
= &gzip_stream_vtbl
;
520 gzip_stream
->zstream
.zalloc
= wininet_zalloc
;
521 gzip_stream
->zstream
.zfree
= wininet_zfree
;
523 zres
= inflateInit2(&gzip_stream
->zstream
, 0x1f);
525 ERR("inflateInit failed: %d\n", zres
);
526 heap_free(gzip_stream
);
527 return ERROR_OUTOFMEMORY
;
530 index
= HTTP_GetCustomHeaderIndex(req
, szContent_Length
, 0, FALSE
);
532 HTTP_DeleteCustomHeader(req
, index
);
535 memcpy(gzip_stream
->buf
, req
->read_buf
+req
->read_pos
, req
->read_size
);
536 gzip_stream
->buf_size
= req
->read_size
;
537 req
->read_pos
= req
->read_size
= 0;
540 req
->read_gzip
= TRUE
;
541 gzip_stream
->parent_stream
= req
->data_stream
;
542 req
->data_stream
= &gzip_stream
->stream
;
543 return ERROR_SUCCESS
;
548 static DWORD
init_gzip_stream(http_request_t
*req
)
550 ERR("gzip stream not supported, missing zlib.\n");
551 return ERROR_SUCCESS
;
556 /***********************************************************************
557 * HTTP_Tokenize (internal)
559 * Tokenize a string, allocating memory for the tokens.
561 static LPWSTR
* HTTP_Tokenize(LPCWSTR string
, LPCWSTR token_string
)
563 LPWSTR
* token_array
;
570 /* empty string has no tokens */
574 for (i
= 0; string
[i
]; i
++)
576 if (!strncmpW(string
+i
, token_string
, strlenW(token_string
)))
580 /* we want to skip over separators, but not the null terminator */
581 for (j
= 0; j
< strlenW(token_string
) - 1; j
++)
589 /* add 1 for terminating NULL */
590 token_array
= heap_alloc((tokens
+1) * sizeof(*token_array
));
591 token_array
[tokens
] = NULL
;
594 for (i
= 0; i
< tokens
; i
++)
597 next_token
= strstrW(string
, token_string
);
598 if (!next_token
) next_token
= string
+strlenW(string
);
599 len
= next_token
- string
;
600 token_array
[i
] = heap_alloc((len
+1)*sizeof(WCHAR
));
601 memcpy(token_array
[i
], string
, len
*sizeof(WCHAR
));
602 token_array
[i
][len
] = '\0';
603 string
= next_token
+strlenW(token_string
);
608 /***********************************************************************
609 * HTTP_FreeTokens (internal)
611 * Frees memory returned from HTTP_Tokenize.
613 static void HTTP_FreeTokens(LPWSTR
* token_array
)
616 for (i
= 0; token_array
[i
]; i
++) heap_free(token_array
[i
]);
617 heap_free(token_array
);
620 static void HTTP_FixURL(http_request_t
*request
)
622 static const WCHAR szSlash
[] = { '/',0 };
623 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/', 0 };
625 /* If we don't have a path we set it to root */
626 if (NULL
== request
->path
)
627 request
->path
= heap_strdupW(szSlash
);
628 else /* remove \r and \n*/
630 int nLen
= strlenW(request
->path
);
631 while ((nLen
>0 ) && ((request
->path
[nLen
-1] == '\r')||(request
->path
[nLen
-1] == '\n')))
634 request
->path
[nLen
]='\0';
636 /* Replace '\' with '/' */
639 if (request
->path
[nLen
] == '\\') request
->path
[nLen
]='/';
643 if(CSTR_EQUAL
!= CompareStringW( LOCALE_INVARIANT
, NORM_IGNORECASE
,
644 request
->path
, strlenW(request
->path
), szHttp
, strlenW(szHttp
) )
645 && request
->path
[0] != '/') /* not an absolute path ?? --> fix it !! */
647 WCHAR
*fixurl
= heap_alloc((strlenW(request
->path
) + 2)*sizeof(WCHAR
));
649 strcpyW(fixurl
+ 1, request
->path
);
650 heap_free( request
->path
);
651 request
->path
= fixurl
;
655 static LPWSTR
HTTP_BuildHeaderRequestString( http_request_t
*request
, LPCWSTR verb
, LPCWSTR path
, LPCWSTR version
)
657 LPWSTR requestString
;
663 static const WCHAR szSpace
[] = { ' ',0 };
664 static const WCHAR szColon
[] = { ':',' ',0 };
665 static const WCHAR sztwocrlf
[] = {'\r','\n','\r','\n', 0};
667 /* allocate space for an array of all the string pointers to be added */
668 len
= (request
->nCustHeaders
)*4 + 10;
669 req
= heap_alloc(len
*sizeof(LPCWSTR
));
671 /* add the verb, path and HTTP version string */
679 /* Append custom request headers */
680 for (i
= 0; i
< request
->nCustHeaders
; i
++)
682 if (request
->custHeaders
[i
].wFlags
& HDR_ISREQUEST
)
685 req
[n
++] = request
->custHeaders
[i
].lpszField
;
687 req
[n
++] = request
->custHeaders
[i
].lpszValue
;
689 TRACE("Adding custom header %s (%s)\n",
690 debugstr_w(request
->custHeaders
[i
].lpszField
),
691 debugstr_w(request
->custHeaders
[i
].lpszValue
));
696 ERR("oops. buffer overrun\n");
699 requestString
= HTTP_build_req( req
, 4 );
703 * Set (header) termination string for request
704 * Make sure there's exactly two new lines at the end of the request
706 p
= &requestString
[strlenW(requestString
)-1];
707 while ( (*p
== '\n') || (*p
== '\r') )
709 strcpyW( p
+1, sztwocrlf
);
711 return requestString
;
714 static void HTTP_ProcessCookies( http_request_t
*request
)
718 LPHTTPHEADERW setCookieHeader
;
720 if(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
)
723 while((HeaderIndex
= HTTP_GetCustomHeaderIndex(request
, szSet_Cookie
, numCookies
++, FALSE
)) != -1)
729 setCookieHeader
= &request
->custHeaders
[HeaderIndex
];
731 if (!setCookieHeader
->lpszValue
)
734 host
= HTTP_GetHeader(request
, hostW
);
738 data
= strchrW(setCookieHeader
->lpszValue
, '=');
742 name
= heap_strndupW(setCookieHeader
->lpszValue
, data
-setCookieHeader
->lpszValue
);
747 set_cookie(host
->lpszValue
, request
->path
, name
, data
);
752 static void strip_spaces(LPWSTR start
)
757 while (*str
== ' ' && *str
!= '\0')
761 memmove(start
, str
, sizeof(WCHAR
) * (strlenW(str
) + 1));
763 end
= start
+ strlenW(start
) - 1;
764 while (end
>= start
&& *end
== ' ')
771 static inline BOOL
is_basic_auth_value( LPCWSTR pszAuthValue
, LPWSTR
*pszRealm
)
773 static const WCHAR szBasic
[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
774 static const WCHAR szRealm
[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
776 is_basic
= !strncmpiW(pszAuthValue
, szBasic
, ARRAYSIZE(szBasic
)) &&
777 ((pszAuthValue
[ARRAYSIZE(szBasic
)] == ' ') || !pszAuthValue
[ARRAYSIZE(szBasic
)]);
778 if (is_basic
&& pszRealm
)
781 LPCWSTR ptr
= &pszAuthValue
[ARRAYSIZE(szBasic
)];
785 token
= strchrW(ptr
,'=');
789 while (*realm
== ' ' && *realm
!= '\0')
791 if(!strncmpiW(realm
, szRealm
, ARRAYSIZE(szRealm
)) &&
792 (realm
[ARRAYSIZE(szRealm
)] == ' ' || realm
[ARRAYSIZE(szRealm
)] == '='))
795 while (*token
== ' ' && *token
!= '\0')
799 *pszRealm
= heap_strdupW(token
);
800 strip_spaces(*pszRealm
);
807 static void destroy_authinfo( struct HttpAuthInfo
*authinfo
)
809 if (!authinfo
) return;
811 if (SecIsValidHandle(&authinfo
->ctx
))
812 DeleteSecurityContext(&authinfo
->ctx
);
813 if (SecIsValidHandle(&authinfo
->cred
))
814 FreeCredentialsHandle(&authinfo
->cred
);
816 heap_free(authinfo
->auth_data
);
817 heap_free(authinfo
->scheme
);
821 static UINT
retrieve_cached_basic_authorization(LPWSTR host
, LPWSTR realm
, LPSTR
*auth_data
)
823 basicAuthorizationData
*ad
;
826 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host
),debugstr_w(realm
));
828 EnterCriticalSection(&authcache_cs
);
829 LIST_FOR_EACH_ENTRY(ad
, &basicAuthorizationCache
, basicAuthorizationData
, entry
)
831 if (!strcmpiW(host
,ad
->host
) && !strcmpW(realm
,ad
->realm
))
833 TRACE("Authorization found in cache\n");
834 *auth_data
= heap_alloc(ad
->authorizationLen
);
835 memcpy(*auth_data
,ad
->authorization
,ad
->authorizationLen
);
836 rc
= ad
->authorizationLen
;
840 LeaveCriticalSection(&authcache_cs
);
844 static void cache_basic_authorization(LPWSTR host
, LPWSTR realm
, LPSTR auth_data
, UINT auth_data_len
)
847 basicAuthorizationData
* ad
= NULL
;
849 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host
),debugstr_w(realm
),debugstr_an(auth_data
,auth_data_len
));
851 EnterCriticalSection(&authcache_cs
);
852 LIST_FOR_EACH(cursor
, &basicAuthorizationCache
)
854 basicAuthorizationData
*check
= LIST_ENTRY(cursor
,basicAuthorizationData
,entry
);
855 if (!strcmpiW(host
,check
->host
) && !strcmpW(realm
,check
->realm
))
864 TRACE("Found match in cache, replacing\n");
865 heap_free(ad
->authorization
);
866 ad
->authorization
= heap_alloc(auth_data_len
);
867 memcpy(ad
->authorization
, auth_data
, auth_data_len
);
868 ad
->authorizationLen
= auth_data_len
;
872 ad
= heap_alloc(sizeof(basicAuthorizationData
));
873 ad
->host
= heap_strdupW(host
);
874 ad
->realm
= heap_strdupW(realm
);
875 ad
->authorization
= heap_alloc(auth_data_len
);
876 memcpy(ad
->authorization
, auth_data
, auth_data_len
);
877 ad
->authorizationLen
= auth_data_len
;
878 list_add_head(&basicAuthorizationCache
,&ad
->entry
);
879 TRACE("authorization cached\n");
881 LeaveCriticalSection(&authcache_cs
);
884 static BOOL
retrieve_cached_authorization(LPWSTR host
, LPWSTR scheme
,
885 SEC_WINNT_AUTH_IDENTITY_W
*nt_auth_identity
)
887 authorizationData
*ad
;
889 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host
), debugstr_w(scheme
));
891 EnterCriticalSection(&authcache_cs
);
892 LIST_FOR_EACH_ENTRY(ad
, &authorizationCache
, authorizationData
, entry
) {
893 if(!strcmpiW(host
, ad
->host
) && !strcmpiW(scheme
, ad
->scheme
)) {
894 TRACE("Authorization found in cache\n");
896 nt_auth_identity
->User
= heap_strdupW(ad
->user
);
897 nt_auth_identity
->Password
= heap_strdupW(ad
->password
);
898 nt_auth_identity
->Domain
= heap_alloc(sizeof(WCHAR
)*ad
->domain_len
);
899 if(!nt_auth_identity
->User
|| !nt_auth_identity
->Password
||
900 (!nt_auth_identity
->Domain
&& ad
->domain_len
)) {
901 heap_free(nt_auth_identity
->User
);
902 heap_free(nt_auth_identity
->Password
);
903 heap_free(nt_auth_identity
->Domain
);
907 nt_auth_identity
->Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
908 nt_auth_identity
->UserLength
= ad
->user_len
;
909 nt_auth_identity
->PasswordLength
= ad
->password_len
;
910 memcpy(nt_auth_identity
->Domain
, ad
->domain
, sizeof(WCHAR
)*ad
->domain_len
);
911 nt_auth_identity
->DomainLength
= ad
->domain_len
;
912 LeaveCriticalSection(&authcache_cs
);
916 LeaveCriticalSection(&authcache_cs
);
921 static void cache_authorization(LPWSTR host
, LPWSTR scheme
,
922 SEC_WINNT_AUTH_IDENTITY_W
*nt_auth_identity
)
924 authorizationData
*ad
;
927 TRACE("Caching authorization for %s:%s\n", debugstr_w(host
), debugstr_w(scheme
));
929 EnterCriticalSection(&authcache_cs
);
930 LIST_FOR_EACH_ENTRY(ad
, &authorizationCache
, authorizationData
, entry
)
931 if(!strcmpiW(host
, ad
->host
) && !strcmpiW(scheme
, ad
->scheme
)) {
938 heap_free(ad
->password
);
939 heap_free(ad
->domain
);
941 ad
= heap_alloc(sizeof(authorizationData
));
943 LeaveCriticalSection(&authcache_cs
);
947 ad
->host
= heap_strdupW(host
);
948 ad
->scheme
= heap_strdupW(scheme
);
949 list_add_head(&authorizationCache
, &ad
->entry
);
952 ad
->user
= heap_strndupW(nt_auth_identity
->User
, nt_auth_identity
->UserLength
);
953 ad
->password
= heap_strndupW(nt_auth_identity
->Password
, nt_auth_identity
->PasswordLength
);
954 ad
->domain
= heap_strndupW(nt_auth_identity
->Domain
, nt_auth_identity
->DomainLength
);
955 ad
->user_len
= nt_auth_identity
->UserLength
;
956 ad
->password_len
= nt_auth_identity
->PasswordLength
;
957 ad
->domain_len
= nt_auth_identity
->DomainLength
;
959 if(!ad
->host
|| !ad
->scheme
|| !ad
->user
|| !ad
->password
960 || (nt_auth_identity
->Domain
&& !ad
->domain
)) {
962 heap_free(ad
->scheme
);
964 heap_free(ad
->password
);
965 heap_free(ad
->domain
);
966 list_remove(&ad
->entry
);
970 LeaveCriticalSection(&authcache_cs
);
973 static BOOL
HTTP_DoAuthorization( http_request_t
*request
, LPCWSTR pszAuthValue
,
974 struct HttpAuthInfo
**ppAuthInfo
,
975 LPWSTR domain_and_username
, LPWSTR password
,
978 SECURITY_STATUS sec_status
;
979 struct HttpAuthInfo
*pAuthInfo
= *ppAuthInfo
;
981 LPWSTR szRealm
= NULL
;
983 TRACE("%s\n", debugstr_w(pszAuthValue
));
990 pAuthInfo
= heap_alloc(sizeof(*pAuthInfo
));
994 SecInvalidateHandle(&pAuthInfo
->cred
);
995 SecInvalidateHandle(&pAuthInfo
->ctx
);
996 memset(&pAuthInfo
->exp
, 0, sizeof(pAuthInfo
->exp
));
998 pAuthInfo
->auth_data
= NULL
;
999 pAuthInfo
->auth_data_len
= 0;
1000 pAuthInfo
->finished
= FALSE
;
1002 if (is_basic_auth_value(pszAuthValue
,NULL
))
1004 static const WCHAR szBasic
[] = {'B','a','s','i','c',0};
1005 pAuthInfo
->scheme
= heap_strdupW(szBasic
);
1006 if (!pAuthInfo
->scheme
)
1008 heap_free(pAuthInfo
);
1015 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity
;
1017 pAuthInfo
->scheme
= heap_strdupW(pszAuthValue
);
1018 if (!pAuthInfo
->scheme
)
1020 heap_free(pAuthInfo
);
1024 if (domain_and_username
)
1026 WCHAR
*user
= strchrW(domain_and_username
, '\\');
1027 WCHAR
*domain
= domain_and_username
;
1029 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1031 pAuthData
= &nt_auth_identity
;
1036 user
= domain_and_username
;
1040 nt_auth_identity
.Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
1041 nt_auth_identity
.User
= user
;
1042 nt_auth_identity
.UserLength
= strlenW(nt_auth_identity
.User
);
1043 nt_auth_identity
.Domain
= domain
;
1044 nt_auth_identity
.DomainLength
= domain
? user
- domain
- 1 : 0;
1045 nt_auth_identity
.Password
= password
;
1046 nt_auth_identity
.PasswordLength
= strlenW(nt_auth_identity
.Password
);
1048 cache_authorization(host
, pAuthInfo
->scheme
, &nt_auth_identity
);
1050 else if(retrieve_cached_authorization(host
, pAuthInfo
->scheme
, &nt_auth_identity
))
1051 pAuthData
= &nt_auth_identity
;
1053 /* use default credentials */
1056 sec_status
= AcquireCredentialsHandleW(NULL
, pAuthInfo
->scheme
,
1057 SECPKG_CRED_OUTBOUND
, NULL
,
1059 NULL
, &pAuthInfo
->cred
,
1062 if(pAuthData
&& !domain_and_username
) {
1063 heap_free(nt_auth_identity
.User
);
1064 heap_free(nt_auth_identity
.Domain
);
1065 heap_free(nt_auth_identity
.Password
);
1068 if (sec_status
== SEC_E_OK
)
1070 PSecPkgInfoW sec_pkg_info
;
1071 sec_status
= QuerySecurityPackageInfoW(pAuthInfo
->scheme
, &sec_pkg_info
);
1072 if (sec_status
== SEC_E_OK
)
1074 pAuthInfo
->max_token
= sec_pkg_info
->cbMaxToken
;
1075 FreeContextBuffer(sec_pkg_info
);
1078 if (sec_status
!= SEC_E_OK
)
1080 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1081 debugstr_w(pAuthInfo
->scheme
), sec_status
);
1082 heap_free(pAuthInfo
->scheme
);
1083 heap_free(pAuthInfo
);
1087 *ppAuthInfo
= pAuthInfo
;
1089 else if (pAuthInfo
->finished
)
1092 if ((strlenW(pszAuthValue
) < strlenW(pAuthInfo
->scheme
)) ||
1093 strncmpiW(pszAuthValue
, pAuthInfo
->scheme
, strlenW(pAuthInfo
->scheme
)))
1095 ERR("authentication scheme changed from %s to %s\n",
1096 debugstr_w(pAuthInfo
->scheme
), debugstr_w(pszAuthValue
));
1100 if (is_basic_auth_value(pszAuthValue
,&szRealm
))
1104 char *auth_data
= NULL
;
1105 UINT auth_data_len
= 0;
1107 TRACE("basic authentication realm %s\n",debugstr_w(szRealm
));
1109 if (!domain_and_username
)
1111 if (host
&& szRealm
)
1112 auth_data_len
= retrieve_cached_basic_authorization(host
, szRealm
,&auth_data
);
1113 if (auth_data_len
== 0)
1121 userlen
= WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, lstrlenW(domain_and_username
), NULL
, 0, NULL
, NULL
);
1122 passlen
= WideCharToMultiByte(CP_UTF8
, 0, password
, lstrlenW(password
), NULL
, 0, NULL
, NULL
);
1124 /* length includes a nul terminator, which will be re-used for the ':' */
1125 auth_data
= heap_alloc(userlen
+ 1 + passlen
);
1132 WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, -1, auth_data
, userlen
, NULL
, NULL
);
1133 auth_data
[userlen
] = ':';
1134 WideCharToMultiByte(CP_UTF8
, 0, password
, -1, &auth_data
[userlen
+1], passlen
, NULL
, NULL
);
1135 auth_data_len
= userlen
+ 1 + passlen
;
1136 if (host
&& szRealm
)
1137 cache_basic_authorization(host
, szRealm
, auth_data
, auth_data_len
);
1140 pAuthInfo
->auth_data
= auth_data
;
1141 pAuthInfo
->auth_data_len
= auth_data_len
;
1142 pAuthInfo
->finished
= TRUE
;
1148 LPCWSTR pszAuthData
;
1149 SecBufferDesc out_desc
, in_desc
;
1151 unsigned char *buffer
;
1152 ULONG context_req
= ISC_REQ_CONNECTION
| ISC_REQ_USE_DCE_STYLE
|
1153 ISC_REQ_MUTUAL_AUTH
| ISC_REQ_DELEGATE
;
1155 in
.BufferType
= SECBUFFER_TOKEN
;
1159 in_desc
.ulVersion
= 0;
1160 in_desc
.cBuffers
= 1;
1161 in_desc
.pBuffers
= &in
;
1163 pszAuthData
= pszAuthValue
+ strlenW(pAuthInfo
->scheme
);
1164 if (*pszAuthData
== ' ')
1167 in
.cbBuffer
= HTTP_DecodeBase64(pszAuthData
, NULL
);
1168 in
.pvBuffer
= heap_alloc(in
.cbBuffer
);
1169 HTTP_DecodeBase64(pszAuthData
, in
.pvBuffer
);
1172 buffer
= heap_alloc(pAuthInfo
->max_token
);
1174 out
.BufferType
= SECBUFFER_TOKEN
;
1175 out
.cbBuffer
= pAuthInfo
->max_token
;
1176 out
.pvBuffer
= buffer
;
1178 out_desc
.ulVersion
= 0;
1179 out_desc
.cBuffers
= 1;
1180 out_desc
.pBuffers
= &out
;
1182 sec_status
= InitializeSecurityContextW(first
? &pAuthInfo
->cred
: NULL
,
1183 first
? NULL
: &pAuthInfo
->ctx
,
1184 first
? request
->server
->name
: NULL
,
1185 context_req
, 0, SECURITY_NETWORK_DREP
,
1186 in
.pvBuffer
? &in_desc
: NULL
,
1187 0, &pAuthInfo
->ctx
, &out_desc
,
1188 &pAuthInfo
->attr
, &pAuthInfo
->exp
);
1189 if (sec_status
== SEC_E_OK
)
1191 pAuthInfo
->finished
= TRUE
;
1192 pAuthInfo
->auth_data
= out
.pvBuffer
;
1193 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
1194 TRACE("sending last auth packet\n");
1196 else if (sec_status
== SEC_I_CONTINUE_NEEDED
)
1198 pAuthInfo
->auth_data
= out
.pvBuffer
;
1199 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
1200 TRACE("sending next auth packet\n");
1204 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status
);
1205 heap_free(out
.pvBuffer
);
1206 destroy_authinfo(pAuthInfo
);
1215 /***********************************************************************
1216 * HTTP_HttpAddRequestHeadersW (internal)
1218 static DWORD
HTTP_HttpAddRequestHeadersW(http_request_t
*request
,
1219 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1224 DWORD len
, res
= ERROR_HTTP_INVALID_HEADER
;
1226 TRACE("copying header: %s\n", debugstr_wn(lpszHeader
, dwHeaderLength
));
1228 if( dwHeaderLength
== ~0U )
1229 len
= strlenW(lpszHeader
);
1231 len
= dwHeaderLength
;
1232 buffer
= heap_alloc(sizeof(WCHAR
)*(len
+1));
1233 lstrcpynW( buffer
, lpszHeader
, len
+ 1);
1239 LPWSTR
* pFieldAndValue
;
1241 lpszEnd
= lpszStart
;
1243 while (*lpszEnd
!= '\0')
1245 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
1250 if (*lpszStart
== '\0')
1253 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
1256 lpszEnd
++; /* Jump over newline */
1258 TRACE("interpreting header %s\n", debugstr_w(lpszStart
));
1259 if (*lpszStart
== '\0')
1261 /* Skip 0-length headers */
1262 lpszStart
= lpszEnd
;
1263 res
= ERROR_SUCCESS
;
1266 pFieldAndValue
= HTTP_InterpretHttpHeader(lpszStart
);
1269 res
= HTTP_VerifyValidHeader(request
, pFieldAndValue
[0]);
1270 if (res
== ERROR_SUCCESS
)
1271 res
= HTTP_ProcessHeader(request
, pFieldAndValue
[0],
1272 pFieldAndValue
[1], dwModifier
| HTTP_ADDHDR_FLAG_REQ
);
1273 HTTP_FreeTokens(pFieldAndValue
);
1276 lpszStart
= lpszEnd
;
1277 } while (res
== ERROR_SUCCESS
);
1283 /***********************************************************************
1284 * HttpAddRequestHeadersW (WININET.@)
1286 * Adds one or more HTTP header to the request handler
1289 * On Windows if dwHeaderLength includes the trailing '\0', then
1290 * HttpAddRequestHeadersW() adds it too. However this results in an
1291 * invalid HTTP header which is rejected by some servers so we probably
1292 * don't need to match Windows on that point.
1299 BOOL WINAPI
HttpAddRequestHeadersW(HINTERNET hHttpRequest
,
1300 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1302 http_request_t
*request
;
1303 DWORD res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
1305 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_wn(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
1310 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
1311 if (request
&& request
->hdr
.htype
== WH_HHTTPREQ
)
1312 res
= HTTP_HttpAddRequestHeadersW( request
, lpszHeader
, dwHeaderLength
, dwModifier
);
1314 WININET_Release( &request
->hdr
);
1316 if(res
!= ERROR_SUCCESS
)
1318 return res
== ERROR_SUCCESS
;
1321 /***********************************************************************
1322 * HttpAddRequestHeadersA (WININET.@)
1324 * Adds one or more HTTP header to the request handler
1331 BOOL WINAPI
HttpAddRequestHeadersA(HINTERNET hHttpRequest
,
1332 LPCSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1338 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_an(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
1340 len
= MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, NULL
, 0 );
1341 hdr
= heap_alloc(len
*sizeof(WCHAR
));
1342 MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, hdr
, len
);
1343 if( dwHeaderLength
!= ~0U )
1344 dwHeaderLength
= len
;
1346 r
= HttpAddRequestHeadersW( hHttpRequest
, hdr
, dwHeaderLength
, dwModifier
);
1352 static void free_accept_types( WCHAR
**accept_types
)
1354 WCHAR
*ptr
, **types
= accept_types
;
1357 while ((ptr
= *types
))
1362 heap_free( accept_types
);
1365 static WCHAR
**convert_accept_types( const char **accept_types
)
1368 const char **types
= accept_types
;
1370 BOOL invalid_pointer
= FALSE
;
1372 if (!types
) return NULL
;
1378 /* find out how many there are */
1379 if (*types
&& **types
)
1381 TRACE("accept type: %s\n", debugstr_a(*types
));
1387 WARN("invalid accept type pointer\n");
1388 invalid_pointer
= TRUE
;
1393 if (invalid_pointer
) return NULL
;
1394 if (!(typesW
= heap_alloc( sizeof(WCHAR
*) * (count
+ 1) ))) return NULL
;
1396 types
= accept_types
;
1399 if (*types
&& **types
) typesW
[count
++] = heap_strdupAtoW( *types
);
1402 typesW
[count
] = NULL
;
1406 /***********************************************************************
1407 * HttpOpenRequestA (WININET.@)
1409 * Open a HTTP request handle
1412 * HINTERNET a HTTP request handle on success
1416 HINTERNET WINAPI
HttpOpenRequestA(HINTERNET hHttpSession
,
1417 LPCSTR lpszVerb
, LPCSTR lpszObjectName
, LPCSTR lpszVersion
,
1418 LPCSTR lpszReferrer
, LPCSTR
*lpszAcceptTypes
,
1419 DWORD dwFlags
, DWORD_PTR dwContext
)
1421 LPWSTR szVerb
= NULL
, szObjectName
= NULL
;
1422 LPWSTR szVersion
= NULL
, szReferrer
= NULL
, *szAcceptTypes
= NULL
;
1423 HINTERNET rc
= FALSE
;
1425 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
1426 debugstr_a(lpszVerb
), debugstr_a(lpszObjectName
),
1427 debugstr_a(lpszVersion
), debugstr_a(lpszReferrer
), lpszAcceptTypes
,
1428 dwFlags
, dwContext
);
1432 szVerb
= heap_strdupAtoW(lpszVerb
);
1439 szObjectName
= heap_strdupAtoW(lpszObjectName
);
1440 if ( !szObjectName
)
1446 szVersion
= heap_strdupAtoW(lpszVersion
);
1453 szReferrer
= heap_strdupAtoW(lpszReferrer
);
1458 szAcceptTypes
= convert_accept_types( lpszAcceptTypes
);
1459 rc
= HttpOpenRequestW(hHttpSession
, szVerb
, szObjectName
, szVersion
, szReferrer
,
1460 (const WCHAR
**)szAcceptTypes
, dwFlags
, dwContext
);
1463 free_accept_types(szAcceptTypes
);
1464 heap_free(szReferrer
);
1465 heap_free(szVersion
);
1466 heap_free(szObjectName
);
1471 /***********************************************************************
1474 static UINT
HTTP_EncodeBase64( LPCSTR bin
, unsigned int len
, LPWSTR base64
)
1477 static const CHAR HTTP_Base64Enc
[] =
1478 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1482 /* first 6 bits, all from bin[0] */
1483 base64
[n
++] = HTTP_Base64Enc
[(bin
[0] & 0xfc) >> 2];
1484 x
= (bin
[0] & 3) << 4;
1486 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1489 base64
[n
++] = HTTP_Base64Enc
[x
];
1494 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[1]&0xf0) >> 4 ) ];
1495 x
= ( bin
[1] & 0x0f ) << 2;
1497 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1500 base64
[n
++] = HTTP_Base64Enc
[x
];
1504 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[2]&0xc0 ) >> 6 ) ];
1506 /* last 6 bits, all from bin [2] */
1507 base64
[n
++] = HTTP_Base64Enc
[ bin
[2] & 0x3f ];
1515 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1516 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1517 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1518 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1519 static const signed char HTTP_Base64Dec
[256] =
1521 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1522 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1523 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1524 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1525 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1526 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1527 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1528 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1529 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1530 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1531 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1532 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1533 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1534 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1535 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1536 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1537 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1538 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1539 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1540 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1541 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1542 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1543 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1544 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1545 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1546 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1550 /***********************************************************************
1553 static UINT
HTTP_DecodeBase64( LPCWSTR base64
, LPSTR bin
)
1561 if (base64
[0] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1562 ((in
[0] = HTTP_Base64Dec
[base64
[0]]) == -1) ||
1563 base64
[1] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1564 ((in
[1] = HTTP_Base64Dec
[base64
[1]]) == -1))
1566 WARN("invalid base64: %s\n", debugstr_w(base64
));
1570 bin
[n
] = (unsigned char) (in
[0] << 2 | in
[1] >> 4);
1573 if ((base64
[2] == '=') && (base64
[3] == '='))
1575 if (base64
[2] > ARRAYSIZE(HTTP_Base64Dec
) ||
1576 ((in
[2] = HTTP_Base64Dec
[base64
[2]]) == -1))
1578 WARN("invalid base64: %s\n", debugstr_w(&base64
[2]));
1582 bin
[n
] = (unsigned char) (in
[1] << 4 | in
[2] >> 2);
1585 if (base64
[3] == '=')
1587 if (base64
[3] > ARRAYSIZE(HTTP_Base64Dec
) ||
1588 ((in
[3] = HTTP_Base64Dec
[base64
[3]]) == -1))
1590 WARN("invalid base64: %s\n", debugstr_w(&base64
[3]));
1594 bin
[n
] = (unsigned char) (((in
[2] << 6) & 0xc0) | in
[3]);
1603 /***********************************************************************
1604 * HTTP_InsertAuthorization
1606 * Insert or delete the authorization field in the request header.
1608 static BOOL
HTTP_InsertAuthorization( http_request_t
*request
, struct HttpAuthInfo
*pAuthInfo
, LPCWSTR header
)
1612 static const WCHAR wszSpace
[] = {' ',0};
1613 static const WCHAR wszBasic
[] = {'B','a','s','i','c',0};
1615 WCHAR
*authorization
= NULL
;
1617 if (pAuthInfo
->auth_data_len
)
1619 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1620 len
= strlenW(pAuthInfo
->scheme
)+1+((pAuthInfo
->auth_data_len
+2)*4)/3;
1621 authorization
= heap_alloc((len
+1)*sizeof(WCHAR
));
1625 strcpyW(authorization
, pAuthInfo
->scheme
);
1626 strcatW(authorization
, wszSpace
);
1627 HTTP_EncodeBase64(pAuthInfo
->auth_data
,
1628 pAuthInfo
->auth_data_len
,
1629 authorization
+strlenW(authorization
));
1631 /* clear the data as it isn't valid now that it has been sent to the
1632 * server, unless it's Basic authentication which doesn't do
1633 * connection tracking */
1634 if (strcmpiW(pAuthInfo
->scheme
, wszBasic
))
1636 heap_free(pAuthInfo
->auth_data
);
1637 pAuthInfo
->auth_data
= NULL
;
1638 pAuthInfo
->auth_data_len
= 0;
1642 TRACE("Inserting authorization: %s\n", debugstr_w(authorization
));
1644 HTTP_ProcessHeader(request
, header
, authorization
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
1645 heap_free(authorization
);
1650 static WCHAR
*HTTP_BuildProxyRequestUrl(http_request_t
*req
)
1652 static const WCHAR slash
[] = { '/',0 };
1653 static const WCHAR format
[] = { 'h','t','t','p',':','/','/','%','s',':','%','u',0 };
1654 static const WCHAR formatSSL
[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','u',0 };
1655 http_session_t
*session
= req
->session
;
1656 WCHAR new_location
[INTERNET_MAX_URL_LENGTH
], *url
;
1659 size
= sizeof(new_location
);
1660 if (HTTP_HttpQueryInfoW(req
, HTTP_QUERY_LOCATION
, new_location
, &size
, NULL
) == ERROR_SUCCESS
)
1662 URL_COMPONENTSW UrlComponents
;
1664 if (!(url
= heap_alloc(size
+ sizeof(WCHAR
)))) return NULL
;
1665 strcpyW( url
, new_location
);
1667 ZeroMemory(&UrlComponents
,sizeof(URL_COMPONENTSW
));
1668 if(InternetCrackUrlW(url
, 0, 0, &UrlComponents
)) goto done
;
1672 size
= 16; /* "https://" + sizeof(port#) + ":/\0" */
1673 size
+= strlenW( session
->hostName
) + strlenW( req
->path
);
1675 if (!(url
= heap_alloc(size
* sizeof(WCHAR
)))) return NULL
;
1677 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1678 sprintfW( url
, formatSSL
, session
->hostName
, session
->hostPort
);
1680 sprintfW( url
, format
, session
->hostName
, session
->hostPort
);
1681 if (req
->path
[0] != '/') strcatW( url
, slash
);
1682 strcatW( url
, req
->path
);
1685 TRACE("url=%s\n", debugstr_w(url
));
1689 /***********************************************************************
1690 * HTTP_DealWithProxy
1692 static BOOL
HTTP_DealWithProxy(appinfo_t
*hIC
, http_session_t
*session
, http_request_t
*request
)
1694 WCHAR buf
[INTERNET_MAX_HOST_NAME_LENGTH
];
1695 WCHAR protoProxy
[INTERNET_MAX_URL_LENGTH
];
1696 DWORD protoProxyLen
= INTERNET_MAX_URL_LENGTH
;
1697 WCHAR proxy
[INTERNET_MAX_URL_LENGTH
];
1698 static WCHAR szNul
[] = { 0 };
1699 URL_COMPONENTSW UrlComponents
;
1700 server_t
*new_server
;
1701 static const WCHAR protoHttp
[] = { 'h','t','t','p',0 };
1702 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/',0 };
1703 static const WCHAR szFormat
[] = { 'h','t','t','p',':','/','/','%','s',0 };
1705 memset( &UrlComponents
, 0, sizeof UrlComponents
);
1706 UrlComponents
.dwStructSize
= sizeof UrlComponents
;
1707 UrlComponents
.lpszHostName
= buf
;
1708 UrlComponents
.dwHostNameLength
= INTERNET_MAX_HOST_NAME_LENGTH
;
1710 if (!INTERNET_FindProxyForProtocol(hIC
->proxy
, protoHttp
, protoProxy
, &protoProxyLen
))
1712 if( CSTR_EQUAL
!= CompareStringW(LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
1713 protoProxy
,strlenW(szHttp
),szHttp
,strlenW(szHttp
)) )
1714 sprintfW(proxy
, szFormat
, protoProxy
);
1716 strcpyW(proxy
, protoProxy
);
1717 if( !InternetCrackUrlW(proxy
, 0, 0, &UrlComponents
) )
1719 if( UrlComponents
.dwHostNameLength
== 0 )
1722 if( !request
->path
)
1723 request
->path
= szNul
;
1725 if(UrlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
1726 UrlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
1728 new_server
= get_server(UrlComponents
.lpszHostName
, UrlComponents
.nPort
, TRUE
);
1732 request
->proxy
= new_server
;
1734 TRACE("proxy server=%s port=%d\n", debugstr_w(new_server
->name
), new_server
->port
);
1738 static DWORD
HTTP_ResolveName(http_request_t
*request
)
1740 server_t
*server
= request
->proxy
? request
->proxy
: request
->server
;
1744 if(server
->addr_len
)
1745 return ERROR_SUCCESS
;
1747 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
1748 INTERNET_STATUS_RESOLVING_NAME
,
1750 (strlenW(server
->name
)+1) * sizeof(WCHAR
));
1752 addr_len
= sizeof(server
->addr
);
1753 if (!GetAddress(server
->name
, server
->port
, (struct sockaddr
*)&server
->addr
, &addr_len
))
1754 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
1756 switch(server
->addr
.ss_family
) {
1758 addr
= &((struct sockaddr_in
*)&server
->addr
)->sin_addr
;
1761 addr
= &((struct sockaddr_in6
*)&server
->addr
)->sin6_addr
;
1764 WARN("unsupported family %d\n", server
->addr
.ss_family
);
1765 return ERROR_INTERNET_NAME_NOT_RESOLVED
;
1768 server
->addr_len
= addr_len
;
1769 inet_ntop(server
->addr
.ss_family
, addr
, server
->addr_str
, sizeof(server
->addr_str
));
1770 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
1771 INTERNET_STATUS_NAME_RESOLVED
,
1772 server
->addr_str
, strlen(server
->addr_str
)+1);
1774 TRACE("resolved %s to %s\n", debugstr_w(server
->name
), server
->addr_str
);
1775 return ERROR_SUCCESS
;
1778 static BOOL
HTTP_GetRequestURL(http_request_t
*req
, LPWSTR buf
)
1780 static const WCHAR http
[] = { 'h','t','t','p',':','/','/',0 };
1781 static const WCHAR https
[] = { 'h','t','t','p','s',':','/','/',0 };
1782 static const WCHAR slash
[] = { '/',0 };
1783 LPHTTPHEADERW host_header
;
1786 host_header
= HTTP_GetHeader(req
, hostW
);
1790 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1794 strcpyW(buf
, scheme
);
1795 strcatW(buf
, host_header
->lpszValue
);
1796 if (req
->path
[0] != '/')
1797 strcatW(buf
, slash
);
1798 strcatW(buf
, req
->path
);
1803 /***********************************************************************
1804 * HTTPREQ_Destroy (internal)
1806 * Deallocate request handle
1809 static void HTTPREQ_Destroy(object_header_t
*hdr
)
1811 http_request_t
*request
= (http_request_t
*) hdr
;
1816 if(request
->hCacheFile
) {
1817 CloseHandle(request
->hCacheFile
);
1818 DeleteFileW(request
->cacheFile
);
1820 heap_free(request
->cacheFile
);
1822 request
->read_section
.DebugInfo
->Spare
[0] = 0;
1823 DeleteCriticalSection( &request
->read_section
);
1824 WININET_Release(&request
->session
->hdr
);
1826 destroy_authinfo(request
->authInfo
);
1827 destroy_authinfo(request
->proxyAuthInfo
);
1830 server_release(request
->server
);
1832 server_release(request
->proxy
);
1834 heap_free(request
->path
);
1835 heap_free(request
->verb
);
1836 heap_free(request
->rawHeaders
);
1837 heap_free(request
->version
);
1838 heap_free(request
->statusText
);
1840 for (i
= 0; i
< request
->nCustHeaders
; i
++)
1842 heap_free(request
->custHeaders
[i
].lpszField
);
1843 heap_free(request
->custHeaders
[i
].lpszValue
);
1845 destroy_data_stream(request
->data_stream
);
1846 heap_free(request
->custHeaders
);
1849 static void http_release_netconn(http_request_t
*req
, BOOL reuse
)
1851 TRACE("%p %p\n",req
, req
->netconn
);
1856 if(reuse
&& req
->netconn
->keep_alive
) {
1859 EnterCriticalSection(&connection_pool_cs
);
1861 list_add_head(&req
->netconn
->server
->conn_pool
, &req
->netconn
->pool_entry
);
1862 req
->netconn
->keep_until
= GetTickCount64() + COLLECT_TIME
;
1863 req
->netconn
= NULL
;
1865 run_collector
= !collector_running
;
1866 collector_running
= TRUE
;
1868 LeaveCriticalSection(&connection_pool_cs
);
1871 HANDLE thread
= NULL
;
1874 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
, (const WCHAR
*)WININET_hModule
, &module
);
1876 thread
= CreateThread(NULL
, 0, collect_connections_proc
, NULL
, 0, NULL
);
1878 EnterCriticalSection(&connection_pool_cs
);
1879 collector_running
= FALSE
;
1880 LeaveCriticalSection(&connection_pool_cs
);
1883 FreeLibrary(module
);
1886 CloseHandle(thread
);
1891 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
1892 INTERNET_STATUS_CLOSING_CONNECTION
, 0, 0);
1894 free_netconn(req
->netconn
);
1895 req
->netconn
= NULL
;
1897 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
1898 INTERNET_STATUS_CONNECTION_CLOSED
, 0, 0);
1901 static BOOL
HTTP_KeepAlive(http_request_t
*request
)
1903 WCHAR szVersion
[10];
1904 WCHAR szConnectionResponse
[20];
1905 DWORD dwBufferSize
= sizeof(szVersion
);
1906 BOOL keepalive
= FALSE
;
1908 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1909 * the connection is keep-alive by default */
1910 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_VERSION
, szVersion
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1911 && !strcmpiW(szVersion
, g_szHttp1_1
))
1916 dwBufferSize
= sizeof(szConnectionResponse
);
1917 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_PROXY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1918 || HTTP_HttpQueryInfoW(request
, HTTP_QUERY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
)
1920 keepalive
= !strcmpiW(szConnectionResponse
, szKeepAlive
);
1926 static void HTTPREQ_CloseConnection(object_header_t
*hdr
)
1928 http_request_t
*req
= (http_request_t
*)hdr
;
1930 http_release_netconn(req
, drain_content(req
, FALSE
));
1933 static DWORD
HTTPREQ_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
1935 http_request_t
*req
= (http_request_t
*)hdr
;
1938 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO
:
1940 http_session_t
*session
= req
->session
;
1941 INTERNET_DIAGNOSTIC_SOCKET_INFO
*info
= buffer
;
1943 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1945 if (*size
< sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
))
1946 return ERROR_INSUFFICIENT_BUFFER
;
1947 *size
= sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
);
1948 /* FIXME: can't get a SOCKET from our connection since we don't use
1952 /* FIXME: get source port from req->netConnection */
1953 info
->SourcePort
= 0;
1954 info
->DestPort
= session
->hostPort
;
1956 if (HTTP_KeepAlive(req
))
1957 info
->Flags
|= IDSI_FLAG_KEEP_ALIVE
;
1958 if (session
->appInfo
->proxy
&& session
->appInfo
->proxy
[0] != 0)
1959 info
->Flags
|= IDSI_FLAG_PROXY
;
1960 if (req
->netconn
->useSSL
)
1961 info
->Flags
|= IDSI_FLAG_SECURE
;
1963 return ERROR_SUCCESS
;
1967 TRACE("Queried undocumented option 98, forwarding to INTERNET_OPTION_SECURITY_FLAGS\n");
1969 case INTERNET_OPTION_SECURITY_FLAGS
:
1973 if (*size
< sizeof(ULONG
))
1974 return ERROR_INSUFFICIENT_BUFFER
;
1976 *size
= sizeof(DWORD
);
1977 flags
= req
->netconn
? req
->netconn
->security_flags
: req
->security_flags
| req
->server
->security_flags
;
1978 *(DWORD
*)buffer
= flags
;
1980 TRACE("INTERNET_OPTION_SECURITY_FLAGS %x\n", flags
);
1981 return ERROR_SUCCESS
;
1984 case INTERNET_OPTION_HANDLE_TYPE
:
1985 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1987 if (*size
< sizeof(ULONG
))
1988 return ERROR_INSUFFICIENT_BUFFER
;
1990 *size
= sizeof(DWORD
);
1991 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_HTTP_REQUEST
;
1992 return ERROR_SUCCESS
;
1994 case INTERNET_OPTION_URL
: {
1995 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
2000 static const WCHAR httpW
[] = {'h','t','t','p',':','/','/',0};
2002 TRACE("INTERNET_OPTION_URL\n");
2004 host
= HTTP_GetHeader(req
, hostW
);
2005 strcpyW(url
, httpW
);
2006 strcatW(url
, host
->lpszValue
);
2007 if (NULL
!= (pch
= strchrW(url
+ strlenW(httpW
), ':')))
2009 strcatW(url
, req
->path
);
2011 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url
));
2014 len
= (strlenW(url
)+1) * sizeof(WCHAR
);
2016 return ERROR_INSUFFICIENT_BUFFER
;
2019 strcpyW(buffer
, url
);
2020 return ERROR_SUCCESS
;
2022 len
= WideCharToMultiByte(CP_ACP
, 0, url
, -1, buffer
, *size
, NULL
, NULL
);
2024 return ERROR_INSUFFICIENT_BUFFER
;
2027 return ERROR_SUCCESS
;
2031 case INTERNET_OPTION_CACHE_TIMESTAMPS
: {
2032 INTERNET_CACHE_ENTRY_INFOW
*info
;
2033 INTERNET_CACHE_TIMESTAMPS
*ts
= buffer
;
2034 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
2035 DWORD nbytes
, error
;
2038 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2040 if (*size
< sizeof(*ts
))
2042 *size
= sizeof(*ts
);
2043 return ERROR_INSUFFICIENT_BUFFER
;
2046 HTTP_GetRequestURL(req
, url
);
2047 ret
= GetUrlCacheEntryInfoW(url
, NULL
, &nbytes
);
2048 error
= GetLastError();
2049 if (!ret
&& error
== ERROR_INSUFFICIENT_BUFFER
)
2051 if (!(info
= heap_alloc(nbytes
)))
2052 return ERROR_OUTOFMEMORY
;
2054 GetUrlCacheEntryInfoW(url
, info
, &nbytes
);
2056 ts
->ftExpires
= info
->ExpireTime
;
2057 ts
->ftLastModified
= info
->LastModifiedTime
;
2060 *size
= sizeof(*ts
);
2061 return ERROR_SUCCESS
;
2066 case INTERNET_OPTION_DATAFILE_NAME
: {
2069 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2071 if(!req
->cacheFile
) {
2073 return ERROR_INTERNET_ITEM_NOT_FOUND
;
2077 req_size
= (lstrlenW(req
->cacheFile
)+1) * sizeof(WCHAR
);
2078 if(*size
< req_size
)
2079 return ERROR_INSUFFICIENT_BUFFER
;
2082 memcpy(buffer
, req
->cacheFile
, *size
);
2083 return ERROR_SUCCESS
;
2085 req_size
= WideCharToMultiByte(CP_ACP
, 0, req
->cacheFile
, -1, NULL
, 0, NULL
, NULL
);
2086 if (req_size
> *size
)
2087 return ERROR_INSUFFICIENT_BUFFER
;
2089 *size
= WideCharToMultiByte(CP_ACP
, 0, req
->cacheFile
,
2090 -1, buffer
, *size
, NULL
, NULL
);
2091 return ERROR_SUCCESS
;
2095 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT
: {
2096 PCCERT_CONTEXT context
;
2098 if(*size
< sizeof(INTERNET_CERTIFICATE_INFOA
)) {
2099 *size
= sizeof(INTERNET_CERTIFICATE_INFOA
);
2100 return ERROR_INSUFFICIENT_BUFFER
;
2103 context
= (PCCERT_CONTEXT
)NETCON_GetCert(req
->netconn
);
2105 INTERNET_CERTIFICATE_INFOA
*info
= (INTERNET_CERTIFICATE_INFOA
*)buffer
;
2108 memset(info
, 0, sizeof(*info
));
2109 info
->ftExpiry
= context
->pCertInfo
->NotAfter
;
2110 info
->ftStart
= context
->pCertInfo
->NotBefore
;
2111 len
= CertNameToStrA(context
->dwCertEncodingType
,
2112 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
|CERT_NAME_STR_CRLF_FLAG
, NULL
, 0);
2113 info
->lpszSubjectInfo
= LocalAlloc(0, len
);
2114 if(info
->lpszSubjectInfo
)
2115 CertNameToStrA(context
->dwCertEncodingType
,
2116 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
|CERT_NAME_STR_CRLF_FLAG
,
2117 info
->lpszSubjectInfo
, len
);
2118 len
= CertNameToStrA(context
->dwCertEncodingType
,
2119 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
|CERT_NAME_STR_CRLF_FLAG
, NULL
, 0);
2120 info
->lpszIssuerInfo
= LocalAlloc(0, len
);
2121 if(info
->lpszIssuerInfo
)
2122 CertNameToStrA(context
->dwCertEncodingType
,
2123 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
|CERT_NAME_STR_CRLF_FLAG
,
2124 info
->lpszIssuerInfo
, len
);
2125 info
->dwKeySize
= NETCON_GetCipherStrength(req
->netconn
);
2126 CertFreeCertificateContext(context
);
2127 return ERROR_SUCCESS
;
2129 return ERROR_NOT_SUPPORTED
;
2131 case INTERNET_OPTION_CONNECT_TIMEOUT
:
2132 if (*size
< sizeof(DWORD
))
2133 return ERROR_INSUFFICIENT_BUFFER
;
2135 *size
= sizeof(DWORD
);
2136 *(DWORD
*)buffer
= req
->connect_timeout
;
2137 return ERROR_SUCCESS
;
2138 case INTERNET_OPTION_REQUEST_FLAGS
: {
2141 if (*size
< sizeof(DWORD
))
2142 return ERROR_INSUFFICIENT_BUFFER
;
2144 /* FIXME: Add support for:
2145 * INTERNET_REQFLAG_FROM_CACHE
2146 * INTERNET_REQFLAG_CACHE_WRITE_DISABLED
2149 if(req
->session
->appInfo
->proxy
)
2150 flags
|= INTERNET_REQFLAG_VIA_PROXY
;
2151 if(!req
->rawHeaders
)
2152 flags
|= INTERNET_REQFLAG_NO_HEADERS
;
2154 TRACE("INTERNET_OPTION_REQUEST_FLAGS returning %x\n", flags
);
2156 *size
= sizeof(DWORD
);
2157 *(DWORD
*)buffer
= flags
;
2158 return ERROR_SUCCESS
;
2162 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
2165 static DWORD
HTTPREQ_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
2167 http_request_t
*req
= (http_request_t
*)hdr
;
2170 case 99: /* Undocumented, seems to be INTERNET_OPTION_SECURITY_FLAGS with argument validation */
2171 TRACE("Undocumented option 99\n");
2173 if (!buffer
|| size
!= sizeof(DWORD
))
2174 return ERROR_INVALID_PARAMETER
;
2175 if(*(DWORD
*)buffer
& ~SECURITY_SET_MASK
)
2176 return ERROR_INTERNET_OPTION_NOT_SETTABLE
;
2179 case INTERNET_OPTION_SECURITY_FLAGS
:
2183 if (!buffer
|| size
!= sizeof(DWORD
))
2184 return ERROR_INVALID_PARAMETER
;
2185 flags
= *(DWORD
*)buffer
;
2186 TRACE("INTERNET_OPTION_SECURITY_FLAGS %08x\n", flags
);
2187 flags
&= SECURITY_SET_MASK
;
2188 req
->security_flags
|= flags
;
2190 req
->netconn
->security_flags
|= flags
;
2191 return ERROR_SUCCESS
;
2193 case INTERNET_OPTION_CONNECT_TIMEOUT
:
2194 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
2195 req
->connect_timeout
= *(DWORD
*)buffer
;
2196 return ERROR_SUCCESS
;
2198 case INTERNET_OPTION_SEND_TIMEOUT
:
2199 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
2200 req
->send_timeout
= *(DWORD
*)buffer
;
2201 return ERROR_SUCCESS
;
2203 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
2204 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
2205 req
->receive_timeout
= *(DWORD
*)buffer
;
2206 return ERROR_SUCCESS
;
2208 case INTERNET_OPTION_USERNAME
:
2209 heap_free(req
->session
->userName
);
2210 if (!(req
->session
->userName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
2211 return ERROR_SUCCESS
;
2213 case INTERNET_OPTION_PASSWORD
:
2214 heap_free(req
->session
->password
);
2215 if (!(req
->session
->password
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
2216 return ERROR_SUCCESS
;
2217 case INTERNET_OPTION_HTTP_DECODING
:
2218 if(size
!= sizeof(BOOL
))
2219 return ERROR_INVALID_PARAMETER
;
2220 req
->decoding
= *(BOOL
*)buffer
;
2221 return ERROR_SUCCESS
;
2224 return INET_SetOption(hdr
, option
, buffer
, size
);
2227 static void commit_cache_entry(http_request_t
*req
)
2229 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
2233 CloseHandle(req
->hCacheFile
);
2234 req
->hCacheFile
= NULL
;
2236 if(HTTP_GetRequestURL(req
, url
)) {
2239 headersLen
= req
->rawHeaders
? strlenW(req
->rawHeaders
) : 0;
2240 CommitUrlCacheEntryW(url
, req
->cacheFile
, req
->expires
,
2241 req
->last_modified
, NORMAL_CACHE_ENTRY
,
2242 req
->rawHeaders
, headersLen
, NULL
, 0);
2246 static void create_cache_entry(http_request_t
*req
)
2248 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
2249 WCHAR file_name
[MAX_PATH
+1];
2252 /* FIXME: We should free previous cache file earlier */
2253 heap_free(req
->cacheFile
);
2254 CloseHandle(req
->hCacheFile
);
2255 req
->hCacheFile
= NULL
;
2257 b
= HTTP_GetRequestURL(req
, url
);
2259 WARN("Could not get URL\n");
2263 b
= CreateUrlCacheEntryW(url
, req
->contentLength
, NULL
, file_name
, 0);
2265 WARN("Could not create cache entry: %08x\n", GetLastError());
2269 req
->cacheFile
= heap_strdupW(file_name
);
2270 req
->hCacheFile
= CreateFileW(req
->cacheFile
, GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
2271 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
2272 if(req
->hCacheFile
== INVALID_HANDLE_VALUE
) {
2273 WARN("Could not create file: %u\n", GetLastError());
2274 req
->hCacheFile
= NULL
;
2278 if(req
->read_size
) {
2281 b
= WriteFile(req
->hCacheFile
, req
->read_buf
+req
->read_pos
, req
->read_size
, &written
, NULL
);
2283 FIXME("WriteFile failed: %u\n", GetLastError());
2285 if(req
->data_stream
->vtbl
->end_of_data(req
->data_stream
, req
))
2286 commit_cache_entry(req
);
2290 /* read some more data into the read buffer (the read section must be held) */
2291 static DWORD
read_more_data( http_request_t
*req
, int maxlen
)
2298 /* move existing data to the start of the buffer */
2300 memmove( req
->read_buf
, req
->read_buf
+ req
->read_pos
, req
->read_size
);
2304 if (maxlen
== -1) maxlen
= sizeof(req
->read_buf
);
2306 res
= NETCON_recv( req
->netconn
, req
->read_buf
+ req
->read_size
,
2307 maxlen
- req
->read_size
, 0, &len
);
2308 if(res
== ERROR_SUCCESS
)
2309 req
->read_size
+= len
;
2314 /* remove some amount of data from the read buffer (the read section must be held) */
2315 static void remove_data( http_request_t
*req
, int count
)
2317 if (!(req
->read_size
-= count
)) req
->read_pos
= 0;
2318 else req
->read_pos
+= count
;
2321 static BOOL
read_line( http_request_t
*req
, LPSTR buffer
, DWORD
*len
)
2323 int count
, bytes_read
, pos
= 0;
2326 EnterCriticalSection( &req
->read_section
);
2329 BYTE
*eol
= memchr( req
->read_buf
+ req
->read_pos
, '\n', req
->read_size
);
2333 count
= eol
- (req
->read_buf
+ req
->read_pos
);
2334 bytes_read
= count
+ 1;
2336 else count
= bytes_read
= req
->read_size
;
2338 count
= min( count
, *len
- pos
);
2339 memcpy( buffer
+ pos
, req
->read_buf
+ req
->read_pos
, count
);
2341 remove_data( req
, bytes_read
);
2344 if ((res
= read_more_data( req
, -1 )) != ERROR_SUCCESS
|| !req
->read_size
)
2347 TRACE( "returning empty string %u\n", res
);
2348 LeaveCriticalSection( &req
->read_section
);
2349 INTERNET_SetLastError(res
);
2353 LeaveCriticalSection( &req
->read_section
);
2357 if (pos
&& buffer
[pos
- 1] == '\r') pos
--;
2360 buffer
[*len
- 1] = 0;
2361 TRACE( "returning %s\n", debugstr_a(buffer
));
2365 /* check if we have reached the end of the data to read (the read section must be held) */
2366 static BOOL
end_of_read_data( http_request_t
*req
)
2368 return !req
->read_size
&& req
->data_stream
->vtbl
->end_of_data(req
->data_stream
, req
);
2371 static DWORD
read_http_stream(http_request_t
*req
, BYTE
*buf
, DWORD size
, DWORD
*read
, read_mode_t read_mode
)
2375 res
= req
->data_stream
->vtbl
->read(req
->data_stream
, req
, buf
, size
, read
, read_mode
);
2376 assert(*read
<= size
);
2378 if(req
->hCacheFile
) {
2383 bres
= WriteFile(req
->hCacheFile
, buf
, *read
, &written
, NULL
);
2385 FIXME("WriteFile failed: %u\n", GetLastError());
2388 if(req
->data_stream
->vtbl
->end_of_data(req
->data_stream
, req
))
2389 commit_cache_entry(req
);
2395 /* fetch some more data into the read buffer (the read section must be held) */
2396 static DWORD
refill_read_buffer(http_request_t
*req
, read_mode_t read_mode
, DWORD
*read_bytes
)
2400 if(req
->read_size
== sizeof(req
->read_buf
))
2401 return ERROR_SUCCESS
;
2405 memmove(req
->read_buf
, req
->read_buf
+req
->read_pos
, req
->read_size
);
2409 res
= read_http_stream(req
, req
->read_buf
+req
->read_size
, sizeof(req
->read_buf
) - req
->read_size
,
2411 req
->read_size
+= read
;
2413 TRACE("read %u bytes, read_size %u\n", read
, req
->read_size
);
2419 /* return the size of data available to be read immediately (the read section must be held) */
2420 static DWORD
get_avail_data( http_request_t
*req
)
2422 return req
->read_size
+ req
->data_stream
->vtbl
->get_avail_data(req
->data_stream
, req
);
2425 static DWORD
netconn_get_avail_data(data_stream_t
*stream
, http_request_t
*req
)
2427 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2431 NETCON_query_data_available(req
->netconn
, &avail
);
2432 return netconn_stream
->content_length
== ~0u
2434 : min(avail
, netconn_stream
->content_length
-netconn_stream
->content_read
);
2437 static BOOL
netconn_end_of_data(data_stream_t
*stream
, http_request_t
*req
)
2439 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2440 return netconn_stream
->content_read
== netconn_stream
->content_length
|| !req
->netconn
;
2443 static DWORD
netconn_read(data_stream_t
*stream
, http_request_t
*req
, BYTE
*buf
, DWORD size
,
2444 DWORD
*read
, read_mode_t read_mode
)
2446 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2449 size
= min(size
, netconn_stream
->content_length
-netconn_stream
->content_read
);
2451 if(read_mode
== READMODE_NOBLOCK
) {
2452 DWORD avail
= netconn_get_avail_data(stream
, req
);
2457 if(size
&& req
->netconn
) {
2458 if(NETCON_recv(req
->netconn
, buf
, size
, read_mode
== READMODE_SYNC
? MSG_WAITALL
: 0, &len
) != ERROR_SUCCESS
)
2461 netconn_stream
->content_length
= netconn_stream
->content_read
;
2464 netconn_stream
->content_read
+= *read
= len
;
2465 TRACE("read %u bytes\n", len
);
2466 return ERROR_SUCCESS
;
2469 static BOOL
netconn_drain_content(data_stream_t
*stream
, http_request_t
*req
)
2471 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2476 if(netconn_end_of_data(stream
, req
))
2480 avail
= netconn_get_avail_data(stream
, req
);
2484 if(NETCON_recv(req
->netconn
, buf
, min(avail
, sizeof(buf
)), 0, &len
) != ERROR_SUCCESS
)
2487 netconn_stream
->content_read
+= len
;
2488 }while(netconn_stream
->content_read
< netconn_stream
->content_length
);
2493 static void netconn_destroy(data_stream_t
*stream
)
2497 static const data_stream_vtbl_t netconn_stream_vtbl
= {
2498 netconn_get_avail_data
,
2499 netconn_end_of_data
,
2501 netconn_drain_content
,
2505 /* read some more data into the read buffer (the read section must be held) */
2506 static DWORD
read_more_chunked_data(chunked_stream_t
*stream
, http_request_t
*req
, int maxlen
)
2511 if (stream
->buf_pos
)
2513 /* move existing data to the start of the buffer */
2514 if(stream
->buf_size
)
2515 memmove(stream
->buf
, stream
->buf
+ stream
->buf_pos
, stream
->buf_size
);
2516 stream
->buf_pos
= 0;
2519 if (maxlen
== -1) maxlen
= sizeof(stream
->buf
);
2521 res
= NETCON_recv( req
->netconn
, stream
->buf
+ stream
->buf_size
,
2522 maxlen
- stream
->buf_size
, 0, &len
);
2523 if(res
== ERROR_SUCCESS
)
2524 stream
->buf_size
+= len
;
2529 /* remove some amount of data from the read buffer (the read section must be held) */
2530 static void remove_chunked_data(chunked_stream_t
*stream
, int count
)
2532 if (!(stream
->buf_size
-= count
)) stream
->buf_pos
= 0;
2533 else stream
->buf_pos
+= count
;
2536 /* discard data contents until we reach end of line (the read section must be held) */
2537 static DWORD
discard_chunked_eol(chunked_stream_t
*stream
, http_request_t
*req
)
2543 BYTE
*eol
= memchr(stream
->buf
+ stream
->buf_pos
, '\n', stream
->buf_size
);
2546 remove_chunked_data(stream
, (eol
+ 1) - (stream
->buf
+ stream
->buf_pos
));
2549 stream
->buf_pos
= stream
->buf_size
= 0; /* discard everything */
2550 if ((res
= read_more_chunked_data(stream
, req
, -1)) != ERROR_SUCCESS
) return res
;
2551 } while (stream
->buf_size
);
2552 return ERROR_SUCCESS
;
2555 /* read the size of the next chunk (the read section must be held) */
2556 static DWORD
start_next_chunk(chunked_stream_t
*stream
, http_request_t
*req
)
2559 DWORD chunk_size
= 0, res
;
2561 if(stream
->chunk_size
!= ~0u && (res
= discard_chunked_eol(stream
, req
)) != ERROR_SUCCESS
)
2566 while (stream
->buf_size
)
2568 char ch
= stream
->buf
[stream
->buf_pos
];
2569 if (ch
>= '0' && ch
<= '9') chunk_size
= chunk_size
* 16 + ch
- '0';
2570 else if (ch
>= 'a' && ch
<= 'f') chunk_size
= chunk_size
* 16 + ch
- 'a' + 10;
2571 else if (ch
>= 'A' && ch
<= 'F') chunk_size
= chunk_size
* 16 + ch
- 'A' + 10;
2572 else if (ch
== ';' || ch
== '\r' || ch
== '\n')
2574 TRACE( "reading %u byte chunk\n", chunk_size
);
2575 stream
->chunk_size
= chunk_size
;
2576 req
->contentLength
+= chunk_size
;
2577 return discard_chunked_eol(stream
, req
);
2579 remove_chunked_data(stream
, 1);
2581 if ((res
= read_more_chunked_data(stream
, req
, -1)) != ERROR_SUCCESS
) return res
;
2582 if (!stream
->buf_size
)
2584 stream
->chunk_size
= 0;
2585 return ERROR_SUCCESS
;
2590 static DWORD
chunked_get_avail_data(data_stream_t
*stream
, http_request_t
*req
)
2592 /* Allow reading only from read buffer */
2596 static BOOL
chunked_end_of_data(data_stream_t
*stream
, http_request_t
*req
)
2598 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2599 return !chunked_stream
->chunk_size
;
2602 static DWORD
chunked_read(data_stream_t
*stream
, http_request_t
*req
, BYTE
*buf
, DWORD size
,
2603 DWORD
*read
, read_mode_t read_mode
)
2605 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2606 DWORD read_bytes
= 0, ret_read
= 0, res
= ERROR_SUCCESS
;
2608 if(chunked_stream
->chunk_size
== ~0u) {
2609 res
= start_next_chunk(chunked_stream
, req
);
2610 if(res
!= ERROR_SUCCESS
)
2614 while(size
&& chunked_stream
->chunk_size
) {
2615 if(chunked_stream
->buf_size
) {
2616 read_bytes
= min(size
, min(chunked_stream
->buf_size
, chunked_stream
->chunk_size
));
2618 /* this could block */
2619 if(read_mode
== READMODE_NOBLOCK
&& read_bytes
== chunked_stream
->chunk_size
)
2622 memcpy(buf
+ret_read
, chunked_stream
->buf
+chunked_stream
->buf_pos
, read_bytes
);
2623 remove_chunked_data(chunked_stream
, read_bytes
);
2625 read_bytes
= min(size
, chunked_stream
->chunk_size
);
2627 if(read_mode
== READMODE_NOBLOCK
) {
2630 if(!NETCON_query_data_available(req
->netconn
, &avail
) || !avail
)
2632 if(read_bytes
> avail
)
2635 /* this could block */
2636 if(read_bytes
== chunked_stream
->chunk_size
)
2640 res
= NETCON_recv(req
->netconn
, (char *)buf
+ret_read
, read_bytes
, 0, (int*)&read_bytes
);
2641 if(res
!= ERROR_SUCCESS
)
2645 chunked_stream
->chunk_size
-= read_bytes
;
2647 ret_read
+= read_bytes
;
2648 if(!chunked_stream
->chunk_size
) {
2649 assert(read_mode
!= READMODE_NOBLOCK
);
2650 res
= start_next_chunk(chunked_stream
, req
);
2651 if(res
!= ERROR_SUCCESS
)
2655 if(read_mode
== READMODE_ASYNC
)
2656 read_mode
= READMODE_NOBLOCK
;
2659 TRACE("read %u bytes\n", ret_read
);
2664 static BOOL
chunked_drain_content(data_stream_t
*stream
, http_request_t
*req
)
2666 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2668 /* FIXME: we can do better */
2669 return !chunked_stream
->chunk_size
;
2672 static void chunked_destroy(data_stream_t
*stream
)
2674 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2675 heap_free(chunked_stream
);
2678 static const data_stream_vtbl_t chunked_stream_vtbl
= {
2679 chunked_get_avail_data
,
2680 chunked_end_of_data
,
2682 chunked_drain_content
,
2686 /* set the request content length based on the headers */
2687 static DWORD
set_content_length(http_request_t
*request
)
2689 static const WCHAR szChunked
[] = {'c','h','u','n','k','e','d',0};
2693 if(request
->status_code
== HTTP_STATUS_NO_CONTENT
) {
2694 request
->contentLength
= request
->netconn_stream
.content_length
= 0;
2695 return ERROR_SUCCESS
;
2698 size
= sizeof(request
->contentLength
);
2699 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_CONTENT_LENGTH
,
2700 &request
->contentLength
, &size
, NULL
) != ERROR_SUCCESS
)
2701 request
->contentLength
= ~0u;
2702 request
->netconn_stream
.content_length
= request
->contentLength
;
2703 request
->netconn_stream
.content_read
= request
->read_size
;
2705 size
= sizeof(encoding
);
2706 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_TRANSFER_ENCODING
, encoding
, &size
, NULL
) == ERROR_SUCCESS
&&
2707 !strcmpiW(encoding
, szChunked
))
2709 chunked_stream_t
*chunked_stream
;
2711 chunked_stream
= heap_alloc(sizeof(*chunked_stream
));
2713 return ERROR_OUTOFMEMORY
;
2715 chunked_stream
->data_stream
.vtbl
= &chunked_stream_vtbl
;
2716 chunked_stream
->buf_size
= chunked_stream
->buf_pos
= 0;
2717 chunked_stream
->chunk_size
= ~0u;
2719 if(request
->read_size
) {
2720 memcpy(chunked_stream
->buf
, request
->read_buf
+request
->read_pos
, request
->read_size
);
2721 chunked_stream
->buf_size
= request
->read_size
;
2722 request
->read_size
= request
->read_pos
= 0;
2725 request
->data_stream
= &chunked_stream
->data_stream
;
2726 request
->contentLength
= ~0u;
2727 request
->read_chunked
= TRUE
;
2730 if(request
->decoding
) {
2733 static const WCHAR gzipW
[] = {'g','z','i','p',0};
2735 encoding_idx
= HTTP_GetCustomHeaderIndex(request
, szContent_Encoding
, 0, FALSE
);
2736 if(encoding_idx
!= -1 && !strcmpiW(request
->custHeaders
[encoding_idx
].lpszValue
, gzipW
))
2737 return init_gzip_stream(request
);
2740 return ERROR_SUCCESS
;
2743 static void send_request_complete(http_request_t
*req
, DWORD_PTR result
, DWORD error
)
2745 INTERNET_ASYNC_RESULT iar
;
2747 iar
.dwResult
= result
;
2748 iar
.dwError
= error
;
2750 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2751 sizeof(INTERNET_ASYNC_RESULT
));
2754 static void HTTP_ReceiveRequestData(http_request_t
*req
, BOOL first_notif
)
2756 DWORD res
, read
= 0, avail
= 0;
2761 EnterCriticalSection( &req
->read_section
);
2763 mode
= first_notif
&& req
->read_size
? READMODE_NOBLOCK
: READMODE_ASYNC
;
2764 res
= refill_read_buffer(req
, mode
, &read
);
2765 if(res
== ERROR_SUCCESS
&& !first_notif
)
2766 avail
= get_avail_data(req
);
2768 LeaveCriticalSection( &req
->read_section
);
2770 if(res
!= ERROR_SUCCESS
|| (mode
!= READMODE_NOBLOCK
&& !read
)) {
2771 WARN("res %u read %u, closing connection\n", res
, read
);
2772 http_release_netconn(req
, FALSE
);
2775 if(res
== ERROR_SUCCESS
)
2776 send_request_complete(req
, req
->session
->hdr
.dwInternalFlags
& INET_OPENURL
? (DWORD_PTR
)req
->hdr
.hInternet
: 1, avail
);
2778 send_request_complete(req
, 0, res
);
2781 /* read data from the http connection (the read section must be held) */
2782 static DWORD
HTTPREQ_Read(http_request_t
*req
, void *buffer
, DWORD size
, DWORD
*read
, BOOL sync
)
2784 DWORD current_read
= 0, ret_read
= 0;
2785 read_mode_t read_mode
;
2786 DWORD res
= ERROR_SUCCESS
;
2788 read_mode
= req
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
? READMODE_ASYNC
: READMODE_SYNC
;
2790 EnterCriticalSection( &req
->read_section
);
2792 if(req
->read_size
) {
2793 ret_read
= min(size
, req
->read_size
);
2794 memcpy(buffer
, req
->read_buf
+req
->read_pos
, ret_read
);
2795 req
->read_size
-= ret_read
;
2796 req
->read_pos
+= ret_read
;
2797 if(read_mode
== READMODE_ASYNC
)
2798 read_mode
= READMODE_NOBLOCK
;
2801 if(ret_read
< size
) {
2802 res
= read_http_stream(req
, (BYTE
*)buffer
+ret_read
, size
-ret_read
, ¤t_read
, read_mode
);
2803 ret_read
+= current_read
;
2806 LeaveCriticalSection( &req
->read_section
);
2809 TRACE( "retrieved %u bytes (%u)\n", ret_read
, req
->contentLength
);
2811 if(size
&& !ret_read
)
2812 http_release_netconn(req
, res
== ERROR_SUCCESS
);
2817 static BOOL
drain_content(http_request_t
*req
, BOOL blocking
)
2821 if(!req
->netconn
|| req
->contentLength
== -1)
2824 if(!strcmpW(req
->verb
, szHEAD
))
2828 return req
->data_stream
->vtbl
->drain_content(req
->data_stream
, req
);
2830 EnterCriticalSection( &req
->read_section
);
2833 DWORD bytes_read
, res
;
2836 res
= HTTPREQ_Read(req
, buf
, sizeof(buf
), &bytes_read
, TRUE
);
2837 if(res
!= ERROR_SUCCESS
) {
2847 LeaveCriticalSection( &req
->read_section
);
2851 static DWORD
HTTPREQ_ReadFile(object_header_t
*hdr
, void *buffer
, DWORD size
, DWORD
*read
)
2853 http_request_t
*req
= (http_request_t
*)hdr
;
2856 EnterCriticalSection( &req
->read_section
);
2857 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2858 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2860 res
= HTTPREQ_Read(req
, buffer
, size
, read
, TRUE
);
2861 if(res
== ERROR_SUCCESS
)
2863 LeaveCriticalSection( &req
->read_section
);
2868 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST
*workRequest
)
2870 struct WORKREQ_INTERNETREADFILEEXA
const *data
= &workRequest
->u
.InternetReadFileExA
;
2871 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2874 TRACE("INTERNETREADFILEEXA %p\n", workRequest
->hdr
);
2876 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2877 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2879 send_request_complete(req
, res
== ERROR_SUCCESS
, res
);
2882 static DWORD
HTTPREQ_ReadFileExA(object_header_t
*hdr
, INTERNET_BUFFERSA
*buffers
,
2883 DWORD flags
, DWORD_PTR context
)
2885 http_request_t
*req
= (http_request_t
*)hdr
;
2886 DWORD res
, size
, read
, error
= ERROR_SUCCESS
;
2888 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2889 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2891 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2892 return ERROR_INVALID_PARAMETER
;
2894 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2896 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2898 WORKREQUEST workRequest
;
2900 if (TryEnterCriticalSection( &req
->read_section
))
2902 if (get_avail_data(req
))
2904 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2905 &buffers
->dwBufferLength
, FALSE
);
2906 size
= buffers
->dwBufferLength
;
2907 LeaveCriticalSection( &req
->read_section
);
2910 LeaveCriticalSection( &req
->read_section
);
2913 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExAProc
;
2914 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2915 workRequest
.u
.InternetReadFileExA
.lpBuffersOut
= buffers
;
2917 INTERNET_AsyncCall(&workRequest
);
2919 return ERROR_IO_PENDING
;
2923 size
= buffers
->dwBufferLength
;
2925 EnterCriticalSection( &req
->read_section
);
2926 if(hdr
->dwError
== ERROR_SUCCESS
)
2927 hdr
->dwError
= INTERNET_HANDLE_IN_USE
;
2928 else if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2929 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2932 res
= HTTPREQ_Read(req
, (char*)buffers
->lpvBuffer
+read
, size
-read
,
2933 &buffers
->dwBufferLength
, !(flags
& IRF_NO_WAIT
));
2934 if(res
!= ERROR_SUCCESS
)
2937 read
+= buffers
->dwBufferLength
;
2938 if(read
== size
|| end_of_read_data(req
))
2941 LeaveCriticalSection( &req
->read_section
);
2943 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2944 &buffers
->dwBufferLength
, sizeof(buffers
->dwBufferLength
));
2945 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2946 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2948 EnterCriticalSection( &req
->read_section
);
2951 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2952 hdr
->dwError
= ERROR_SUCCESS
;
2954 error
= hdr
->dwError
;
2956 LeaveCriticalSection( &req
->read_section
);
2957 size
= buffers
->dwBufferLength
;
2958 buffers
->dwBufferLength
= read
;
2961 if (res
== ERROR_SUCCESS
) {
2962 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2963 &size
, sizeof(size
));
2966 return res
==ERROR_SUCCESS
? error
: res
;
2969 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST
*workRequest
)
2971 struct WORKREQ_INTERNETREADFILEEXW
const *data
= &workRequest
->u
.InternetReadFileExW
;
2972 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2975 TRACE("INTERNETREADFILEEXW %p\n", workRequest
->hdr
);
2977 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2978 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2980 send_request_complete(req
, res
== ERROR_SUCCESS
, res
);
2983 static DWORD
HTTPREQ_ReadFileExW(object_header_t
*hdr
, INTERNET_BUFFERSW
*buffers
,
2984 DWORD flags
, DWORD_PTR context
)
2987 http_request_t
*req
= (http_request_t
*)hdr
;
2988 DWORD res
, size
, read
, error
= ERROR_SUCCESS
;
2990 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2991 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2993 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2994 return ERROR_INVALID_PARAMETER
;
2996 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2998 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
3000 WORKREQUEST workRequest
;
3002 if (TryEnterCriticalSection( &req
->read_section
))
3004 if (get_avail_data(req
))
3006 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
3007 &buffers
->dwBufferLength
, FALSE
);
3008 size
= buffers
->dwBufferLength
;
3009 LeaveCriticalSection( &req
->read_section
);
3012 LeaveCriticalSection( &req
->read_section
);
3015 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExWProc
;
3016 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
3017 workRequest
.u
.InternetReadFileExW
.lpBuffersOut
= buffers
;
3019 INTERNET_AsyncCall(&workRequest
);
3021 return ERROR_IO_PENDING
;
3025 size
= buffers
->dwBufferLength
;
3027 EnterCriticalSection( &req
->read_section
);
3028 if(hdr
->dwError
== ERROR_SUCCESS
)
3029 hdr
->dwError
= INTERNET_HANDLE_IN_USE
;
3030 else if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
3031 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
3034 res
= HTTPREQ_Read(req
, (char*)buffers
->lpvBuffer
+read
, size
-read
,
3035 &buffers
->dwBufferLength
, !(flags
& IRF_NO_WAIT
));
3036 if(res
!= ERROR_SUCCESS
)
3039 read
+= buffers
->dwBufferLength
;
3040 if(read
== size
|| end_of_read_data(req
))
3043 LeaveCriticalSection( &req
->read_section
);
3045 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
3046 &buffers
->dwBufferLength
, sizeof(buffers
->dwBufferLength
));
3047 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
3048 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
3050 EnterCriticalSection( &req
->read_section
);
3053 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
3054 hdr
->dwError
= ERROR_SUCCESS
;
3056 error
= hdr
->dwError
;
3058 LeaveCriticalSection( &req
->read_section
);
3059 size
= buffers
->dwBufferLength
;
3060 buffers
->dwBufferLength
= read
;
3063 if (res
== ERROR_SUCCESS
) {
3064 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
3065 &size
, sizeof(size
));
3068 return res
==ERROR_SUCCESS
? error
: res
;
3071 static DWORD
HTTPREQ_WriteFile(object_header_t
*hdr
, const void *buffer
, DWORD size
, DWORD
*written
)
3074 http_request_t
*request
= (http_request_t
*)hdr
;
3076 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
3079 res
= NETCON_send(request
->netconn
, buffer
, size
, 0, (LPINT
)written
);
3080 if (res
== ERROR_SUCCESS
)
3081 request
->bytesWritten
+= *written
;
3083 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_SENT
, written
, sizeof(DWORD
));
3087 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST
*workRequest
)
3089 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
3091 HTTP_ReceiveRequestData(req
, FALSE
);
3094 static DWORD
HTTPREQ_QueryDataAvailable(object_header_t
*hdr
, DWORD
*available
, DWORD flags
, DWORD_PTR ctx
)
3096 http_request_t
*req
= (http_request_t
*)hdr
;
3098 TRACE("(%p %p %x %lx)\n", req
, available
, flags
, ctx
);
3100 if (req
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
3102 WORKREQUEST workRequest
;
3104 /* never wait, if we can't enter the section we queue an async request right away */
3105 if (TryEnterCriticalSection( &req
->read_section
))
3107 refill_read_buffer(req
, READMODE_NOBLOCK
, NULL
);
3108 if ((*available
= get_avail_data( req
))) goto done
;
3109 if (end_of_read_data( req
)) goto done
;
3110 LeaveCriticalSection( &req
->read_section
);
3113 workRequest
.asyncproc
= HTTPREQ_AsyncQueryDataAvailableProc
;
3114 workRequest
.hdr
= WININET_AddRef( &req
->hdr
);
3116 INTERNET_AsyncCall(&workRequest
);
3118 return ERROR_IO_PENDING
;
3121 EnterCriticalSection( &req
->read_section
);
3123 if (!(*available
= get_avail_data( req
)) && !end_of_read_data( req
))
3125 refill_read_buffer( req
, READMODE_ASYNC
, NULL
);
3126 *available
= get_avail_data( req
);
3130 LeaveCriticalSection( &req
->read_section
);
3132 TRACE( "returning %u\n", *available
);
3133 return ERROR_SUCCESS
;
3136 static const object_vtbl_t HTTPREQVtbl
= {
3138 HTTPREQ_CloseConnection
,
3139 HTTPREQ_QueryOption
,
3142 HTTPREQ_ReadFileExA
,
3143 HTTPREQ_ReadFileExW
,
3145 HTTPREQ_QueryDataAvailable
,
3149 /***********************************************************************
3150 * HTTP_HttpOpenRequestW (internal)
3152 * Open a HTTP request handle
3155 * HINTERNET a HTTP request handle on success
3159 static DWORD
HTTP_HttpOpenRequestW(http_session_t
*session
,
3160 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
3161 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
3162 DWORD dwFlags
, DWORD_PTR dwContext
, HINTERNET
*ret
)
3164 appinfo_t
*hIC
= session
->appInfo
;
3165 http_request_t
*request
;
3167 DWORD len
, res
= ERROR_SUCCESS
;
3171 request
= alloc_object(&session
->hdr
, &HTTPREQVtbl
, sizeof(http_request_t
));
3173 return ERROR_OUTOFMEMORY
;
3175 request
->hdr
.htype
= WH_HHTTPREQ
;
3176 request
->hdr
.dwFlags
= dwFlags
;
3177 request
->hdr
.dwContext
= dwContext
;
3178 request
->contentLength
= ~0u;
3180 request
->netconn_stream
.data_stream
.vtbl
= &netconn_stream_vtbl
;
3181 request
->data_stream
= &request
->netconn_stream
.data_stream
;
3182 request
->connect_timeout
= session
->connect_timeout
;
3183 request
->send_timeout
= session
->send_timeout
;
3184 request
->receive_timeout
= session
->receive_timeout
;
3186 InitializeCriticalSection( &request
->read_section
);
3187 request
->read_section
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": http_request_t.read_section");
3189 WININET_AddRef( &session
->hdr
);
3190 request
->session
= session
;
3191 list_add_head( &session
->hdr
.children
, &request
->hdr
.entry
);
3193 port
= session
->hostPort
;
3194 if(port
== INTERNET_INVALID_PORT_NUMBER
)
3195 port
= dwFlags
& INTERNET_FLAG_SECURE
? INTERNET_DEFAULT_HTTPS_PORT
: INTERNET_DEFAULT_HTTP_PORT
;
3197 request
->server
= get_server(session
->hostName
, port
, TRUE
);
3198 if(!request
->server
) {
3199 WININET_Release(&request
->hdr
);
3200 return ERROR_OUTOFMEMORY
;
3203 if (dwFlags
& INTERNET_FLAG_IGNORE_CERT_CN_INVALID
)
3204 request
->security_flags
|= SECURITY_FLAG_IGNORE_CERT_CN_INVALID
;
3205 if (dwFlags
& INTERNET_FLAG_IGNORE_CERT_DATE_INVALID
)
3206 request
->security_flags
|= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
;
3208 if (lpszObjectName
&& *lpszObjectName
) {
3212 rc
= UrlEscapeW(lpszObjectName
, NULL
, &len
, URL_ESCAPE_SPACES_ONLY
);
3213 if (rc
!= E_POINTER
)
3214 len
= strlenW(lpszObjectName
)+1;
3215 request
->path
= heap_alloc(len
*sizeof(WCHAR
));
3216 rc
= UrlEscapeW(lpszObjectName
, request
->path
, &len
,
3217 URL_ESCAPE_SPACES_ONLY
);
3220 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName
),rc
);
3221 strcpyW(request
->path
,lpszObjectName
);
3224 static const WCHAR slashW
[] = {'/',0};
3226 request
->path
= heap_strdupW(slashW
);
3229 if (lpszReferrer
&& *lpszReferrer
)
3230 HTTP_ProcessHeader(request
, HTTP_REFERER
, lpszReferrer
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
3232 if (lpszAcceptTypes
)
3235 for (i
= 0; lpszAcceptTypes
[i
]; i
++)
3237 if (!*lpszAcceptTypes
[i
]) continue;
3238 HTTP_ProcessHeader(request
, HTTP_ACCEPT
, lpszAcceptTypes
[i
],
3239 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
|
3240 HTTP_ADDHDR_FLAG_REQ
|
3241 (i
== 0 ? HTTP_ADDHDR_FLAG_REPLACE
: 0));
3245 request
->verb
= heap_strdupW(lpszVerb
&& *lpszVerb
? lpszVerb
: szGET
);
3246 request
->version
= heap_strdupW(lpszVersion
? lpszVersion
: g_szHttp1_1
);
3248 if (session
->hostPort
!= INTERNET_INVALID_PORT_NUMBER
&&
3249 session
->hostPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
3250 session
->hostPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
3254 static const WCHAR host_formatW
[] = {'%','s',':','%','u',0};
3256 host_name
= heap_alloc((strlenW(session
->hostName
) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR
));
3258 res
= ERROR_OUTOFMEMORY
;
3262 sprintfW(host_name
, host_formatW
, session
->hostName
, session
->hostPort
);
3263 HTTP_ProcessHeader(request
, hostW
, host_name
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
3264 heap_free(host_name
);
3267 HTTP_ProcessHeader(request
, hostW
, session
->hostName
,
3268 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
3270 if (session
->hostPort
== INTERNET_INVALID_PORT_NUMBER
)
3271 session
->hostPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
3272 INTERNET_DEFAULT_HTTPS_PORT
:
3273 INTERNET_DEFAULT_HTTP_PORT
);
3275 if (hIC
->proxy
&& hIC
->proxy
[0])
3276 HTTP_DealWithProxy( hIC
, session
, request
);
3278 INTERNET_SendCallback(&session
->hdr
, dwContext
,
3279 INTERNET_STATUS_HANDLE_CREATED
, &request
->hdr
.hInternet
,
3283 TRACE("<-- %u (%p)\n", res
, request
);
3285 if(res
!= ERROR_SUCCESS
) {
3286 WININET_Release( &request
->hdr
);
3291 *ret
= request
->hdr
.hInternet
;
3292 return ERROR_SUCCESS
;
3295 /***********************************************************************
3296 * HttpOpenRequestW (WININET.@)
3298 * Open a HTTP request handle
3301 * HINTERNET a HTTP request handle on success
3305 HINTERNET WINAPI
HttpOpenRequestW(HINTERNET hHttpSession
,
3306 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
3307 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
3308 DWORD dwFlags
, DWORD_PTR dwContext
)
3310 http_session_t
*session
;
3311 HINTERNET handle
= NULL
;
3314 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
3315 debugstr_w(lpszVerb
), debugstr_w(lpszObjectName
),
3316 debugstr_w(lpszVersion
), debugstr_w(lpszReferrer
), lpszAcceptTypes
,
3317 dwFlags
, dwContext
);
3318 if(lpszAcceptTypes
!=NULL
)
3321 for(i
=0;lpszAcceptTypes
[i
]!=NULL
;i
++)
3322 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes
[i
]));
3325 session
= (http_session_t
*) get_handle_object( hHttpSession
);
3326 if (NULL
== session
|| session
->hdr
.htype
!= WH_HHTTPSESSION
)
3328 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
3333 * My tests seem to show that the windows version does not
3334 * become asynchronous until after this point. And anyhow
3335 * if this call was asynchronous then how would you get the
3336 * necessary HINTERNET pointer returned by this function.
3339 res
= HTTP_HttpOpenRequestW(session
, lpszVerb
, lpszObjectName
,
3340 lpszVersion
, lpszReferrer
, lpszAcceptTypes
,
3341 dwFlags
, dwContext
, &handle
);
3344 WININET_Release( &session
->hdr
);
3345 TRACE("returning %p\n", handle
);
3346 if(res
!= ERROR_SUCCESS
)
3351 static const LPCWSTR header_lookup
[] = {
3352 szMime_Version
, /* HTTP_QUERY_MIME_VERSION = 0 */
3353 szContent_Type
, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3354 szContent_Transfer_Encoding
,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3355 szContent_ID
, /* HTTP_QUERY_CONTENT_ID = 3 */
3356 NULL
, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3357 szContent_Length
, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3358 szContent_Language
, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3359 szAllow
, /* HTTP_QUERY_ALLOW = 7 */
3360 szPublic
, /* HTTP_QUERY_PUBLIC = 8 */
3361 szDate
, /* HTTP_QUERY_DATE = 9 */
3362 szExpires
, /* HTTP_QUERY_EXPIRES = 10 */
3363 szLast_Modified
, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3364 NULL
, /* HTTP_QUERY_MESSAGE_ID = 12 */
3365 szURI
, /* HTTP_QUERY_URI = 13 */
3366 szFrom
, /* HTTP_QUERY_DERIVED_FROM = 14 */
3367 NULL
, /* HTTP_QUERY_COST = 15 */
3368 NULL
, /* HTTP_QUERY_LINK = 16 */
3369 szPragma
, /* HTTP_QUERY_PRAGMA = 17 */
3370 NULL
, /* HTTP_QUERY_VERSION = 18 */
3371 szStatus
, /* HTTP_QUERY_STATUS_CODE = 19 */
3372 NULL
, /* HTTP_QUERY_STATUS_TEXT = 20 */
3373 NULL
, /* HTTP_QUERY_RAW_HEADERS = 21 */
3374 NULL
, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3375 szConnection
, /* HTTP_QUERY_CONNECTION = 23 */
3376 szAccept
, /* HTTP_QUERY_ACCEPT = 24 */
3377 szAccept_Charset
, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3378 szAccept_Encoding
, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3379 szAccept_Language
, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3380 szAuthorization
, /* HTTP_QUERY_AUTHORIZATION = 28 */
3381 szContent_Encoding
, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3382 NULL
, /* HTTP_QUERY_FORWARDED = 30 */
3383 NULL
, /* HTTP_QUERY_FROM = 31 */
3384 szIf_Modified_Since
, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3385 szLocation
, /* HTTP_QUERY_LOCATION = 33 */
3386 NULL
, /* HTTP_QUERY_ORIG_URI = 34 */
3387 szReferer
, /* HTTP_QUERY_REFERER = 35 */
3388 szRetry_After
, /* HTTP_QUERY_RETRY_AFTER = 36 */
3389 szServer
, /* HTTP_QUERY_SERVER = 37 */
3390 NULL
, /* HTTP_TITLE = 38 */
3391 szUser_Agent
, /* HTTP_QUERY_USER_AGENT = 39 */
3392 szWWW_Authenticate
, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3393 szProxy_Authenticate
, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3394 szAccept_Ranges
, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3395 szSet_Cookie
, /* HTTP_QUERY_SET_COOKIE = 43 */
3396 szCookie
, /* HTTP_QUERY_COOKIE = 44 */
3397 NULL
, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3398 NULL
, /* HTTP_QUERY_REFRESH = 46 */
3399 szContent_Disposition
, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3400 szAge
, /* HTTP_QUERY_AGE = 48 */
3401 szCache_Control
, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3402 szContent_Base
, /* HTTP_QUERY_CONTENT_BASE = 50 */
3403 szContent_Location
, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3404 szContent_MD5
, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3405 szContent_Range
, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3406 szETag
, /* HTTP_QUERY_ETAG = 54 */
3407 hostW
, /* HTTP_QUERY_HOST = 55 */
3408 szIf_Match
, /* HTTP_QUERY_IF_MATCH = 56 */
3409 szIf_None_Match
, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3410 szIf_Range
, /* HTTP_QUERY_IF_RANGE = 58 */
3411 szIf_Unmodified_Since
, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3412 szMax_Forwards
, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3413 szProxy_Authorization
, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3414 szRange
, /* HTTP_QUERY_RANGE = 62 */
3415 szTransfer_Encoding
, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3416 szUpgrade
, /* HTTP_QUERY_UPGRADE = 64 */
3417 szVary
, /* HTTP_QUERY_VARY = 65 */
3418 szVia
, /* HTTP_QUERY_VIA = 66 */
3419 szWarning
, /* HTTP_QUERY_WARNING = 67 */
3420 szExpect
, /* HTTP_QUERY_EXPECT = 68 */
3421 szProxy_Connection
, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3422 szUnless_Modified_Since
, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3425 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3427 /***********************************************************************
3428 * HTTP_HttpQueryInfoW (internal)
3430 static DWORD
HTTP_HttpQueryInfoW(http_request_t
*request
, DWORD dwInfoLevel
,
3431 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3433 LPHTTPHEADERW lphttpHdr
= NULL
;
3434 BOOL request_only
= dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
;
3435 INT requested_index
= lpdwIndex
? *lpdwIndex
: 0;
3436 DWORD level
= (dwInfoLevel
& ~HTTP_QUERY_MODIFIER_FLAGS_MASK
);
3439 /* Find requested header structure */
3442 case HTTP_QUERY_CUSTOM
:
3443 if (!lpBuffer
) return ERROR_INVALID_PARAMETER
;
3444 index
= HTTP_GetCustomHeaderIndex(request
, lpBuffer
, requested_index
, request_only
);
3446 case HTTP_QUERY_RAW_HEADERS_CRLF
:
3450 DWORD res
= ERROR_INVALID_PARAMETER
;
3453 headers
= HTTP_BuildHeaderRequestString(request
, request
->verb
, request
->path
, request
->version
);
3455 headers
= request
->rawHeaders
;
3458 len
= strlenW(headers
) * sizeof(WCHAR
);
3460 if (len
+ sizeof(WCHAR
) > *lpdwBufferLength
)
3462 len
+= sizeof(WCHAR
);
3463 res
= ERROR_INSUFFICIENT_BUFFER
;
3468 memcpy(lpBuffer
, headers
, len
+ sizeof(WCHAR
));
3471 len
= strlenW(szCrLf
) * sizeof(WCHAR
);
3472 memcpy(lpBuffer
, szCrLf
, sizeof(szCrLf
));
3474 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
/ sizeof(WCHAR
)));
3475 res
= ERROR_SUCCESS
;
3477 *lpdwBufferLength
= len
;
3479 if (request_only
) heap_free(headers
);
3482 case HTTP_QUERY_RAW_HEADERS
:
3484 LPWSTR
* ppszRawHeaderLines
= HTTP_Tokenize(request
->rawHeaders
, szCrLf
);
3486 LPWSTR pszString
= lpBuffer
;
3488 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
3489 size
+= strlenW(ppszRawHeaderLines
[i
]) + 1;
3491 if (size
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3493 HTTP_FreeTokens(ppszRawHeaderLines
);
3494 *lpdwBufferLength
= (size
+ 1) * sizeof(WCHAR
);
3495 return ERROR_INSUFFICIENT_BUFFER
;
3499 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
3501 DWORD len
= strlenW(ppszRawHeaderLines
[i
]);
3502 memcpy(pszString
, ppszRawHeaderLines
[i
], (len
+1)*sizeof(WCHAR
));
3506 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, size
));
3508 *lpdwBufferLength
= size
* sizeof(WCHAR
);
3509 HTTP_FreeTokens(ppszRawHeaderLines
);
3511 return ERROR_SUCCESS
;
3513 case HTTP_QUERY_STATUS_TEXT
:
3514 if (request
->statusText
)
3516 DWORD len
= strlenW(request
->statusText
);
3517 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3519 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
3520 return ERROR_INSUFFICIENT_BUFFER
;
3524 memcpy(lpBuffer
, request
->statusText
, (len
+ 1) * sizeof(WCHAR
));
3525 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
3527 *lpdwBufferLength
= len
* sizeof(WCHAR
);
3528 return ERROR_SUCCESS
;
3531 case HTTP_QUERY_VERSION
:
3532 if (request
->version
)
3534 DWORD len
= strlenW(request
->version
);
3535 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3537 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
3538 return ERROR_INSUFFICIENT_BUFFER
;
3542 memcpy(lpBuffer
, request
->version
, (len
+ 1) * sizeof(WCHAR
));
3543 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
3545 *lpdwBufferLength
= len
* sizeof(WCHAR
);
3546 return ERROR_SUCCESS
;
3549 case HTTP_QUERY_CONTENT_ENCODING
:
3550 index
= HTTP_GetCustomHeaderIndex(request
, header_lookup
[request
->read_gzip
? HTTP_QUERY_CONTENT_TYPE
: level
],
3551 requested_index
,request_only
);
3553 case HTTP_QUERY_STATUS_CODE
: {
3554 DWORD res
= ERROR_SUCCESS
;
3557 return ERROR_HTTP_INVALID_QUERY_REQUEST
;
3562 if(dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
) {
3563 if(*lpdwBufferLength
>= sizeof(DWORD
))
3564 *(DWORD
*)lpBuffer
= request
->status_code
;
3566 res
= ERROR_INSUFFICIENT_BUFFER
;
3567 *lpdwBufferLength
= sizeof(DWORD
);
3571 static const WCHAR formatW
[] = {'%','u',0};
3573 size
= sprintfW(buf
, formatW
, request
->status_code
) * sizeof(WCHAR
);
3575 if(size
<= *lpdwBufferLength
) {
3576 memcpy(lpBuffer
, buf
, size
+sizeof(WCHAR
));
3578 size
+= sizeof(WCHAR
);
3579 res
= ERROR_INSUFFICIENT_BUFFER
;
3582 *lpdwBufferLength
= size
;
3587 assert (LAST_TABLE_HEADER
== (HTTP_QUERY_UNLESS_MODIFIED_SINCE
+ 1));
3589 if (level
< LAST_TABLE_HEADER
&& header_lookup
[level
])
3590 index
= HTTP_GetCustomHeaderIndex(request
, header_lookup
[level
],
3591 requested_index
,request_only
);
3595 lphttpHdr
= &request
->custHeaders
[index
];
3597 /* Ensure header satisfies requested attributes */
3599 ((dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
) &&
3600 (~lphttpHdr
->wFlags
& HDR_ISREQUEST
)))
3602 return ERROR_HTTP_HEADER_NOT_FOUND
;
3605 if (lpdwIndex
) (*lpdwIndex
)++;
3607 /* coalesce value to requested type */
3608 if (dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
&& lpBuffer
)
3610 *(int *)lpBuffer
= atoiW(lphttpHdr
->lpszValue
);
3611 TRACE(" returning number: %d\n", *(int *)lpBuffer
);
3613 else if (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
&& lpBuffer
)
3619 tmpTime
= ConvertTimeString(lphttpHdr
->lpszValue
);
3621 tmpTM
= *gmtime(&tmpTime
);
3622 STHook
= (SYSTEMTIME
*)lpBuffer
;
3623 STHook
->wDay
= tmpTM
.tm_mday
;
3624 STHook
->wHour
= tmpTM
.tm_hour
;
3625 STHook
->wMilliseconds
= 0;
3626 STHook
->wMinute
= tmpTM
.tm_min
;
3627 STHook
->wDayOfWeek
= tmpTM
.tm_wday
;
3628 STHook
->wMonth
= tmpTM
.tm_mon
+ 1;
3629 STHook
->wSecond
= tmpTM
.tm_sec
;
3630 STHook
->wYear
= tmpTM
.tm_year
;
3632 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3633 STHook
->wYear
, STHook
->wMonth
, STHook
->wDay
, STHook
->wDayOfWeek
,
3634 STHook
->wHour
, STHook
->wMinute
, STHook
->wSecond
, STHook
->wMilliseconds
);
3636 else if (lphttpHdr
->lpszValue
)
3638 DWORD len
= (strlenW(lphttpHdr
->lpszValue
) + 1) * sizeof(WCHAR
);
3640 if (len
> *lpdwBufferLength
)
3642 *lpdwBufferLength
= len
;
3643 return ERROR_INSUFFICIENT_BUFFER
;
3647 memcpy(lpBuffer
, lphttpHdr
->lpszValue
, len
);
3648 TRACE("! returning string: %s\n", debugstr_w(lpBuffer
));
3650 *lpdwBufferLength
= len
- sizeof(WCHAR
);
3652 return ERROR_SUCCESS
;
3655 /***********************************************************************
3656 * HttpQueryInfoW (WININET.@)
3658 * Queries for information about an HTTP request
3665 BOOL WINAPI
HttpQueryInfoW(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3666 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3668 http_request_t
*request
;
3671 if (TRACE_ON(wininet
)) {
3672 #define FE(x) { x, #x }
3673 static const wininet_flag_info query_flags
[] = {
3674 FE(HTTP_QUERY_MIME_VERSION
),
3675 FE(HTTP_QUERY_CONTENT_TYPE
),
3676 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING
),
3677 FE(HTTP_QUERY_CONTENT_ID
),
3678 FE(HTTP_QUERY_CONTENT_DESCRIPTION
),
3679 FE(HTTP_QUERY_CONTENT_LENGTH
),
3680 FE(HTTP_QUERY_CONTENT_LANGUAGE
),
3681 FE(HTTP_QUERY_ALLOW
),
3682 FE(HTTP_QUERY_PUBLIC
),
3683 FE(HTTP_QUERY_DATE
),
3684 FE(HTTP_QUERY_EXPIRES
),
3685 FE(HTTP_QUERY_LAST_MODIFIED
),
3686 FE(HTTP_QUERY_MESSAGE_ID
),
3688 FE(HTTP_QUERY_DERIVED_FROM
),
3689 FE(HTTP_QUERY_COST
),
3690 FE(HTTP_QUERY_LINK
),
3691 FE(HTTP_QUERY_PRAGMA
),
3692 FE(HTTP_QUERY_VERSION
),
3693 FE(HTTP_QUERY_STATUS_CODE
),
3694 FE(HTTP_QUERY_STATUS_TEXT
),
3695 FE(HTTP_QUERY_RAW_HEADERS
),
3696 FE(HTTP_QUERY_RAW_HEADERS_CRLF
),
3697 FE(HTTP_QUERY_CONNECTION
),
3698 FE(HTTP_QUERY_ACCEPT
),
3699 FE(HTTP_QUERY_ACCEPT_CHARSET
),
3700 FE(HTTP_QUERY_ACCEPT_ENCODING
),
3701 FE(HTTP_QUERY_ACCEPT_LANGUAGE
),
3702 FE(HTTP_QUERY_AUTHORIZATION
),
3703 FE(HTTP_QUERY_CONTENT_ENCODING
),
3704 FE(HTTP_QUERY_FORWARDED
),
3705 FE(HTTP_QUERY_FROM
),
3706 FE(HTTP_QUERY_IF_MODIFIED_SINCE
),
3707 FE(HTTP_QUERY_LOCATION
),
3708 FE(HTTP_QUERY_ORIG_URI
),
3709 FE(HTTP_QUERY_REFERER
),
3710 FE(HTTP_QUERY_RETRY_AFTER
),
3711 FE(HTTP_QUERY_SERVER
),
3712 FE(HTTP_QUERY_TITLE
),
3713 FE(HTTP_QUERY_USER_AGENT
),
3714 FE(HTTP_QUERY_WWW_AUTHENTICATE
),
3715 FE(HTTP_QUERY_PROXY_AUTHENTICATE
),
3716 FE(HTTP_QUERY_ACCEPT_RANGES
),
3717 FE(HTTP_QUERY_SET_COOKIE
),
3718 FE(HTTP_QUERY_COOKIE
),
3719 FE(HTTP_QUERY_REQUEST_METHOD
),
3720 FE(HTTP_QUERY_REFRESH
),
3721 FE(HTTP_QUERY_CONTENT_DISPOSITION
),
3723 FE(HTTP_QUERY_CACHE_CONTROL
),
3724 FE(HTTP_QUERY_CONTENT_BASE
),
3725 FE(HTTP_QUERY_CONTENT_LOCATION
),
3726 FE(HTTP_QUERY_CONTENT_MD5
),
3727 FE(HTTP_QUERY_CONTENT_RANGE
),
3728 FE(HTTP_QUERY_ETAG
),
3729 FE(HTTP_QUERY_HOST
),
3730 FE(HTTP_QUERY_IF_MATCH
),
3731 FE(HTTP_QUERY_IF_NONE_MATCH
),
3732 FE(HTTP_QUERY_IF_RANGE
),
3733 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE
),
3734 FE(HTTP_QUERY_MAX_FORWARDS
),
3735 FE(HTTP_QUERY_PROXY_AUTHORIZATION
),
3736 FE(HTTP_QUERY_RANGE
),
3737 FE(HTTP_QUERY_TRANSFER_ENCODING
),
3738 FE(HTTP_QUERY_UPGRADE
),
3739 FE(HTTP_QUERY_VARY
),
3741 FE(HTTP_QUERY_WARNING
),
3742 FE(HTTP_QUERY_CUSTOM
)
3744 static const wininet_flag_info modifier_flags
[] = {
3745 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS
),
3746 FE(HTTP_QUERY_FLAG_SYSTEMTIME
),
3747 FE(HTTP_QUERY_FLAG_NUMBER
),
3748 FE(HTTP_QUERY_FLAG_COALESCE
)
3751 DWORD info_mod
= dwInfoLevel
& HTTP_QUERY_MODIFIER_FLAGS_MASK
;
3752 DWORD info
= dwInfoLevel
& HTTP_QUERY_HEADER_MASK
;
3755 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest
, dwInfoLevel
, info
);
3756 TRACE(" Attribute:");
3757 for (i
= 0; i
< (sizeof(query_flags
) / sizeof(query_flags
[0])); i
++) {
3758 if (query_flags
[i
].val
== info
) {
3759 TRACE(" %s", query_flags
[i
].name
);
3763 if (i
== (sizeof(query_flags
) / sizeof(query_flags
[0]))) {
3764 TRACE(" Unknown (%08x)", info
);
3767 TRACE(" Modifier:");
3768 for (i
= 0; i
< (sizeof(modifier_flags
) / sizeof(modifier_flags
[0])); i
++) {
3769 if (modifier_flags
[i
].val
& info_mod
) {
3770 TRACE(" %s", modifier_flags
[i
].name
);
3771 info_mod
&= ~ modifier_flags
[i
].val
;
3776 TRACE(" Unknown (%08x)", info_mod
);
3781 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
3782 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
3784 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
3788 if (lpBuffer
== NULL
)
3789 *lpdwBufferLength
= 0;
3790 res
= HTTP_HttpQueryInfoW( request
, dwInfoLevel
,
3791 lpBuffer
, lpdwBufferLength
, lpdwIndex
);
3795 WININET_Release( &request
->hdr
);
3797 TRACE("%u <--\n", res
);
3798 if(res
!= ERROR_SUCCESS
)
3800 return res
== ERROR_SUCCESS
;
3803 /***********************************************************************
3804 * HttpQueryInfoA (WININET.@)
3806 * Queries for information about an HTTP request
3813 BOOL WINAPI
HttpQueryInfoA(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3814 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3820 if((dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
) ||
3821 (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
))
3823 return HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, lpBuffer
,
3824 lpdwBufferLength
, lpdwIndex
);
3830 len
= (*lpdwBufferLength
)*sizeof(WCHAR
);
3831 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3833 alloclen
= MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, NULL
, 0 ) * sizeof(WCHAR
);
3839 bufferW
= heap_alloc(alloclen
);
3840 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3841 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3842 MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, bufferW
, alloclen
/ sizeof(WCHAR
) );
3849 result
= HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, bufferW
,
3853 len
= WideCharToMultiByte( CP_ACP
,0, bufferW
, len
/ sizeof(WCHAR
) + 1,
3854 lpBuffer
, *lpdwBufferLength
, NULL
, NULL
);
3855 *lpdwBufferLength
= len
- 1;
3857 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer
));
3860 /* since the strings being returned from HttpQueryInfoW should be
3861 * only ASCII characters, it is reasonable to assume that all of
3862 * the Unicode characters can be reduced to a single byte */
3863 *lpdwBufferLength
= len
/ sizeof(WCHAR
);
3865 heap_free( bufferW
);
3869 /***********************************************************************
3870 * HTTP_GetRedirectURL (internal)
3872 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*request
, LPCWSTR lpszUrl
)
3874 static WCHAR szHttp
[] = {'h','t','t','p',0};
3875 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3876 http_session_t
*session
= request
->session
;
3877 URL_COMPONENTSW urlComponents
;
3878 DWORD url_length
= 0;
3880 LPWSTR combined_url
;
3882 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3883 urlComponents
.lpszScheme
= (request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) ? szHttps
: szHttp
;
3884 urlComponents
.dwSchemeLength
= 0;
3885 urlComponents
.lpszHostName
= session
->hostName
;
3886 urlComponents
.dwHostNameLength
= 0;
3887 urlComponents
.nPort
= session
->hostPort
;
3888 urlComponents
.lpszUserName
= session
->userName
;
3889 urlComponents
.dwUserNameLength
= 0;
3890 urlComponents
.lpszPassword
= NULL
;
3891 urlComponents
.dwPasswordLength
= 0;
3892 urlComponents
.lpszUrlPath
= request
->path
;
3893 urlComponents
.dwUrlPathLength
= 0;
3894 urlComponents
.lpszExtraInfo
= NULL
;
3895 urlComponents
.dwExtraInfoLength
= 0;
3897 if (!InternetCreateUrlW(&urlComponents
, 0, NULL
, &url_length
) &&
3898 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3901 orig_url
= heap_alloc(url_length
);
3903 /* convert from bytes to characters */
3904 url_length
= url_length
/ sizeof(WCHAR
) - 1;
3905 if (!InternetCreateUrlW(&urlComponents
, 0, orig_url
, &url_length
))
3907 heap_free(orig_url
);
3912 if (!InternetCombineUrlW(orig_url
, lpszUrl
, NULL
, &url_length
, ICU_ENCODE_SPACES_ONLY
) &&
3913 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3915 heap_free(orig_url
);
3918 combined_url
= heap_alloc(url_length
* sizeof(WCHAR
));
3920 if (!InternetCombineUrlW(orig_url
, lpszUrl
, combined_url
, &url_length
, ICU_ENCODE_SPACES_ONLY
))
3922 heap_free(orig_url
);
3923 heap_free(combined_url
);
3926 heap_free(orig_url
);
3927 return combined_url
;
3931 /***********************************************************************
3932 * HTTP_HandleRedirect (internal)
3934 static DWORD
HTTP_HandleRedirect(http_request_t
*request
, LPCWSTR lpszUrl
)
3936 http_session_t
*session
= request
->session
;
3937 WCHAR path
[INTERNET_MAX_PATH_LENGTH
];
3942 /* if it's an absolute path, keep the same session info */
3943 lstrcpynW(path
, lpszUrl
, INTERNET_MAX_URL_LENGTH
);
3947 URL_COMPONENTSW urlComponents
;
3948 WCHAR protocol
[INTERNET_MAX_SCHEME_LENGTH
];
3949 WCHAR hostName
[INTERNET_MAX_HOST_NAME_LENGTH
];
3950 WCHAR userName
[INTERNET_MAX_USER_NAME_LENGTH
];
3951 BOOL custom_port
= FALSE
;
3953 static WCHAR httpW
[] = {'h','t','t','p',0};
3954 static WCHAR httpsW
[] = {'h','t','t','p','s',0};
3960 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3961 urlComponents
.lpszScheme
= protocol
;
3962 urlComponents
.dwSchemeLength
= INTERNET_MAX_SCHEME_LENGTH
;
3963 urlComponents
.lpszHostName
= hostName
;
3964 urlComponents
.dwHostNameLength
= INTERNET_MAX_HOST_NAME_LENGTH
;
3965 urlComponents
.lpszUserName
= userName
;
3966 urlComponents
.dwUserNameLength
= INTERNET_MAX_USER_NAME_LENGTH
;
3967 urlComponents
.lpszPassword
= NULL
;
3968 urlComponents
.dwPasswordLength
= 0;
3969 urlComponents
.lpszUrlPath
= path
;
3970 urlComponents
.dwUrlPathLength
= INTERNET_MAX_PATH_LENGTH
;
3971 urlComponents
.lpszExtraInfo
= NULL
;
3972 urlComponents
.dwExtraInfoLength
= 0;
3973 if(!InternetCrackUrlW(lpszUrl
, strlenW(lpszUrl
), 0, &urlComponents
))
3974 return INTERNET_GetLastError();
3976 if(!strcmpiW(protocol
, httpW
)) {
3977 if(request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) {
3978 TRACE("redirect from secure page to non-secure page\n");
3979 /* FIXME: warn about from secure redirect to non-secure page */
3980 request
->hdr
.dwFlags
&= ~INTERNET_FLAG_SECURE
;
3983 if(urlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
3984 urlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
3985 else if(urlComponents
.nPort
!= INTERNET_DEFAULT_HTTP_PORT
)
3987 }else if(!strcmpiW(protocol
, httpsW
)) {
3988 if(!(request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)) {
3989 TRACE("redirect from non-secure page to secure page\n");
3990 /* FIXME: notify about redirect to secure page */
3991 request
->hdr
.dwFlags
|= INTERNET_FLAG_SECURE
;
3994 if(urlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
3995 urlComponents
.nPort
= INTERNET_DEFAULT_HTTPS_PORT
;
3996 else if(urlComponents
.nPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
4000 heap_free(session
->hostName
);
4004 static const WCHAR fmt
[] = {'%','s',':','%','u',0};
4005 len
= lstrlenW(hostName
);
4006 len
+= 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
4007 session
->hostName
= heap_alloc(len
*sizeof(WCHAR
));
4008 sprintfW(session
->hostName
, fmt
, hostName
, urlComponents
.nPort
);
4011 session
->hostName
= heap_strdupW(hostName
);
4013 HTTP_ProcessHeader(request
, hostW
, session
->hostName
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDREQ_FLAG_REPLACE
| HTTP_ADDHDR_FLAG_REQ
);
4015 heap_free(session
->userName
);
4016 session
->userName
= NULL
;
4018 session
->userName
= heap_strdupW(userName
);
4020 reset_data_stream(request
);
4022 if(strcmpiW(request
->server
->name
, hostName
) || request
->server
->port
!= urlComponents
.nPort
) {
4023 server_t
*new_server
;
4025 new_server
= get_server(hostName
, urlComponents
.nPort
, TRUE
);
4026 server_release(request
->server
);
4027 request
->server
= new_server
;
4030 heap_free(request
->path
);
4037 rc
= UrlEscapeW(path
, NULL
, &needed
, URL_ESCAPE_SPACES_ONLY
);
4038 if (rc
!= E_POINTER
)
4039 needed
= strlenW(path
)+1;
4040 request
->path
= heap_alloc(needed
*sizeof(WCHAR
));
4041 rc
= UrlEscapeW(path
, request
->path
, &needed
,
4042 URL_ESCAPE_SPACES_ONLY
);
4045 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path
),rc
);
4046 strcpyW(request
->path
,path
);
4050 /* Remove custom content-type/length headers on redirects. */
4051 index
= HTTP_GetCustomHeaderIndex(request
, szContent_Type
, 0, TRUE
);
4053 HTTP_DeleteCustomHeader(request
, index
);
4054 index
= HTTP_GetCustomHeaderIndex(request
, szContent_Length
, 0, TRUE
);
4056 HTTP_DeleteCustomHeader(request
, index
);
4058 return ERROR_SUCCESS
;
4061 /***********************************************************************
4062 * HTTP_build_req (internal)
4064 * concatenate all the strings in the request together
4066 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
)
4071 for( t
= list
; *t
; t
++ )
4072 len
+= strlenW( *t
);
4075 str
= heap_alloc(len
*sizeof(WCHAR
));
4078 for( t
= list
; *t
; t
++ )
4084 static DWORD
HTTP_SecureProxyConnect(http_request_t
*request
)
4086 server_t
*server
= request
->server
;
4088 LPWSTR requestString
;
4094 static const WCHAR szConnect
[] = {'C','O','N','N','E','C','T',0};
4095 static const WCHAR szFormat
[] = {'%','s',':','%','u',0};
4099 lpszPath
= heap_alloc((lstrlenW(server
->name
) + 13)*sizeof(WCHAR
));
4100 sprintfW(lpszPath
, szFormat
, server
->name
, server
->port
);
4101 requestString
= HTTP_BuildHeaderRequestString( request
, szConnect
, lpszPath
, g_szHttp1_1
);
4102 heap_free( lpszPath
);
4104 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
4105 NULL
, 0, NULL
, NULL
);
4106 len
--; /* the nul terminator isn't needed */
4107 ascii_req
= heap_alloc(len
);
4108 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1, ascii_req
, len
, NULL
, NULL
);
4109 heap_free( requestString
);
4111 TRACE("full request -> %s\n", debugstr_an( ascii_req
, len
) );
4113 NETCON_set_timeout( request
->netconn
, TRUE
, request
->send_timeout
);
4114 res
= NETCON_send( request
->netconn
, ascii_req
, len
, 0, &cnt
);
4115 heap_free( ascii_req
);
4116 if (res
!= ERROR_SUCCESS
)
4119 responseLen
= HTTP_GetResponseHeaders( request
, TRUE
);
4121 return ERROR_HTTP_INVALID_HEADER
;
4123 return ERROR_SUCCESS
;
4126 static void HTTP_InsertCookies(http_request_t
*request
)
4128 DWORD cookie_size
, size
, cnt
= 0;
4132 static const WCHAR cookieW
[] = {'C','o','o','k','i','e',':',' ',0};
4134 host
= HTTP_GetHeader(request
, hostW
);
4138 if(!get_cookie(host
->lpszValue
, request
->path
, NULL
, &cookie_size
))
4141 size
= sizeof(cookieW
) + cookie_size
* sizeof(WCHAR
) + sizeof(szCrLf
);
4142 if(!(cookies
= heap_alloc(size
)))
4145 cnt
+= sprintfW(cookies
, cookieW
);
4146 get_cookie(host
->lpszValue
, request
->path
, cookies
+cnt
, &cookie_size
);
4147 strcatW(cookies
, szCrLf
);
4149 HTTP_HttpAddRequestHeadersW(request
, cookies
, strlenW(cookies
), HTTP_ADDREQ_FLAG_REPLACE
);
4154 static WORD
HTTP_ParseWkday(LPCWSTR day
)
4156 static const WCHAR days
[7][4] = {{ 's','u','n',0 },
4164 for (i
= 0; i
< sizeof(days
)/sizeof(*days
); i
++)
4165 if (!strcmpiW(day
, days
[i
]))
4172 static WORD
HTTP_ParseMonth(LPCWSTR month
)
4174 static const WCHAR jan
[] = { 'j','a','n',0 };
4175 static const WCHAR feb
[] = { 'f','e','b',0 };
4176 static const WCHAR mar
[] = { 'm','a','r',0 };
4177 static const WCHAR apr
[] = { 'a','p','r',0 };
4178 static const WCHAR may
[] = { 'm','a','y',0 };
4179 static const WCHAR jun
[] = { 'j','u','n',0 };
4180 static const WCHAR jul
[] = { 'j','u','l',0 };
4181 static const WCHAR aug
[] = { 'a','u','g',0 };
4182 static const WCHAR sep
[] = { 's','e','p',0 };
4183 static const WCHAR oct
[] = { 'o','c','t',0 };
4184 static const WCHAR nov
[] = { 'n','o','v',0 };
4185 static const WCHAR dec
[] = { 'd','e','c',0 };
4187 if (!strcmpiW(month
, jan
)) return 1;
4188 if (!strcmpiW(month
, feb
)) return 2;
4189 if (!strcmpiW(month
, mar
)) return 3;
4190 if (!strcmpiW(month
, apr
)) return 4;
4191 if (!strcmpiW(month
, may
)) return 5;
4192 if (!strcmpiW(month
, jun
)) return 6;
4193 if (!strcmpiW(month
, jul
)) return 7;
4194 if (!strcmpiW(month
, aug
)) return 8;
4195 if (!strcmpiW(month
, sep
)) return 9;
4196 if (!strcmpiW(month
, oct
)) return 10;
4197 if (!strcmpiW(month
, nov
)) return 11;
4198 if (!strcmpiW(month
, dec
)) return 12;
4203 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4204 * optionally preceded by whitespace.
4205 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4206 * st, and sets *str to the first character after the time format.
4208 static BOOL
HTTP_ParseTime(SYSTEMTIME
*st
, LPCWSTR
*str
)
4214 while (isspaceW(*ptr
))
4217 num
= strtoulW(ptr
, &nextPtr
, 10);
4218 if (!nextPtr
|| nextPtr
<= ptr
|| *nextPtr
!= ':')
4220 ERR("unexpected time format %s\n", debugstr_w(ptr
));
4225 ERR("unexpected hour in time format %s\n", debugstr_w(ptr
));
4229 st
->wHour
= (WORD
)num
;
4230 num
= strtoulW(ptr
, &nextPtr
, 10);
4231 if (!nextPtr
|| nextPtr
<= ptr
|| *nextPtr
!= ':')
4233 ERR("unexpected time format %s\n", debugstr_w(ptr
));
4238 ERR("unexpected minute in time format %s\n", debugstr_w(ptr
));
4242 st
->wMinute
= (WORD
)num
;
4243 num
= strtoulW(ptr
, &nextPtr
, 10);
4244 if (!nextPtr
|| nextPtr
<= ptr
)
4246 ERR("unexpected time format %s\n", debugstr_w(ptr
));
4251 ERR("unexpected second in time format %s\n", debugstr_w(ptr
));
4256 st
->wSecond
= (WORD
)num
;
4260 static BOOL
HTTP_ParseDateAsAsctime(LPCWSTR value
, FILETIME
*ft
)
4262 static const WCHAR gmt
[]= { 'G','M','T',0 };
4263 WCHAR day
[4], *dayPtr
, month
[4], *monthPtr
, *nextPtr
;
4265 SYSTEMTIME st
= { 0 };
4268 for (ptr
= value
, dayPtr
= day
; *ptr
&& !isspaceW(*ptr
) &&
4269 dayPtr
- day
< sizeof(day
) / sizeof(day
[0]) - 1; ptr
++, dayPtr
++)
4272 st
.wDayOfWeek
= HTTP_ParseWkday(day
);
4273 if (st
.wDayOfWeek
>= 7)
4275 ERR("unexpected weekday %s\n", debugstr_w(day
));
4279 while (isspaceW(*ptr
))
4282 for (monthPtr
= month
; !isspace(*ptr
) &&
4283 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
4287 st
.wMonth
= HTTP_ParseMonth(month
);
4288 if (!st
.wMonth
|| st
.wMonth
> 12)
4290 ERR("unexpected month %s\n", debugstr_w(month
));
4294 while (isspaceW(*ptr
))
4297 num
= strtoulW(ptr
, &nextPtr
, 10);
4298 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
4300 ERR("unexpected day %s\n", debugstr_w(ptr
));
4304 st
.wDay
= (WORD
)num
;
4306 while (isspaceW(*ptr
))
4309 if (!HTTP_ParseTime(&st
, &ptr
))
4312 while (isspaceW(*ptr
))
4315 num
= strtoulW(ptr
, &nextPtr
, 10);
4316 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
4318 ERR("unexpected year %s\n", debugstr_w(ptr
));
4322 st
.wYear
= (WORD
)num
;
4324 while (isspaceW(*ptr
))
4327 /* asctime() doesn't report a timezone, but some web servers do, so accept
4328 * with or without GMT.
4330 if (*ptr
&& strcmpW(ptr
, gmt
))
4332 ERR("unexpected timezone %s\n", debugstr_w(ptr
));
4335 return SystemTimeToFileTime(&st
, ft
);
4338 static BOOL
HTTP_ParseRfc1123Date(LPCWSTR value
, FILETIME
*ft
)
4340 static const WCHAR gmt
[]= { 'G','M','T',0 };
4341 WCHAR
*nextPtr
, day
[4], month
[4], *monthPtr
;
4344 SYSTEMTIME st
= { 0 };
4346 ptr
= strchrW(value
, ',');
4349 if (ptr
- value
!= 3)
4351 WARN("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4354 memcpy(day
, value
, (ptr
- value
) * sizeof(WCHAR
));
4356 st
.wDayOfWeek
= HTTP_ParseWkday(day
);
4357 if (st
.wDayOfWeek
> 6)
4359 WARN("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4364 while (isspaceW(*ptr
))
4367 num
= strtoulW(ptr
, &nextPtr
, 10);
4368 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
4370 WARN("unexpected day %s\n", debugstr_w(value
));
4374 st
.wDay
= (WORD
)num
;
4376 while (isspaceW(*ptr
))
4379 for (monthPtr
= month
; !isspace(*ptr
) &&
4380 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
4384 st
.wMonth
= HTTP_ParseMonth(month
);
4385 if (!st
.wMonth
|| st
.wMonth
> 12)
4387 WARN("unexpected month %s\n", debugstr_w(month
));
4391 while (isspaceW(*ptr
))
4394 num
= strtoulW(ptr
, &nextPtr
, 10);
4395 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
4397 ERR("unexpected year %s\n", debugstr_w(value
));
4401 st
.wYear
= (WORD
)num
;
4403 if (!HTTP_ParseTime(&st
, &ptr
))
4406 while (isspaceW(*ptr
))
4409 if (strcmpW(ptr
, gmt
))
4411 ERR("unexpected time zone %s\n", debugstr_w(ptr
));
4414 return SystemTimeToFileTime(&st
, ft
);
4417 static WORD
HTTP_ParseWeekday(LPCWSTR day
)
4419 static const WCHAR days
[7][10] = {{ 's','u','n','d','a','y',0 },
4420 { 'm','o','n','d','a','y',0 },
4421 { 't','u','e','s','d','a','y',0 },
4422 { 'w','e','d','n','e','s','d','a','y',0 },
4423 { 't','h','u','r','s','d','a','y',0 },
4424 { 'f','r','i','d','a','y',0 },
4425 { 's','a','t','u','r','d','a','y',0 }};
4427 for (i
= 0; i
< sizeof(days
)/sizeof(*days
); i
++)
4428 if (!strcmpiW(day
, days
[i
]))
4435 static BOOL
HTTP_ParseRfc850Date(LPCWSTR value
, FILETIME
*ft
)
4437 static const WCHAR gmt
[]= { 'G','M','T',0 };
4438 WCHAR
*nextPtr
, day
[10], month
[4], *monthPtr
;
4441 SYSTEMTIME st
= { 0 };
4443 ptr
= strchrW(value
, ',');
4446 if (ptr
- value
== 3)
4448 memcpy(day
, value
, (ptr
- value
) * sizeof(WCHAR
));
4450 st
.wDayOfWeek
= HTTP_ParseWkday(day
);
4451 if (st
.wDayOfWeek
> 6)
4453 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4457 else if (ptr
- value
< sizeof(day
) / sizeof(day
[0]))
4459 memcpy(day
, value
, (ptr
- value
) * sizeof(WCHAR
));
4460 day
[ptr
- value
+ 1] = 0;
4461 st
.wDayOfWeek
= HTTP_ParseWeekday(day
);
4462 if (st
.wDayOfWeek
> 6)
4464 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4470 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4475 while (isspaceW(*ptr
))
4478 num
= strtoulW(ptr
, &nextPtr
, 10);
4479 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
4481 ERR("unexpected day %s\n", debugstr_w(value
));
4485 st
.wDay
= (WORD
)num
;
4489 ERR("unexpected month format %s\n", debugstr_w(ptr
));
4494 for (monthPtr
= month
; *ptr
!= '-' &&
4495 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
4499 st
.wMonth
= HTTP_ParseMonth(month
);
4500 if (!st
.wMonth
|| st
.wMonth
> 12)
4502 ERR("unexpected month %s\n", debugstr_w(month
));
4508 ERR("unexpected year format %s\n", debugstr_w(ptr
));
4513 num
= strtoulW(ptr
, &nextPtr
, 10);
4514 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
4516 ERR("unexpected year %s\n", debugstr_w(value
));
4520 st
.wYear
= (WORD
)num
;
4522 if (!HTTP_ParseTime(&st
, &ptr
))
4525 while (isspaceW(*ptr
))
4528 if (strcmpW(ptr
, gmt
))
4530 ERR("unexpected time zone %s\n", debugstr_w(ptr
));
4533 return SystemTimeToFileTime(&st
, ft
);
4536 static BOOL
HTTP_ParseDate(LPCWSTR value
, FILETIME
*ft
)
4538 static const WCHAR zero
[] = { '0',0 };
4541 if (!strcmpW(value
, zero
))
4543 ft
->dwLowDateTime
= ft
->dwHighDateTime
= 0;
4546 else if (strchrW(value
, ','))
4548 ret
= HTTP_ParseRfc1123Date(value
, ft
);
4551 ret
= HTTP_ParseRfc850Date(value
, ft
);
4553 ERR("unexpected date format %s\n", debugstr_w(value
));
4558 ret
= HTTP_ParseDateAsAsctime(value
, ft
);
4560 ERR("unexpected date format %s\n", debugstr_w(value
));
4565 static void HTTP_ProcessExpires(http_request_t
*request
)
4567 BOOL expirationFound
= FALSE
;
4570 /* Look for a Cache-Control header with a max-age directive, as it takes
4571 * precedence over the Expires header.
4573 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szCache_Control
, 0, FALSE
);
4574 if (headerIndex
!= -1)
4576 LPHTTPHEADERW ccHeader
= &request
->custHeaders
[headerIndex
];
4579 for (ptr
= ccHeader
->lpszValue
; ptr
&& *ptr
; )
4581 LPWSTR comma
= strchrW(ptr
, ','), end
, equal
;
4586 end
= ptr
+ strlenW(ptr
);
4587 for (equal
= end
- 1; equal
> ptr
&& *equal
!= '='; equal
--)
4591 static const WCHAR max_age
[] = {
4592 'm','a','x','-','a','g','e',0 };
4594 if (!strncmpiW(ptr
, max_age
, equal
- ptr
- 1))
4599 age
= strtoulW(equal
+ 1, &nextPtr
, 10);
4600 if (nextPtr
> equal
+ 1)
4604 NtQuerySystemTime( &ft
);
4605 /* Age is in seconds, FILETIME resolution is in
4606 * 100 nanosecond intervals.
4608 ft
.QuadPart
+= age
* (ULONGLONG
)1000000;
4609 request
->expires
.dwLowDateTime
= ft
.u
.LowPart
;
4610 request
->expires
.dwHighDateTime
= ft
.u
.HighPart
;
4611 expirationFound
= TRUE
;
4618 while (isspaceW(*ptr
))
4625 if (!expirationFound
)
4627 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szExpires
, 0, FALSE
);
4628 if (headerIndex
!= -1)
4630 LPHTTPHEADERW expiresHeader
= &request
->custHeaders
[headerIndex
];
4633 if (HTTP_ParseDate(expiresHeader
->lpszValue
, &ft
))
4635 expirationFound
= TRUE
;
4636 request
->expires
= ft
;
4640 if (!expirationFound
)
4644 /* With no known age, default to 10 minutes until expiration. */
4645 NtQuerySystemTime( &t
);
4646 t
.QuadPart
+= 10 * 60 * (ULONGLONG
)10000000;
4647 request
->expires
.dwLowDateTime
= t
.u
.LowPart
;
4648 request
->expires
.dwHighDateTime
= t
.u
.HighPart
;
4652 static void HTTP_ProcessLastModified(http_request_t
*request
)
4656 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szLast_Modified
, 0, FALSE
);
4657 if (headerIndex
!= -1)
4659 LPHTTPHEADERW expiresHeader
= &request
->custHeaders
[headerIndex
];
4662 if (HTTP_ParseDate(expiresHeader
->lpszValue
, &ft
))
4663 request
->last_modified
= ft
;
4667 static void http_process_keep_alive(http_request_t
*req
)
4671 index
= HTTP_GetCustomHeaderIndex(req
, szConnection
, 0, FALSE
);
4673 req
->netconn
->keep_alive
= !strcmpiW(req
->custHeaders
[index
].lpszValue
, szKeepAlive
);
4675 req
->netconn
->keep_alive
= !strcmpiW(req
->version
, g_szHttp1_1
);
4678 static DWORD
open_http_connection(http_request_t
*request
, BOOL
*reusing
)
4680 const BOOL is_https
= (request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) != 0;
4681 netconn_t
*netconn
= NULL
;
4684 assert(!request
->netconn
);
4685 reset_data_stream(request
);
4687 res
= HTTP_ResolveName(request
);
4688 if(res
!= ERROR_SUCCESS
)
4691 EnterCriticalSection(&connection_pool_cs
);
4693 while(!list_empty(&request
->server
->conn_pool
)) {
4694 netconn
= LIST_ENTRY(list_head(&request
->server
->conn_pool
), netconn_t
, pool_entry
);
4695 list_remove(&netconn
->pool_entry
);
4697 if(NETCON_is_alive(netconn
))
4700 TRACE("connection %p closed during idle\n", netconn
);
4701 free_netconn(netconn
);
4705 LeaveCriticalSection(&connection_pool_cs
);
4708 TRACE("<-- reusing %p netconn\n", netconn
);
4709 request
->netconn
= netconn
;
4711 return ERROR_SUCCESS
;
4714 TRACE("connecting to %s, proxy %s\n", debugstr_w(request
->server
->name
),
4715 request
->proxy
? debugstr_w(request
->proxy
->name
) : "(null)");
4717 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4718 INTERNET_STATUS_CONNECTING_TO_SERVER
,
4719 request
->server
->addr_str
,
4720 strlen(request
->server
->addr_str
)+1);
4722 res
= create_netconn(is_https
, request
->proxy
? request
->proxy
: request
->server
, request
->security_flags
,
4723 (request
->hdr
.ErrorMask
& INTERNET_ERROR_MASK_COMBINED_SEC_CERT
) != 0,
4724 request
->connect_timeout
, &netconn
);
4725 if(res
!= ERROR_SUCCESS
) {
4726 ERR("create_netconn failed: %u\n", res
);
4730 request
->netconn
= netconn
;
4732 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4733 INTERNET_STATUS_CONNECTED_TO_SERVER
,
4734 request
->server
->addr_str
, strlen(request
->server
->addr_str
)+1);
4737 /* Note: we differ from Microsoft's WinINet here. they seem to have
4738 * a bug that causes no status callbacks to be sent when starting
4739 * a tunnel to a proxy server using the CONNECT verb. i believe our
4740 * behaviour to be more correct and to not cause any incompatibilities
4741 * because using a secure connection through a proxy server is a rare
4742 * case that would be hard for anyone to depend on */
4744 res
= HTTP_SecureProxyConnect(request
);
4745 if(res
== ERROR_SUCCESS
)
4746 res
= NETCON_secure_connect(request
->netconn
);
4749 if(res
!= ERROR_SUCCESS
) {
4750 http_release_netconn(request
, FALSE
);
4755 TRACE("Created connection to %s: %p\n", debugstr_w(request
->server
->name
), netconn
);
4756 return ERROR_SUCCESS
;
4759 /***********************************************************************
4760 * HTTP_HttpSendRequestW (internal)
4762 * Sends the specified request to the HTTP server
4765 * ERROR_SUCCESS on success
4766 * win32 error code on failure
4769 static DWORD
HTTP_HttpSendRequestW(http_request_t
*request
, LPCWSTR lpszHeaders
,
4770 DWORD dwHeaderLength
, LPVOID lpOptional
, DWORD dwOptionalLength
,
4771 DWORD dwContentLength
, BOOL bEndRequest
)
4774 BOOL redirected
= FALSE
;
4775 LPWSTR requestString
= NULL
;
4778 static const WCHAR szPost
[] = { 'P','O','S','T',0 };
4779 static const WCHAR szContentLength
[] =
4780 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4781 WCHAR contentLengthStr
[sizeof szContentLength
/2 /* includes \r\n */ + 20 /* int */ ];
4784 TRACE("--> %p\n", request
);
4786 assert(request
->hdr
.htype
== WH_HHTTPREQ
);
4788 /* if the verb is NULL default to GET */
4790 request
->verb
= heap_strdupW(szGET
);
4792 if (dwContentLength
|| strcmpW(request
->verb
, szGET
))
4794 sprintfW(contentLengthStr
, szContentLength
, dwContentLength
);
4795 HTTP_HttpAddRequestHeadersW(request
, contentLengthStr
, -1L, HTTP_ADDREQ_FLAG_REPLACE
);
4796 request
->bytesToWrite
= dwContentLength
;
4798 if (request
->session
->appInfo
->agent
)
4800 WCHAR
*agent_header
;
4801 static const WCHAR user_agent
[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4804 len
= strlenW(request
->session
->appInfo
->agent
) + strlenW(user_agent
);
4805 agent_header
= heap_alloc(len
* sizeof(WCHAR
));
4806 sprintfW(agent_header
, user_agent
, request
->session
->appInfo
->agent
);
4808 HTTP_HttpAddRequestHeadersW(request
, agent_header
, strlenW(agent_header
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4809 heap_free(agent_header
);
4811 if (request
->hdr
.dwFlags
& INTERNET_FLAG_PRAGMA_NOCACHE
)
4813 static const WCHAR pragma_nocache
[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4814 HTTP_HttpAddRequestHeadersW(request
, pragma_nocache
, strlenW(pragma_nocache
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4816 if ((request
->hdr
.dwFlags
& INTERNET_FLAG_NO_CACHE_WRITE
) && !strcmpW(request
->verb
, szPost
))
4818 static const WCHAR cache_control
[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4819 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4820 HTTP_HttpAddRequestHeadersW(request
, cache_control
, strlenW(cache_control
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4823 /* add the headers the caller supplied */
4824 if( lpszHeaders
&& dwHeaderLength
)
4825 HTTP_HttpAddRequestHeadersW(request
, lpszHeaders
, dwHeaderLength
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REPLACE
);
4830 BOOL reusing_connection
;
4834 reusing_connection
= request
->netconn
!= NULL
;
4837 request
->contentLength
= ~0u;
4838 request
->bytesToWrite
= 0;
4841 if (TRACE_ON(wininet
))
4843 LPHTTPHEADERW Host
= HTTP_GetHeader(request
, hostW
);
4844 TRACE("Going to url %s %s\n", debugstr_w(Host
->lpszValue
), debugstr_w(request
->path
));
4847 HTTP_FixURL(request
);
4848 if (request
->hdr
.dwFlags
& INTERNET_FLAG_KEEP_CONNECTION
)
4850 HTTP_ProcessHeader(request
, szConnection
, szKeepAlive
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
4852 HTTP_InsertAuthorization(request
, request
->authInfo
, szAuthorization
);
4853 HTTP_InsertAuthorization(request
, request
->proxyAuthInfo
, szProxy_Authorization
);
4855 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
))
4856 HTTP_InsertCookies(request
);
4858 if (request
->session
->appInfo
->proxy
&& request
->session
->appInfo
->proxy
[0])
4860 WCHAR
*url
= HTTP_BuildProxyRequestUrl(request
);
4861 requestString
= HTTP_BuildHeaderRequestString(request
, request
->verb
, url
, request
->version
);
4865 requestString
= HTTP_BuildHeaderRequestString(request
, request
->verb
, request
->path
, request
->version
);
4868 TRACE("Request header -> %s\n", debugstr_w(requestString
) );
4870 if (!reusing_connection
&& (res
= open_http_connection(request
, &reusing_connection
)) != ERROR_SUCCESS
)
4873 /* send the request as ASCII, tack on the optional data */
4874 if (!lpOptional
|| redirected
)
4875 dwOptionalLength
= 0;
4876 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
4877 NULL
, 0, NULL
, NULL
);
4878 ascii_req
= heap_alloc(len
+ dwOptionalLength
);
4879 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
4880 ascii_req
, len
, NULL
, NULL
);
4882 memcpy( &ascii_req
[len
-1], lpOptional
, dwOptionalLength
);
4883 len
= (len
+ dwOptionalLength
- 1);
4885 TRACE("full request -> %s\n", debugstr_a(ascii_req
) );
4887 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4888 INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
4890 NETCON_set_timeout( request
->netconn
, TRUE
, request
->send_timeout
);
4891 res
= NETCON_send(request
->netconn
, ascii_req
, len
, 0, &cnt
);
4892 heap_free( ascii_req
);
4893 if(res
!= ERROR_SUCCESS
) {
4894 TRACE("send failed: %u\n", res
);
4895 if(!reusing_connection
)
4897 http_release_netconn(request
, FALSE
);
4902 request
->bytesWritten
= dwOptionalLength
;
4904 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4905 INTERNET_STATUS_REQUEST_SENT
,
4906 &len
, sizeof(DWORD
));
4912 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4913 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
4915 responseLen
= HTTP_GetResponseHeaders(request
, TRUE
);
4916 /* FIXME: We should know that connection is closed before sending
4917 * headers. Otherwise wrong callbacks are executed */
4918 if(!responseLen
&& reusing_connection
) {
4919 TRACE("Connection closed by server, reconnecting\n");
4920 http_release_netconn(request
, FALSE
);
4925 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4926 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
,
4929 http_process_keep_alive(request
);
4930 HTTP_ProcessCookies(request
);
4931 HTTP_ProcessExpires(request
);
4932 HTTP_ProcessLastModified(request
);
4934 res
= set_content_length(request
);
4935 if(res
!= ERROR_SUCCESS
)
4937 if(!request
->contentLength
)
4938 http_release_netconn(request
, TRUE
);
4940 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
) && responseLen
)
4942 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
4943 dwBufferSize
=sizeof(szNewLocation
);
4944 switch(request
->status_code
) {
4945 case HTTP_STATUS_REDIRECT
:
4946 case HTTP_STATUS_MOVED
:
4947 case HTTP_STATUS_REDIRECT_KEEP_VERB
:
4948 case HTTP_STATUS_REDIRECT_METHOD
:
4949 if(HTTP_HttpQueryInfoW(request
,HTTP_QUERY_LOCATION
,szNewLocation
,&dwBufferSize
,NULL
) != ERROR_SUCCESS
)
4952 if (strcmpW(request
->verb
, szGET
) && strcmpW(request
->verb
, szHEAD
) &&
4953 request
->status_code
!= HTTP_STATUS_REDIRECT_KEEP_VERB
)
4955 heap_free(request
->verb
);
4956 request
->verb
= heap_strdupW(szGET
);
4958 http_release_netconn(request
, drain_content(request
, FALSE
));
4959 if ((new_url
= HTTP_GetRedirectURL( request
, szNewLocation
)))
4961 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
4962 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
4963 res
= HTTP_HandleRedirect(request
, new_url
);
4964 if (res
== ERROR_SUCCESS
)
4966 heap_free(requestString
);
4969 heap_free( new_url
);
4974 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTH
) && res
== ERROR_SUCCESS
)
4976 WCHAR szAuthValue
[2048];
4978 if (request
->status_code
== HTTP_STATUS_DENIED
)
4980 LPHTTPHEADERW Host
= HTTP_GetHeader(request
, hostW
);
4982 while (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_WWW_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
4984 if (HTTP_DoAuthorization(request
, szAuthValue
,
4986 request
->session
->userName
,
4987 request
->session
->password
,
4990 heap_free(requestString
);
4991 if(!drain_content(request
, TRUE
)) {
4992 FIXME("Could not drain content\n");
4993 http_release_netconn(request
, FALSE
);
5001 TRACE("Cleaning wrong authorization data\n");
5002 destroy_authinfo(request
->authInfo
);
5003 request
->authInfo
= NULL
;
5006 if (request
->status_code
== HTTP_STATUS_PROXY_AUTH_REQ
)
5009 while (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_PROXY_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
5011 if (HTTP_DoAuthorization(request
, szAuthValue
,
5012 &request
->proxyAuthInfo
,
5013 request
->session
->appInfo
->proxyUsername
,
5014 request
->session
->appInfo
->proxyPassword
,
5017 if(!drain_content(request
, TRUE
)) {
5018 FIXME("Could not drain content\n");
5019 http_release_netconn(request
, FALSE
);
5027 TRACE("Cleaning wrong proxy authorization data\n");
5028 destroy_authinfo(request
->proxyAuthInfo
);
5029 request
->proxyAuthInfo
= NULL
;
5035 res
= ERROR_SUCCESS
;
5040 heap_free(requestString
);
5042 /* TODO: send notification for P3P header */
5044 if(res
== ERROR_SUCCESS
)
5045 create_cache_entry(request
);
5047 if (request
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
5049 if (res
== ERROR_SUCCESS
) {
5050 if(bEndRequest
&& request
->contentLength
&& request
->bytesWritten
== request
->bytesToWrite
)
5051 HTTP_ReceiveRequestData(request
, TRUE
);
5053 send_request_complete(request
,
5054 request
->session
->hdr
.dwInternalFlags
& INET_OPENURL
? (DWORD_PTR
)request
->hdr
.hInternet
: 1, 0);
5056 send_request_complete(request
, 0, res
);
5064 /***********************************************************************
5066 * Helper functions for the HttpSendRequest(Ex) functions
5069 static void AsyncHttpSendRequestProc(WORKREQUEST
*workRequest
)
5071 struct WORKREQ_HTTPSENDREQUESTW
const *req
= &workRequest
->u
.HttpSendRequestW
;
5072 http_request_t
*request
= (http_request_t
*) workRequest
->hdr
;
5074 TRACE("%p\n", request
);
5076 HTTP_HttpSendRequestW(request
, req
->lpszHeader
,
5077 req
->dwHeaderLength
, req
->lpOptional
, req
->dwOptionalLength
,
5078 req
->dwContentLength
, req
->bEndRequest
);
5080 heap_free(req
->lpszHeader
);
5084 static DWORD
HTTP_HttpEndRequestW(http_request_t
*request
, DWORD dwFlags
, DWORD_PTR dwContext
)
5088 DWORD res
= ERROR_SUCCESS
;
5090 if(!request
->netconn
) {
5091 WARN("Not connected\n");
5092 send_request_complete(request
, 0, ERROR_INTERNET_OPERATION_CANCELLED
);
5093 return ERROR_INTERNET_OPERATION_CANCELLED
;
5096 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
5097 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
5099 responseLen
= HTTP_GetResponseHeaders(request
, TRUE
);
5101 res
= ERROR_HTTP_HEADER_NOT_FOUND
;
5103 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
5104 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
, sizeof(DWORD
));
5106 /* process cookies here. Is this right? */
5107 http_process_keep_alive(request
);
5108 HTTP_ProcessCookies(request
);
5109 HTTP_ProcessExpires(request
);
5110 HTTP_ProcessLastModified(request
);
5112 if ((res
= set_content_length(request
)) == ERROR_SUCCESS
) {
5113 if(!request
->contentLength
)
5114 http_release_netconn(request
, TRUE
);
5117 if (res
== ERROR_SUCCESS
&& !(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
))
5119 switch(request
->status_code
) {
5120 case HTTP_STATUS_REDIRECT
:
5121 case HTTP_STATUS_MOVED
:
5122 case HTTP_STATUS_REDIRECT_METHOD
:
5123 case HTTP_STATUS_REDIRECT_KEEP_VERB
: {
5124 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
5125 dwBufferSize
=sizeof(szNewLocation
);
5126 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_LOCATION
, szNewLocation
, &dwBufferSize
, NULL
) != ERROR_SUCCESS
)
5129 if (strcmpW(request
->verb
, szGET
) && strcmpW(request
->verb
, szHEAD
) &&
5130 request
->status_code
!= HTTP_STATUS_REDIRECT_KEEP_VERB
)
5132 heap_free(request
->verb
);
5133 request
->verb
= heap_strdupW(szGET
);
5135 http_release_netconn(request
, drain_content(request
, FALSE
));
5136 if ((new_url
= HTTP_GetRedirectURL( request
, szNewLocation
)))
5138 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
5139 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
5140 res
= HTTP_HandleRedirect(request
, new_url
);
5141 if (res
== ERROR_SUCCESS
)
5142 res
= HTTP_HttpSendRequestW(request
, NULL
, 0, NULL
, 0, 0, TRUE
);
5143 heap_free( new_url
);
5149 if(res
== ERROR_SUCCESS
)
5150 create_cache_entry(request
);
5152 if (res
== ERROR_SUCCESS
&& request
->contentLength
)
5153 HTTP_ReceiveRequestData(request
, TRUE
);
5155 send_request_complete(request
, res
== ERROR_SUCCESS
, res
);
5160 /***********************************************************************
5161 * HttpEndRequestA (WININET.@)
5163 * Ends an HTTP request that was started by HttpSendRequestEx
5166 * TRUE if successful
5170 BOOL WINAPI
HttpEndRequestA(HINTERNET hRequest
,
5171 LPINTERNET_BUFFERSA lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
5173 TRACE("(%p, %p, %08x, %08lx)\n", hRequest
, lpBuffersOut
, dwFlags
, dwContext
);
5177 SetLastError(ERROR_INVALID_PARAMETER
);
5181 return HttpEndRequestW(hRequest
, NULL
, dwFlags
, dwContext
);
5184 static void AsyncHttpEndRequestProc(WORKREQUEST
*work
)
5186 struct WORKREQ_HTTPENDREQUESTW
const *req
= &work
->u
.HttpEndRequestW
;
5187 http_request_t
*request
= (http_request_t
*)work
->hdr
;
5189 TRACE("%p\n", request
);
5191 HTTP_HttpEndRequestW(request
, req
->dwFlags
, req
->dwContext
);
5194 /***********************************************************************
5195 * HttpEndRequestW (WININET.@)
5197 * Ends an HTTP request that was started by HttpSendRequestEx
5200 * TRUE if successful
5204 BOOL WINAPI
HttpEndRequestW(HINTERNET hRequest
,
5205 LPINTERNET_BUFFERSW lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
5207 http_request_t
*request
;
5210 TRACE("%p %p %x %lx -->\n", hRequest
, lpBuffersOut
, dwFlags
, dwContext
);
5214 SetLastError(ERROR_INVALID_PARAMETER
);
5218 request
= (http_request_t
*) get_handle_object( hRequest
);
5220 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
5222 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
5224 WININET_Release( &request
->hdr
);
5227 request
->hdr
.dwFlags
|= dwFlags
;
5229 if (request
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
5232 struct WORKREQ_HTTPENDREQUESTW
*work_endrequest
;
5234 work
.asyncproc
= AsyncHttpEndRequestProc
;
5235 work
.hdr
= WININET_AddRef( &request
->hdr
);
5237 work_endrequest
= &work
.u
.HttpEndRequestW
;
5238 work_endrequest
->dwFlags
= dwFlags
;
5239 work_endrequest
->dwContext
= dwContext
;
5241 INTERNET_AsyncCall(&work
);
5242 res
= ERROR_IO_PENDING
;
5245 res
= HTTP_HttpEndRequestW(request
, dwFlags
, dwContext
);
5247 WININET_Release( &request
->hdr
);
5248 TRACE("%u <--\n", res
);
5249 if(res
!= ERROR_SUCCESS
)
5251 return res
== ERROR_SUCCESS
;
5254 /***********************************************************************
5255 * HttpSendRequestExA (WININET.@)
5257 * Sends the specified request to the HTTP server and allows chunked
5262 * Failure: FALSE, call GetLastError() for more information.
5264 BOOL WINAPI
HttpSendRequestExA(HINTERNET hRequest
,
5265 LPINTERNET_BUFFERSA lpBuffersIn
,
5266 LPINTERNET_BUFFERSA lpBuffersOut
,
5267 DWORD dwFlags
, DWORD_PTR dwContext
)
5269 INTERNET_BUFFERSW BuffersInW
;
5272 LPWSTR header
= NULL
;
5274 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
5275 lpBuffersOut
, dwFlags
, dwContext
);
5279 BuffersInW
.dwStructSize
= sizeof(LPINTERNET_BUFFERSW
);
5280 if (lpBuffersIn
->lpcszHeader
)
5282 headerlen
= MultiByteToWideChar(CP_ACP
,0,lpBuffersIn
->lpcszHeader
,
5283 lpBuffersIn
->dwHeadersLength
,0,0);
5284 header
= heap_alloc(headerlen
*sizeof(WCHAR
));
5285 if (!(BuffersInW
.lpcszHeader
= header
))
5287 SetLastError(ERROR_OUTOFMEMORY
);
5290 BuffersInW
.dwHeadersLength
= MultiByteToWideChar(CP_ACP
, 0,
5291 lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
5295 BuffersInW
.lpcszHeader
= NULL
;
5296 BuffersInW
.dwHeadersTotal
= lpBuffersIn
->dwHeadersTotal
;
5297 BuffersInW
.lpvBuffer
= lpBuffersIn
->lpvBuffer
;
5298 BuffersInW
.dwBufferLength
= lpBuffersIn
->dwBufferLength
;
5299 BuffersInW
.dwBufferTotal
= lpBuffersIn
->dwBufferTotal
;
5300 BuffersInW
.Next
= NULL
;
5303 rc
= HttpSendRequestExW(hRequest
, lpBuffersIn
? &BuffersInW
: NULL
, NULL
, dwFlags
, dwContext
);
5309 /***********************************************************************
5310 * HttpSendRequestExW (WININET.@)
5312 * Sends the specified request to the HTTP server and allows chunked
5317 * Failure: FALSE, call GetLastError() for more information.
5319 BOOL WINAPI
HttpSendRequestExW(HINTERNET hRequest
,
5320 LPINTERNET_BUFFERSW lpBuffersIn
,
5321 LPINTERNET_BUFFERSW lpBuffersOut
,
5322 DWORD dwFlags
, DWORD_PTR dwContext
)
5324 http_request_t
*request
;
5325 http_session_t
*session
;
5329 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
5330 lpBuffersOut
, dwFlags
, dwContext
);
5332 request
= (http_request_t
*) get_handle_object( hRequest
);
5334 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
5336 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5340 session
= request
->session
;
5341 assert(session
->hdr
.htype
== WH_HHTTPSESSION
);
5342 hIC
= session
->appInfo
;
5343 assert(hIC
->hdr
.htype
== WH_HINIT
);
5345 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
5347 WORKREQUEST workRequest
;
5348 struct WORKREQ_HTTPSENDREQUESTW
*req
;
5350 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
5351 workRequest
.hdr
= WININET_AddRef( &request
->hdr
);
5352 req
= &workRequest
.u
.HttpSendRequestW
;
5357 if (lpBuffersIn
->lpcszHeader
)
5359 if (lpBuffersIn
->dwHeadersLength
== ~0u)
5360 size
= (strlenW( lpBuffersIn
->lpcszHeader
) + 1) * sizeof(WCHAR
);
5362 size
= lpBuffersIn
->dwHeadersLength
* sizeof(WCHAR
);
5364 req
->lpszHeader
= heap_alloc(size
);
5365 memcpy( req
->lpszHeader
, lpBuffersIn
->lpcszHeader
, size
);
5367 else req
->lpszHeader
= NULL
;
5369 req
->dwHeaderLength
= size
/ sizeof(WCHAR
);
5370 req
->lpOptional
= lpBuffersIn
->lpvBuffer
;
5371 req
->dwOptionalLength
= lpBuffersIn
->dwBufferLength
;
5372 req
->dwContentLength
= lpBuffersIn
->dwBufferTotal
;
5376 req
->lpszHeader
= NULL
;
5377 req
->dwHeaderLength
= 0;
5378 req
->lpOptional
= NULL
;
5379 req
->dwOptionalLength
= 0;
5380 req
->dwContentLength
= 0;
5383 req
->bEndRequest
= FALSE
;
5385 INTERNET_AsyncCall(&workRequest
);
5387 * This is from windows.
5389 res
= ERROR_IO_PENDING
;
5394 res
= HTTP_HttpSendRequestW(request
, lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
5395 lpBuffersIn
->lpvBuffer
, lpBuffersIn
->dwBufferLength
,
5396 lpBuffersIn
->dwBufferTotal
, FALSE
);
5398 res
= HTTP_HttpSendRequestW(request
, NULL
, 0, NULL
, 0, 0, FALSE
);
5403 WININET_Release( &request
->hdr
);
5407 return res
== ERROR_SUCCESS
;
5410 /***********************************************************************
5411 * HttpSendRequestW (WININET.@)
5413 * Sends the specified request to the HTTP server
5420 BOOL WINAPI
HttpSendRequestW(HINTERNET hHttpRequest
, LPCWSTR lpszHeaders
,
5421 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
5423 http_request_t
*request
;
5424 http_session_t
*session
= NULL
;
5425 appinfo_t
*hIC
= NULL
;
5426 DWORD res
= ERROR_SUCCESS
;
5428 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest
,
5429 debugstr_wn(lpszHeaders
, dwHeaderLength
), dwHeaderLength
, lpOptional
, dwOptionalLength
);
5431 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
5432 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
5434 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5438 session
= request
->session
;
5439 if (NULL
== session
|| session
->hdr
.htype
!= WH_HHTTPSESSION
)
5441 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5445 hIC
= session
->appInfo
;
5446 if (NULL
== hIC
|| hIC
->hdr
.htype
!= WH_HINIT
)
5448 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5452 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
5454 WORKREQUEST workRequest
;
5455 struct WORKREQ_HTTPSENDREQUESTW
*req
;
5457 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
5458 workRequest
.hdr
= WININET_AddRef( &request
->hdr
);
5459 req
= &workRequest
.u
.HttpSendRequestW
;
5464 if (dwHeaderLength
== ~0u) size
= (strlenW(lpszHeaders
) + 1) * sizeof(WCHAR
);
5465 else size
= dwHeaderLength
* sizeof(WCHAR
);
5467 req
->lpszHeader
= heap_alloc(size
);
5468 memcpy(req
->lpszHeader
, lpszHeaders
, size
);
5471 req
->lpszHeader
= 0;
5472 req
->dwHeaderLength
= dwHeaderLength
;
5473 req
->lpOptional
= lpOptional
;
5474 req
->dwOptionalLength
= dwOptionalLength
;
5475 req
->dwContentLength
= dwOptionalLength
;
5476 req
->bEndRequest
= TRUE
;
5478 INTERNET_AsyncCall(&workRequest
);
5480 * This is from windows.
5482 res
= ERROR_IO_PENDING
;
5486 res
= HTTP_HttpSendRequestW(request
, lpszHeaders
,
5487 dwHeaderLength
, lpOptional
, dwOptionalLength
,
5488 dwOptionalLength
, TRUE
);
5492 WININET_Release( &request
->hdr
);
5495 return res
== ERROR_SUCCESS
;
5498 /***********************************************************************
5499 * HttpSendRequestA (WININET.@)
5501 * Sends the specified request to the HTTP server
5508 BOOL WINAPI
HttpSendRequestA(HINTERNET hHttpRequest
, LPCSTR lpszHeaders
,
5509 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
5512 LPWSTR szHeaders
=NULL
;
5513 DWORD nLen
=dwHeaderLength
;
5514 if(lpszHeaders
!=NULL
)
5516 nLen
=MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,NULL
,0);
5517 szHeaders
= heap_alloc(nLen
*sizeof(WCHAR
));
5518 MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,szHeaders
,nLen
);
5520 result
= HttpSendRequestW(hHttpRequest
, szHeaders
, nLen
, lpOptional
, dwOptionalLength
);
5521 heap_free(szHeaders
);
5525 /***********************************************************************
5526 * HTTPSESSION_Destroy (internal)
5528 * Deallocate session handle
5531 static void HTTPSESSION_Destroy(object_header_t
*hdr
)
5533 http_session_t
*session
= (http_session_t
*) hdr
;
5535 TRACE("%p\n", session
);
5537 WININET_Release(&session
->appInfo
->hdr
);
5539 heap_free(session
->hostName
);
5540 heap_free(session
->password
);
5541 heap_free(session
->userName
);
5544 static DWORD
HTTPSESSION_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
5546 http_session_t
*ses
= (http_session_t
*)hdr
;
5549 case INTERNET_OPTION_HANDLE_TYPE
:
5550 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5552 if (*size
< sizeof(ULONG
))
5553 return ERROR_INSUFFICIENT_BUFFER
;
5555 *size
= sizeof(DWORD
);
5556 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_CONNECT_HTTP
;
5557 return ERROR_SUCCESS
;
5558 case INTERNET_OPTION_CONNECT_TIMEOUT
:
5559 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5561 if (*size
< sizeof(DWORD
))
5562 return ERROR_INSUFFICIENT_BUFFER
;
5564 *size
= sizeof(DWORD
);
5565 *(DWORD
*)buffer
= ses
->connect_timeout
;
5566 return ERROR_SUCCESS
;
5568 case INTERNET_OPTION_SEND_TIMEOUT
:
5569 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5571 if (*size
< sizeof(DWORD
))
5572 return ERROR_INSUFFICIENT_BUFFER
;
5574 *size
= sizeof(DWORD
);
5575 *(DWORD
*)buffer
= ses
->send_timeout
;
5576 return ERROR_SUCCESS
;
5578 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
5579 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5581 if (*size
< sizeof(DWORD
))
5582 return ERROR_INSUFFICIENT_BUFFER
;
5584 *size
= sizeof(DWORD
);
5585 *(DWORD
*)buffer
= ses
->receive_timeout
;
5586 return ERROR_SUCCESS
;
5589 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
5592 static DWORD
HTTPSESSION_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
5594 http_session_t
*ses
= (http_session_t
*)hdr
;
5597 case INTERNET_OPTION_USERNAME
:
5599 heap_free(ses
->userName
);
5600 if (!(ses
->userName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
5601 return ERROR_SUCCESS
;
5603 case INTERNET_OPTION_PASSWORD
:
5605 heap_free(ses
->password
);
5606 if (!(ses
->password
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
5607 return ERROR_SUCCESS
;
5609 case INTERNET_OPTION_CONNECT_TIMEOUT
:
5611 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
5612 ses
->connect_timeout
= *(DWORD
*)buffer
;
5613 return ERROR_SUCCESS
;
5615 case INTERNET_OPTION_SEND_TIMEOUT
:
5617 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
5618 ses
->send_timeout
= *(DWORD
*)buffer
;
5619 return ERROR_SUCCESS
;
5621 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
5623 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
5624 ses
->receive_timeout
= *(DWORD
*)buffer
;
5625 return ERROR_SUCCESS
;
5630 return INET_SetOption(hdr
, option
, buffer
, size
);
5633 static const object_vtbl_t HTTPSESSIONVtbl
= {
5634 HTTPSESSION_Destroy
,
5636 HTTPSESSION_QueryOption
,
5637 HTTPSESSION_SetOption
,
5646 /***********************************************************************
5647 * HTTP_Connect (internal)
5649 * Create http session handle
5652 * HINTERNET a session handle on success
5656 DWORD
HTTP_Connect(appinfo_t
*hIC
, LPCWSTR lpszServerName
,
5657 INTERNET_PORT serverPort
, LPCWSTR lpszUserName
,
5658 LPCWSTR lpszPassword
, DWORD dwFlags
, DWORD_PTR dwContext
,
5659 DWORD dwInternalFlags
, HINTERNET
*ret
)
5661 http_session_t
*session
= NULL
;
5665 if (!lpszServerName
|| !lpszServerName
[0])
5666 return ERROR_INVALID_PARAMETER
;
5668 assert( hIC
->hdr
.htype
== WH_HINIT
);
5670 session
= alloc_object(&hIC
->hdr
, &HTTPSESSIONVtbl
, sizeof(http_session_t
));
5672 return ERROR_OUTOFMEMORY
;
5675 * According to my tests. The name is not resolved until a request is sent
5678 session
->hdr
.htype
= WH_HHTTPSESSION
;
5679 session
->hdr
.dwFlags
= dwFlags
;
5680 session
->hdr
.dwContext
= dwContext
;
5681 session
->hdr
.dwInternalFlags
|= dwInternalFlags
;
5683 WININET_AddRef( &hIC
->hdr
);
5684 session
->appInfo
= hIC
;
5685 list_add_head( &hIC
->hdr
.children
, &session
->hdr
.entry
);
5687 if(hIC
->proxy
&& hIC
->accessType
== INTERNET_OPEN_TYPE_PROXY
) {
5688 if(hIC
->proxyBypass
)
5689 FIXME("Proxy bypass is ignored.\n");
5691 session
->hostName
= heap_strdupW(lpszServerName
);
5692 if (lpszUserName
&& lpszUserName
[0])
5693 session
->userName
= heap_strdupW(lpszUserName
);
5694 if (lpszPassword
&& lpszPassword
[0])
5695 session
->password
= heap_strdupW(lpszPassword
);
5696 session
->hostPort
= serverPort
;
5697 session
->connect_timeout
= hIC
->connect_timeout
;
5698 session
->send_timeout
= INFINITE
;
5699 session
->receive_timeout
= INFINITE
;
5701 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5702 if (!(session
->hdr
.dwInternalFlags
& INET_OPENURL
))
5704 INTERNET_SendCallback(&hIC
->hdr
, dwContext
,
5705 INTERNET_STATUS_HANDLE_CREATED
, &session
->hdr
.hInternet
,
5710 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5714 TRACE("%p --> %p\n", hIC
, session
);
5716 *ret
= session
->hdr
.hInternet
;
5717 return ERROR_SUCCESS
;
5720 /***********************************************************************
5721 * HTTP_clear_response_headers (internal)
5723 * clear out any old response headers
5725 static void HTTP_clear_response_headers( http_request_t
*request
)
5729 for( i
=0; i
<request
->nCustHeaders
; i
++)
5731 if( !request
->custHeaders
[i
].lpszField
)
5733 if( !request
->custHeaders
[i
].lpszValue
)
5735 if ( request
->custHeaders
[i
].wFlags
& HDR_ISREQUEST
)
5737 HTTP_DeleteCustomHeader( request
, i
);
5742 /***********************************************************************
5743 * HTTP_GetResponseHeaders (internal)
5745 * Read server response
5752 static INT
HTTP_GetResponseHeaders(http_request_t
*request
, BOOL clear
)
5755 WCHAR buffer
[MAX_REPLY_LEN
];
5756 DWORD buflen
= MAX_REPLY_LEN
;
5757 BOOL bSuccess
= FALSE
;
5759 char bufferA
[MAX_REPLY_LEN
];
5760 LPWSTR status_code
= NULL
, status_text
= NULL
;
5761 DWORD cchMaxRawHeaders
= 1024;
5762 LPWSTR lpszRawHeaders
= NULL
;
5764 DWORD cchRawHeaders
= 0;
5765 BOOL codeHundred
= FALSE
;
5769 if(!request
->netconn
)
5772 NETCON_set_timeout( request
->netconn
, FALSE
, request
->receive_timeout
);
5774 static const WCHAR szHundred
[] = {'1','0','0',0};
5776 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5778 buflen
= MAX_REPLY_LEN
;
5779 if (!read_line(request
, bufferA
, &buflen
))
5782 /* clear old response headers (eg. from a redirect response) */
5784 HTTP_clear_response_headers( request
);
5789 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
5790 /* check is this a status code line? */
5791 if (!strncmpW(buffer
, g_szHttp1_0
, 4))
5793 /* split the version from the status code */
5794 status_code
= strchrW( buffer
, ' ' );
5799 /* split the status code from the status text */
5800 status_text
= strchrW( status_code
, ' ' );
5805 request
->status_code
= atoiW(status_code
);
5807 TRACE("version [%s] status code [%s] status text [%s]\n",
5808 debugstr_w(buffer
), debugstr_w(status_code
), debugstr_w(status_text
) );
5810 codeHundred
= (!strcmpW(status_code
, szHundred
));
5812 else if (!codeHundred
)
5814 WARN("No status line at head of response (%s)\n", debugstr_w(buffer
));
5816 heap_free(request
->version
);
5817 heap_free(request
->statusText
);
5819 request
->status_code
= HTTP_STATUS_OK
;
5820 request
->version
= heap_strdupW(g_szHttp1_0
);
5821 request
->statusText
= heap_strdupW(szOK
);
5823 heap_free(request
->rawHeaders
);
5824 request
->rawHeaders
= heap_strdupW(szDefaultHeader
);
5829 } while (codeHundred
);
5831 /* Add status code */
5832 HTTP_ProcessHeader(request
, szStatus
, status_code
,
5833 HTTP_ADDHDR_FLAG_REPLACE
);
5835 heap_free(request
->version
);
5836 heap_free(request
->statusText
);
5838 request
->version
= heap_strdupW(buffer
);
5839 request
->statusText
= heap_strdupW(status_text
);
5841 /* Restore the spaces */
5842 *(status_code
-1) = ' ';
5843 *(status_text
-1) = ' ';
5845 /* regenerate raw headers */
5846 lpszRawHeaders
= heap_alloc((cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
5847 if (!lpszRawHeaders
) goto lend
;
5849 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5850 cchMaxRawHeaders
*= 2;
5851 temp
= heap_realloc(lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
5852 if (temp
== NULL
) goto lend
;
5853 lpszRawHeaders
= temp
;
5854 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
5855 cchRawHeaders
+= (buflen
-1);
5856 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
5857 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
5858 lpszRawHeaders
[cchRawHeaders
] = '\0';
5860 /* Parse each response line */
5863 buflen
= MAX_REPLY_LEN
;
5864 if (read_line(request
, bufferA
, &buflen
))
5866 LPWSTR
* pFieldAndValue
;
5868 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA
));
5870 if (!bufferA
[0]) break;
5871 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
5873 pFieldAndValue
= HTTP_InterpretHttpHeader(buffer
);
5876 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5877 cchMaxRawHeaders
*= 2;
5878 temp
= heap_realloc(lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
5879 if (temp
== NULL
) goto lend
;
5880 lpszRawHeaders
= temp
;
5881 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
5882 cchRawHeaders
+= (buflen
-1);
5883 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
5884 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
5885 lpszRawHeaders
[cchRawHeaders
] = '\0';
5887 HTTP_ProcessHeader(request
, pFieldAndValue
[0], pFieldAndValue
[1],
5888 HTTP_ADDREQ_FLAG_ADD
);
5890 HTTP_FreeTokens(pFieldAndValue
);
5901 /* make sure the response header is terminated with an empty line. Some apps really
5902 truly care about that empty line being there for some reason. Just add it to the
5904 if (cchRawHeaders
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5906 cchMaxRawHeaders
= cchRawHeaders
+ strlenW(szCrLf
);
5907 temp
= heap_realloc(lpszRawHeaders
, (cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
5908 if (temp
== NULL
) goto lend
;
5909 lpszRawHeaders
= temp
;
5912 memcpy(&lpszRawHeaders
[cchRawHeaders
], szCrLf
, sizeof(szCrLf
));
5914 heap_free(request
->rawHeaders
);
5915 request
->rawHeaders
= lpszRawHeaders
;
5916 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders
));
5926 heap_free(lpszRawHeaders
);
5931 /***********************************************************************
5932 * HTTP_InterpretHttpHeader (internal)
5934 * Parse server response
5938 * Pointer to array of field, value, NULL on success.
5941 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
)
5943 LPWSTR
* pTokenPair
;
5947 pTokenPair
= heap_alloc_zero(sizeof(*pTokenPair
)*3);
5949 pszColon
= strchrW(buffer
, ':');
5950 /* must have two tokens */
5953 HTTP_FreeTokens(pTokenPair
);
5955 TRACE("No ':' in line: %s\n", debugstr_w(buffer
));
5959 pTokenPair
[0] = heap_alloc((pszColon
- buffer
+ 1) * sizeof(WCHAR
));
5962 HTTP_FreeTokens(pTokenPair
);
5965 memcpy(pTokenPair
[0], buffer
, (pszColon
- buffer
) * sizeof(WCHAR
));
5966 pTokenPair
[0][pszColon
- buffer
] = '\0';
5970 len
= strlenW(pszColon
);
5971 pTokenPair
[1] = heap_alloc((len
+ 1) * sizeof(WCHAR
));
5974 HTTP_FreeTokens(pTokenPair
);
5977 memcpy(pTokenPair
[1], pszColon
, (len
+ 1) * sizeof(WCHAR
));
5979 strip_spaces(pTokenPair
[0]);
5980 strip_spaces(pTokenPair
[1]);
5982 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair
[0]), debugstr_w(pTokenPair
[1]));
5986 /***********************************************************************
5987 * HTTP_ProcessHeader (internal)
5989 * Stuff header into header tables according to <dwModifier>
5993 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5995 static DWORD
HTTP_ProcessHeader(http_request_t
*request
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
)
5997 LPHTTPHEADERW lphttpHdr
= NULL
;
5999 BOOL request_only
= dwModifier
& HTTP_ADDHDR_FLAG_REQ
;
6000 DWORD res
= ERROR_HTTP_INVALID_HEADER
;
6002 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field
), debugstr_w(value
), dwModifier
);
6004 /* REPLACE wins out over ADD */
6005 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
6006 dwModifier
&= ~HTTP_ADDHDR_FLAG_ADD
;
6008 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD
)
6011 index
= HTTP_GetCustomHeaderIndex(request
, field
, 0, request_only
);
6015 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD_IF_NEW
)
6016 return ERROR_HTTP_INVALID_HEADER
;
6017 lphttpHdr
= &request
->custHeaders
[index
];
6023 hdr
.lpszField
= (LPWSTR
)field
;
6024 hdr
.lpszValue
= (LPWSTR
)value
;
6025 hdr
.wFlags
= hdr
.wCount
= 0;
6027 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
6028 hdr
.wFlags
|= HDR_ISREQUEST
;
6030 return HTTP_InsertCustomHeader(request
, &hdr
);
6032 /* no value to delete */
6033 else return ERROR_SUCCESS
;
6035 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
6036 lphttpHdr
->wFlags
|= HDR_ISREQUEST
;
6038 lphttpHdr
->wFlags
&= ~HDR_ISREQUEST
;
6040 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
6042 HTTP_DeleteCustomHeader( request
, index
);
6048 hdr
.lpszField
= (LPWSTR
)field
;
6049 hdr
.lpszValue
= (LPWSTR
)value
;
6050 hdr
.wFlags
= hdr
.wCount
= 0;
6052 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
6053 hdr
.wFlags
|= HDR_ISREQUEST
;
6055 return HTTP_InsertCustomHeader(request
, &hdr
);
6058 return ERROR_SUCCESS
;
6060 else if (dwModifier
& COALESCEFLAGS
)
6065 INT origlen
= strlenW(lphttpHdr
->lpszValue
);
6066 INT valuelen
= strlenW(value
);
6068 if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
)
6071 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
6073 else if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON
)
6076 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
6079 len
= origlen
+ valuelen
+ ((ch
> 0) ? 2 : 0);
6081 lpsztmp
= heap_realloc(lphttpHdr
->lpszValue
, (len
+1)*sizeof(WCHAR
));
6084 lphttpHdr
->lpszValue
= lpsztmp
;
6085 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
6088 lphttpHdr
->lpszValue
[origlen
] = ch
;
6090 lphttpHdr
->lpszValue
[origlen
] = ' ';
6094 memcpy(&lphttpHdr
->lpszValue
[origlen
], value
, valuelen
*sizeof(WCHAR
));
6095 lphttpHdr
->lpszValue
[len
] = '\0';
6096 res
= ERROR_SUCCESS
;
6100 WARN("heap_realloc (%d bytes) failed\n",len
+1);
6101 res
= ERROR_OUTOFMEMORY
;
6104 TRACE("<-- %d\n", res
);
6108 /***********************************************************************
6109 * HTTP_GetCustomHeaderIndex (internal)
6111 * Return index of custom header from header array
6114 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*request
, LPCWSTR lpszField
,
6115 int requested_index
, BOOL request_only
)
6119 TRACE("%s, %d, %d\n", debugstr_w(lpszField
), requested_index
, request_only
);
6121 for (index
= 0; index
< request
->nCustHeaders
; index
++)
6123 if (strcmpiW(request
->custHeaders
[index
].lpszField
, lpszField
))
6126 if (request_only
&& !(request
->custHeaders
[index
].wFlags
& HDR_ISREQUEST
))
6129 if (!request_only
&& (request
->custHeaders
[index
].wFlags
& HDR_ISREQUEST
))
6132 if (requested_index
== 0)
6137 if (index
>= request
->nCustHeaders
)
6140 TRACE("Return: %d\n", index
);
6145 /***********************************************************************
6146 * HTTP_InsertCustomHeader (internal)
6148 * Insert header into array
6151 static DWORD
HTTP_InsertCustomHeader(http_request_t
*request
, LPHTTPHEADERW lpHdr
)
6154 LPHTTPHEADERW lph
= NULL
;
6156 TRACE("--> %s: %s\n", debugstr_w(lpHdr
->lpszField
), debugstr_w(lpHdr
->lpszValue
));
6157 count
= request
->nCustHeaders
+ 1;
6159 lph
= heap_realloc_zero(request
->custHeaders
, sizeof(HTTPHEADERW
) * count
);
6161 lph
= heap_alloc_zero(sizeof(HTTPHEADERW
) * count
);
6164 return ERROR_OUTOFMEMORY
;
6166 request
->custHeaders
= lph
;
6167 request
->custHeaders
[count
-1].lpszField
= heap_strdupW(lpHdr
->lpszField
);
6168 request
->custHeaders
[count
-1].lpszValue
= heap_strdupW(lpHdr
->lpszValue
);
6169 request
->custHeaders
[count
-1].wFlags
= lpHdr
->wFlags
;
6170 request
->custHeaders
[count
-1].wCount
= lpHdr
->wCount
;
6171 request
->nCustHeaders
++;
6173 return ERROR_SUCCESS
;
6177 /***********************************************************************
6178 * HTTP_DeleteCustomHeader (internal)
6180 * Delete header from array
6181 * If this function is called, the indexs may change.
6183 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*request
, DWORD index
)
6185 if( request
->nCustHeaders
<= 0 )
6187 if( index
>= request
->nCustHeaders
)
6189 request
->nCustHeaders
--;
6191 heap_free(request
->custHeaders
[index
].lpszField
);
6192 heap_free(request
->custHeaders
[index
].lpszValue
);
6194 memmove( &request
->custHeaders
[index
], &request
->custHeaders
[index
+1],
6195 (request
->nCustHeaders
- index
)* sizeof(HTTPHEADERW
) );
6196 memset( &request
->custHeaders
[request
->nCustHeaders
], 0, sizeof(HTTPHEADERW
) );
6202 /***********************************************************************
6203 * HTTP_VerifyValidHeader (internal)
6205 * Verify the given header is not invalid for the given http request
6208 static BOOL
HTTP_VerifyValidHeader(http_request_t
*request
, LPCWSTR field
)
6210 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6211 if (!strcmpW(request
->version
, g_szHttp1_0
) && !strcmpiW(field
, szAccept_Encoding
))
6212 return ERROR_HTTP_INVALID_HEADER
;
6214 return ERROR_SUCCESS
;
6217 /***********************************************************************
6218 * IsHostInProxyBypassList (@)
6223 BOOL WINAPI
IsHostInProxyBypassList(DWORD flags
, LPCSTR szHost
, DWORD length
)
6225 FIXME("STUB: flags=%d host=%s length=%d\n",flags
,szHost
,length
);
6229 /***********************************************************************
6230 * InternetShowSecurityInfoByURLA (@)
6232 BOOL WINAPI
InternetShowSecurityInfoByURLA(LPCSTR url
, HWND window
)
6234 FIXME("stub: %s %p\n", url
, window
);
6238 /***********************************************************************
6239 * InternetShowSecurityInfoByURLW (@)
6241 BOOL WINAPI
InternetShowSecurityInfoByURLW(LPCWSTR url
, HWND window
)
6243 FIXME("stub: %s %p\n", debugstr_w(url
), window
);
6247 /***********************************************************************
6248 * ShowX509EncodedCertificate (@)
6250 DWORD WINAPI
ShowX509EncodedCertificate(HWND parent
, LPBYTE cert
, DWORD len
)
6252 PCCERT_CONTEXT certContext
= CertCreateCertificateContext(X509_ASN_ENCODING
,
6258 CRYPTUI_VIEWCERTIFICATE_STRUCTW view
;
6260 memset(&view
, 0, sizeof(view
));
6261 view
.hwndParent
= parent
;
6262 view
.pCertContext
= certContext
;
6263 if (CryptUIDlgViewCertificateW(&view
, NULL
))
6264 ret
= ERROR_SUCCESS
;
6266 ret
= GetLastError();
6267 CertFreeCertificateContext(certContext
);
6270 ret
= GetLastError();