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
);
223 static CRITICAL_SECTION connection_pool_cs
;
224 static CRITICAL_SECTION_DEBUG connection_pool_debug
=
226 0, 0, &connection_pool_cs
,
227 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
228 0, 0, { (DWORD_PTR
)(__FILE__
": connection_pool_cs") }
230 static CRITICAL_SECTION connection_pool_cs
= { &connection_pool_debug
, -1, 0, 0, 0, 0 };
232 static struct list connection_pool
= LIST_INIT(connection_pool
);
233 static BOOL collector_running
;
235 void server_addref(server_t
*server
)
237 InterlockedIncrement(&server
->ref
);
240 void server_release(server_t
*server
)
242 if(InterlockedDecrement(&server
->ref
))
245 list_remove(&server
->entry
);
247 heap_free(server
->name
);
251 static server_t
*get_server(const WCHAR
*name
, INTERNET_PORT port
)
253 server_t
*iter
, *server
= NULL
;
255 EnterCriticalSection(&connection_pool_cs
);
257 LIST_FOR_EACH_ENTRY(iter
, &connection_pool
, server_t
, entry
) {
258 if(iter
->port
== port
&& !strcmpW(iter
->name
, name
)) {
260 server_addref(server
);
266 server
= heap_alloc(sizeof(*server
));
268 server
->addr_len
= 0;
269 server
->ref
= 2; /* list reference and return */
271 server
->security_flags
= 0;
272 list_init(&server
->conn_pool
);
273 server
->name
= heap_strdupW(name
);
275 list_add_head(&connection_pool
, &server
->entry
);
283 LeaveCriticalSection(&connection_pool_cs
);
288 BOOL
collect_connections(collect_type_t collect_type
)
290 netconn_t
*netconn
, *netconn_safe
;
291 server_t
*server
, *server_safe
;
292 BOOL remaining
= FALSE
;
295 now
= GetTickCount64();
297 LIST_FOR_EACH_ENTRY_SAFE(server
, server_safe
, &connection_pool
, server_t
, entry
) {
298 LIST_FOR_EACH_ENTRY_SAFE(netconn
, netconn_safe
, &server
->conn_pool
, netconn_t
, pool_entry
) {
299 if(collect_type
> COLLECT_TIMEOUT
|| netconn
->keep_until
< now
) {
300 TRACE("freeing %p\n", netconn
);
301 list_remove(&netconn
->pool_entry
);
302 free_netconn(netconn
);
308 if(collect_type
== COLLECT_CLEANUP
) {
309 list_remove(&server
->entry
);
310 list_init(&server
->entry
);
311 server_release(server
);
318 static DWORD WINAPI
collect_connections_proc(void *arg
)
320 BOOL remaining_conns
;
323 /* FIXME: Use more sophisticated method */
326 EnterCriticalSection(&connection_pool_cs
);
328 remaining_conns
= collect_connections(COLLECT_TIMEOUT
);
330 collector_running
= FALSE
;
332 LeaveCriticalSection(&connection_pool_cs
);
333 }while(remaining_conns
);
335 FreeLibraryAndExitThread(WININET_hModule
, 0);
338 static LPHTTPHEADERW
HTTP_GetHeader(http_request_t
*req
, LPCWSTR head
)
341 HeaderIndex
= HTTP_GetCustomHeaderIndex(req
, head
, 0, TRUE
);
342 if (HeaderIndex
== -1)
345 return &req
->custHeaders
[HeaderIndex
];
354 struct data_stream_vtbl_t
{
355 DWORD (*get_avail_data
)(data_stream_t
*,http_request_t
*);
356 BOOL (*end_of_data
)(data_stream_t
*,http_request_t
*);
357 DWORD (*read
)(data_stream_t
*,http_request_t
*,BYTE
*,DWORD
,DWORD
*,read_mode_t
);
358 BOOL (*drain_content
)(data_stream_t
*,http_request_t
*);
359 void (*destroy
)(data_stream_t
*);
363 data_stream_t data_stream
;
365 BYTE buf
[READ_BUFFER_SIZE
];
371 static inline void destroy_data_stream(data_stream_t
*stream
)
373 stream
->vtbl
->destroy(stream
);
376 static void reset_data_stream(http_request_t
*req
)
378 destroy_data_stream(req
->data_stream
);
379 req
->data_stream
= &req
->netconn_stream
.data_stream
;
380 req
->read_pos
= req
->read_size
= req
->netconn_stream
.content_read
= 0;
381 req
->read_chunked
= req
->read_gzip
= FALSE
;
387 data_stream_t stream
;
388 data_stream_t
*parent_stream
;
390 BYTE buf
[READ_BUFFER_SIZE
];
396 static DWORD
gzip_get_avail_data(data_stream_t
*stream
, http_request_t
*req
)
398 /* Allow reading only from read buffer */
402 static BOOL
gzip_end_of_data(data_stream_t
*stream
, http_request_t
*req
)
404 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
405 return gzip_stream
->end_of_data
;
408 static DWORD
gzip_read(data_stream_t
*stream
, http_request_t
*req
, BYTE
*buf
, DWORD size
,
409 DWORD
*read
, read_mode_t read_mode
)
411 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
412 z_stream
*zstream
= &gzip_stream
->zstream
;
413 DWORD current_read
, ret_read
= 0;
416 DWORD res
= ERROR_SUCCESS
;
418 while(size
&& !gzip_stream
->end_of_data
) {
419 end
= gzip_stream
->parent_stream
->vtbl
->end_of_data(gzip_stream
->parent_stream
, req
);
421 if(gzip_stream
->buf_size
<= 64 && !end
) {
422 if(gzip_stream
->buf_pos
) {
423 if(gzip_stream
->buf_size
)
424 memmove(gzip_stream
->buf
, gzip_stream
->buf
+gzip_stream
->buf_pos
, gzip_stream
->buf_size
);
425 gzip_stream
->buf_pos
= 0;
427 res
= gzip_stream
->parent_stream
->vtbl
->read(gzip_stream
->parent_stream
, req
, gzip_stream
->buf
+gzip_stream
->buf_size
,
428 sizeof(gzip_stream
->buf
)-gzip_stream
->buf_size
, ¤t_read
, read_mode
);
429 gzip_stream
->buf_size
+= current_read
;
430 if(res
!= ERROR_SUCCESS
)
432 end
= gzip_stream
->parent_stream
->vtbl
->end_of_data(gzip_stream
->parent_stream
, req
);
433 if(!current_read
&& !end
) {
434 if(read_mode
!= READMODE_NOBLOCK
) {
435 WARN("unexpected end of data\n");
436 gzip_stream
->end_of_data
= TRUE
;
440 if(gzip_stream
->buf_size
<= 64 && !end
)
444 zstream
->next_in
= gzip_stream
->buf
+gzip_stream
->buf_pos
;
445 zstream
->avail_in
= gzip_stream
->buf_size
-(end
? 0 : 64);
446 zstream
->next_out
= buf
+ret_read
;
447 zstream
->avail_out
= size
;
448 zres
= inflate(&gzip_stream
->zstream
, 0);
449 current_read
= size
- zstream
->avail_out
;
450 size
-= current_read
;
451 ret_read
+= current_read
;
452 gzip_stream
->buf_size
-= zstream
->next_in
- (gzip_stream
->buf
+gzip_stream
->buf_pos
);
453 gzip_stream
->buf_pos
= zstream
->next_in
-gzip_stream
->buf
;
454 if(zres
== Z_STREAM_END
) {
455 TRACE("end of data\n");
456 gzip_stream
->end_of_data
= TRUE
;
458 }else if(zres
!= Z_OK
) {
459 WARN("inflate failed %d: %s\n", zres
, debugstr_a(zstream
->msg
));
461 res
= ERROR_INTERNET_DECODING_FAILED
;
465 if(ret_read
&& read_mode
== READMODE_ASYNC
)
466 read_mode
= READMODE_NOBLOCK
;
469 TRACE("read %u bytes\n", ret_read
);
474 static BOOL
gzip_drain_content(data_stream_t
*stream
, http_request_t
*req
)
476 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
477 return gzip_stream
->parent_stream
->vtbl
->drain_content(gzip_stream
->parent_stream
, req
);
480 static void gzip_destroy(data_stream_t
*stream
)
482 gzip_stream_t
*gzip_stream
= (gzip_stream_t
*)stream
;
484 destroy_data_stream(gzip_stream
->parent_stream
);
486 if(!gzip_stream
->end_of_data
)
487 inflateEnd(&gzip_stream
->zstream
);
488 heap_free(gzip_stream
);
491 static const data_stream_vtbl_t gzip_stream_vtbl
= {
499 static voidpf
wininet_zalloc(voidpf opaque
, uInt items
, uInt size
)
501 return heap_alloc(items
*size
);
504 static void wininet_zfree(voidpf opaque
, voidpf address
)
509 static DWORD
init_gzip_stream(http_request_t
*req
)
511 gzip_stream_t
*gzip_stream
;
514 gzip_stream
= heap_alloc_zero(sizeof(gzip_stream_t
));
516 return ERROR_OUTOFMEMORY
;
518 gzip_stream
->stream
.vtbl
= &gzip_stream_vtbl
;
519 gzip_stream
->zstream
.zalloc
= wininet_zalloc
;
520 gzip_stream
->zstream
.zfree
= wininet_zfree
;
522 zres
= inflateInit2(&gzip_stream
->zstream
, 0x1f);
524 ERR("inflateInit failed: %d\n", zres
);
525 heap_free(gzip_stream
);
526 return ERROR_OUTOFMEMORY
;
529 index
= HTTP_GetCustomHeaderIndex(req
, szContent_Length
, 0, FALSE
);
531 HTTP_DeleteCustomHeader(req
, index
);
534 memcpy(gzip_stream
->buf
, req
->read_buf
+req
->read_pos
, req
->read_size
);
535 gzip_stream
->buf_size
= req
->read_size
;
536 req
->read_pos
= req
->read_size
= 0;
539 req
->read_gzip
= TRUE
;
540 gzip_stream
->parent_stream
= req
->data_stream
;
541 req
->data_stream
= &gzip_stream
->stream
;
542 return ERROR_SUCCESS
;
547 static DWORD
init_gzip_stream(http_request_t
*req
)
549 ERR("gzip stream not supported, missing zlib.\n");
550 return ERROR_SUCCESS
;
555 /***********************************************************************
556 * HTTP_Tokenize (internal)
558 * Tokenize a string, allocating memory for the tokens.
560 static LPWSTR
* HTTP_Tokenize(LPCWSTR string
, LPCWSTR token_string
)
562 LPWSTR
* token_array
;
569 /* empty string has no tokens */
573 for (i
= 0; string
[i
]; i
++)
575 if (!strncmpW(string
+i
, token_string
, strlenW(token_string
)))
579 /* we want to skip over separators, but not the null terminator */
580 for (j
= 0; j
< strlenW(token_string
) - 1; j
++)
588 /* add 1 for terminating NULL */
589 token_array
= heap_alloc((tokens
+1) * sizeof(*token_array
));
590 token_array
[tokens
] = NULL
;
593 for (i
= 0; i
< tokens
; i
++)
596 next_token
= strstrW(string
, token_string
);
597 if (!next_token
) next_token
= string
+strlenW(string
);
598 len
= next_token
- string
;
599 token_array
[i
] = heap_alloc((len
+1)*sizeof(WCHAR
));
600 memcpy(token_array
[i
], string
, len
*sizeof(WCHAR
));
601 token_array
[i
][len
] = '\0';
602 string
= next_token
+strlenW(token_string
);
607 /***********************************************************************
608 * HTTP_FreeTokens (internal)
610 * Frees memory returned from HTTP_Tokenize.
612 static void HTTP_FreeTokens(LPWSTR
* token_array
)
615 for (i
= 0; token_array
[i
]; i
++) heap_free(token_array
[i
]);
616 heap_free(token_array
);
619 static void HTTP_FixURL(http_request_t
*request
)
621 static const WCHAR szSlash
[] = { '/',0 };
622 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/', 0 };
624 /* If we don't have a path we set it to root */
625 if (NULL
== request
->path
)
626 request
->path
= heap_strdupW(szSlash
);
627 else /* remove \r and \n*/
629 int nLen
= strlenW(request
->path
);
630 while ((nLen
>0 ) && ((request
->path
[nLen
-1] == '\r')||(request
->path
[nLen
-1] == '\n')))
633 request
->path
[nLen
]='\0';
635 /* Replace '\' with '/' */
638 if (request
->path
[nLen
] == '\\') request
->path
[nLen
]='/';
642 if(CSTR_EQUAL
!= CompareStringW( LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
643 request
->path
, strlenW(request
->path
), szHttp
, strlenW(szHttp
) )
644 && request
->path
[0] != '/') /* not an absolute path ?? --> fix it !! */
646 WCHAR
*fixurl
= heap_alloc((strlenW(request
->path
) + 2)*sizeof(WCHAR
));
648 strcpyW(fixurl
+ 1, request
->path
);
649 heap_free( request
->path
);
650 request
->path
= fixurl
;
654 static LPWSTR
HTTP_BuildHeaderRequestString( http_request_t
*request
, LPCWSTR verb
, LPCWSTR path
, LPCWSTR version
)
656 LPWSTR requestString
;
662 static const WCHAR szSpace
[] = { ' ',0 };
663 static const WCHAR szColon
[] = { ':',' ',0 };
664 static const WCHAR sztwocrlf
[] = {'\r','\n','\r','\n', 0};
666 /* allocate space for an array of all the string pointers to be added */
667 len
= (request
->nCustHeaders
)*4 + 10;
668 req
= heap_alloc(len
*sizeof(LPCWSTR
));
670 /* add the verb, path and HTTP version string */
678 /* Append custom request headers */
679 for (i
= 0; i
< request
->nCustHeaders
; i
++)
681 if (request
->custHeaders
[i
].wFlags
& HDR_ISREQUEST
)
684 req
[n
++] = request
->custHeaders
[i
].lpszField
;
686 req
[n
++] = request
->custHeaders
[i
].lpszValue
;
688 TRACE("Adding custom header %s (%s)\n",
689 debugstr_w(request
->custHeaders
[i
].lpszField
),
690 debugstr_w(request
->custHeaders
[i
].lpszValue
));
695 ERR("oops. buffer overrun\n");
698 requestString
= HTTP_build_req( req
, 4 );
702 * Set (header) termination string for request
703 * Make sure there's exactly two new lines at the end of the request
705 p
= &requestString
[strlenW(requestString
)-1];
706 while ( (*p
== '\n') || (*p
== '\r') )
708 strcpyW( p
+1, sztwocrlf
);
710 return requestString
;
713 static void HTTP_ProcessCookies( http_request_t
*request
)
717 LPHTTPHEADERW setCookieHeader
;
719 if(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
)
722 while((HeaderIndex
= HTTP_GetCustomHeaderIndex(request
, szSet_Cookie
, numCookies
++, FALSE
)) != -1)
728 setCookieHeader
= &request
->custHeaders
[HeaderIndex
];
730 if (!setCookieHeader
->lpszValue
)
733 host
= HTTP_GetHeader(request
, hostW
);
737 data
= strchrW(setCookieHeader
->lpszValue
, '=');
741 name
= heap_strndupW(setCookieHeader
->lpszValue
, data
-setCookieHeader
->lpszValue
);
746 set_cookie(host
->lpszValue
, request
->path
, name
, data
);
751 static void strip_spaces(LPWSTR start
)
756 while (*str
== ' ' && *str
!= '\0')
760 memmove(start
, str
, sizeof(WCHAR
) * (strlenW(str
) + 1));
762 end
= start
+ strlenW(start
) - 1;
763 while (end
>= start
&& *end
== ' ')
770 static inline BOOL
is_basic_auth_value( LPCWSTR pszAuthValue
, LPWSTR
*pszRealm
)
772 static const WCHAR szBasic
[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
773 static const WCHAR szRealm
[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
775 is_basic
= !strncmpiW(pszAuthValue
, szBasic
, ARRAYSIZE(szBasic
)) &&
776 ((pszAuthValue
[ARRAYSIZE(szBasic
)] == ' ') || !pszAuthValue
[ARRAYSIZE(szBasic
)]);
777 if (is_basic
&& pszRealm
)
780 LPCWSTR ptr
= &pszAuthValue
[ARRAYSIZE(szBasic
)];
784 token
= strchrW(ptr
,'=');
788 while (*realm
== ' ' && *realm
!= '\0')
790 if(!strncmpiW(realm
, szRealm
, ARRAYSIZE(szRealm
)) &&
791 (realm
[ARRAYSIZE(szRealm
)] == ' ' || realm
[ARRAYSIZE(szRealm
)] == '='))
794 while (*token
== ' ' && *token
!= '\0')
798 *pszRealm
= heap_strdupW(token
);
799 strip_spaces(*pszRealm
);
806 static void destroy_authinfo( struct HttpAuthInfo
*authinfo
)
808 if (!authinfo
) return;
810 if (SecIsValidHandle(&authinfo
->ctx
))
811 DeleteSecurityContext(&authinfo
->ctx
);
812 if (SecIsValidHandle(&authinfo
->cred
))
813 FreeCredentialsHandle(&authinfo
->cred
);
815 heap_free(authinfo
->auth_data
);
816 heap_free(authinfo
->scheme
);
820 static UINT
retrieve_cached_basic_authorization(LPWSTR host
, LPWSTR realm
, LPSTR
*auth_data
)
822 basicAuthorizationData
*ad
;
825 TRACE("Looking for authorization for %s:%s\n",debugstr_w(host
),debugstr_w(realm
));
827 EnterCriticalSection(&authcache_cs
);
828 LIST_FOR_EACH_ENTRY(ad
, &basicAuthorizationCache
, basicAuthorizationData
, entry
)
830 if (!strcmpiW(host
,ad
->host
) && !strcmpW(realm
,ad
->realm
))
832 TRACE("Authorization found in cache\n");
833 *auth_data
= heap_alloc(ad
->authorizationLen
);
834 memcpy(*auth_data
,ad
->authorization
,ad
->authorizationLen
);
835 rc
= ad
->authorizationLen
;
839 LeaveCriticalSection(&authcache_cs
);
843 static void cache_basic_authorization(LPWSTR host
, LPWSTR realm
, LPSTR auth_data
, UINT auth_data_len
)
846 basicAuthorizationData
* ad
= NULL
;
848 TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host
),debugstr_w(realm
),debugstr_an(auth_data
,auth_data_len
));
850 EnterCriticalSection(&authcache_cs
);
851 LIST_FOR_EACH(cursor
, &basicAuthorizationCache
)
853 basicAuthorizationData
*check
= LIST_ENTRY(cursor
,basicAuthorizationData
,entry
);
854 if (!strcmpiW(host
,check
->host
) && !strcmpW(realm
,check
->realm
))
863 TRACE("Found match in cache, replacing\n");
864 heap_free(ad
->authorization
);
865 ad
->authorization
= heap_alloc(auth_data_len
);
866 memcpy(ad
->authorization
, auth_data
, auth_data_len
);
867 ad
->authorizationLen
= auth_data_len
;
871 ad
= heap_alloc(sizeof(basicAuthorizationData
));
872 ad
->host
= heap_strdupW(host
);
873 ad
->realm
= heap_strdupW(realm
);
874 ad
->authorization
= heap_alloc(auth_data_len
);
875 memcpy(ad
->authorization
, auth_data
, auth_data_len
);
876 ad
->authorizationLen
= auth_data_len
;
877 list_add_head(&basicAuthorizationCache
,&ad
->entry
);
878 TRACE("authorization cached\n");
880 LeaveCriticalSection(&authcache_cs
);
883 static BOOL
retrieve_cached_authorization(LPWSTR host
, LPWSTR scheme
,
884 SEC_WINNT_AUTH_IDENTITY_W
*nt_auth_identity
)
886 authorizationData
*ad
;
888 TRACE("Looking for authorization for %s:%s\n", debugstr_w(host
), debugstr_w(scheme
));
890 EnterCriticalSection(&authcache_cs
);
891 LIST_FOR_EACH_ENTRY(ad
, &authorizationCache
, authorizationData
, entry
) {
892 if(!strcmpiW(host
, ad
->host
) && !strcmpiW(scheme
, ad
->scheme
)) {
893 TRACE("Authorization found in cache\n");
895 nt_auth_identity
->User
= heap_strdupW(ad
->user
);
896 nt_auth_identity
->Password
= heap_strdupW(ad
->password
);
897 nt_auth_identity
->Domain
= heap_alloc(sizeof(WCHAR
)*ad
->domain_len
);
898 if(!nt_auth_identity
->User
|| !nt_auth_identity
->Password
||
899 (!nt_auth_identity
->Domain
&& ad
->domain_len
)) {
900 heap_free(nt_auth_identity
->User
);
901 heap_free(nt_auth_identity
->Password
);
902 heap_free(nt_auth_identity
->Domain
);
906 nt_auth_identity
->Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
907 nt_auth_identity
->UserLength
= ad
->user_len
;
908 nt_auth_identity
->PasswordLength
= ad
->password_len
;
909 memcpy(nt_auth_identity
->Domain
, ad
->domain
, sizeof(WCHAR
)*ad
->domain_len
);
910 nt_auth_identity
->DomainLength
= ad
->domain_len
;
911 LeaveCriticalSection(&authcache_cs
);
915 LeaveCriticalSection(&authcache_cs
);
920 static void cache_authorization(LPWSTR host
, LPWSTR scheme
,
921 SEC_WINNT_AUTH_IDENTITY_W
*nt_auth_identity
)
923 authorizationData
*ad
;
926 TRACE("Caching authorization for %s:%s\n", debugstr_w(host
), debugstr_w(scheme
));
928 EnterCriticalSection(&authcache_cs
);
929 LIST_FOR_EACH_ENTRY(ad
, &authorizationCache
, authorizationData
, entry
)
930 if(!strcmpiW(host
, ad
->host
) && !strcmpiW(scheme
, ad
->scheme
)) {
937 heap_free(ad
->password
);
938 heap_free(ad
->domain
);
940 ad
= heap_alloc(sizeof(authorizationData
));
942 LeaveCriticalSection(&authcache_cs
);
946 ad
->host
= heap_strdupW(host
);
947 ad
->scheme
= heap_strdupW(scheme
);
948 list_add_head(&authorizationCache
, &ad
->entry
);
951 ad
->user
= heap_strndupW(nt_auth_identity
->User
, nt_auth_identity
->UserLength
);
952 ad
->password
= heap_strndupW(nt_auth_identity
->Password
, nt_auth_identity
->PasswordLength
);
953 ad
->domain
= heap_strndupW(nt_auth_identity
->Domain
, nt_auth_identity
->DomainLength
);
954 ad
->user_len
= nt_auth_identity
->UserLength
;
955 ad
->password_len
= nt_auth_identity
->PasswordLength
;
956 ad
->domain_len
= nt_auth_identity
->DomainLength
;
958 if(!ad
->host
|| !ad
->scheme
|| !ad
->user
|| !ad
->password
959 || (nt_auth_identity
->Domain
&& !ad
->domain
)) {
961 heap_free(ad
->scheme
);
963 heap_free(ad
->password
);
964 heap_free(ad
->domain
);
965 list_remove(&ad
->entry
);
969 LeaveCriticalSection(&authcache_cs
);
972 static BOOL
HTTP_DoAuthorization( http_request_t
*request
, LPCWSTR pszAuthValue
,
973 struct HttpAuthInfo
**ppAuthInfo
,
974 LPWSTR domain_and_username
, LPWSTR password
,
977 SECURITY_STATUS sec_status
;
978 struct HttpAuthInfo
*pAuthInfo
= *ppAuthInfo
;
980 LPWSTR szRealm
= NULL
;
982 TRACE("%s\n", debugstr_w(pszAuthValue
));
989 pAuthInfo
= heap_alloc(sizeof(*pAuthInfo
));
993 SecInvalidateHandle(&pAuthInfo
->cred
);
994 SecInvalidateHandle(&pAuthInfo
->ctx
);
995 memset(&pAuthInfo
->exp
, 0, sizeof(pAuthInfo
->exp
));
997 pAuthInfo
->auth_data
= NULL
;
998 pAuthInfo
->auth_data_len
= 0;
999 pAuthInfo
->finished
= FALSE
;
1001 if (is_basic_auth_value(pszAuthValue
,NULL
))
1003 static const WCHAR szBasic
[] = {'B','a','s','i','c',0};
1004 pAuthInfo
->scheme
= heap_strdupW(szBasic
);
1005 if (!pAuthInfo
->scheme
)
1007 heap_free(pAuthInfo
);
1014 SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity
;
1016 pAuthInfo
->scheme
= heap_strdupW(pszAuthValue
);
1017 if (!pAuthInfo
->scheme
)
1019 heap_free(pAuthInfo
);
1023 if (domain_and_username
)
1025 WCHAR
*user
= strchrW(domain_and_username
, '\\');
1026 WCHAR
*domain
= domain_and_username
;
1028 /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
1030 pAuthData
= &nt_auth_identity
;
1035 user
= domain_and_username
;
1039 nt_auth_identity
.Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
1040 nt_auth_identity
.User
= user
;
1041 nt_auth_identity
.UserLength
= strlenW(nt_auth_identity
.User
);
1042 nt_auth_identity
.Domain
= domain
;
1043 nt_auth_identity
.DomainLength
= domain
? user
- domain
- 1 : 0;
1044 nt_auth_identity
.Password
= password
;
1045 nt_auth_identity
.PasswordLength
= strlenW(nt_auth_identity
.Password
);
1047 cache_authorization(host
, pAuthInfo
->scheme
, &nt_auth_identity
);
1049 else if(retrieve_cached_authorization(host
, pAuthInfo
->scheme
, &nt_auth_identity
))
1050 pAuthData
= &nt_auth_identity
;
1052 /* use default credentials */
1055 sec_status
= AcquireCredentialsHandleW(NULL
, pAuthInfo
->scheme
,
1056 SECPKG_CRED_OUTBOUND
, NULL
,
1058 NULL
, &pAuthInfo
->cred
,
1061 if(pAuthData
&& !domain_and_username
) {
1062 heap_free(nt_auth_identity
.User
);
1063 heap_free(nt_auth_identity
.Domain
);
1064 heap_free(nt_auth_identity
.Password
);
1067 if (sec_status
== SEC_E_OK
)
1069 PSecPkgInfoW sec_pkg_info
;
1070 sec_status
= QuerySecurityPackageInfoW(pAuthInfo
->scheme
, &sec_pkg_info
);
1071 if (sec_status
== SEC_E_OK
)
1073 pAuthInfo
->max_token
= sec_pkg_info
->cbMaxToken
;
1074 FreeContextBuffer(sec_pkg_info
);
1077 if (sec_status
!= SEC_E_OK
)
1079 WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
1080 debugstr_w(pAuthInfo
->scheme
), sec_status
);
1081 heap_free(pAuthInfo
->scheme
);
1082 heap_free(pAuthInfo
);
1086 *ppAuthInfo
= pAuthInfo
;
1088 else if (pAuthInfo
->finished
)
1091 if ((strlenW(pszAuthValue
) < strlenW(pAuthInfo
->scheme
)) ||
1092 strncmpiW(pszAuthValue
, pAuthInfo
->scheme
, strlenW(pAuthInfo
->scheme
)))
1094 ERR("authentication scheme changed from %s to %s\n",
1095 debugstr_w(pAuthInfo
->scheme
), debugstr_w(pszAuthValue
));
1099 if (is_basic_auth_value(pszAuthValue
,&szRealm
))
1103 char *auth_data
= NULL
;
1104 UINT auth_data_len
= 0;
1106 TRACE("basic authentication realm %s\n",debugstr_w(szRealm
));
1108 if (!domain_and_username
)
1110 if (host
&& szRealm
)
1111 auth_data_len
= retrieve_cached_basic_authorization(host
, szRealm
,&auth_data
);
1112 if (auth_data_len
== 0)
1120 userlen
= WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, lstrlenW(domain_and_username
), NULL
, 0, NULL
, NULL
);
1121 passlen
= WideCharToMultiByte(CP_UTF8
, 0, password
, lstrlenW(password
), NULL
, 0, NULL
, NULL
);
1123 /* length includes a nul terminator, which will be re-used for the ':' */
1124 auth_data
= heap_alloc(userlen
+ 1 + passlen
);
1131 WideCharToMultiByte(CP_UTF8
, 0, domain_and_username
, -1, auth_data
, userlen
, NULL
, NULL
);
1132 auth_data
[userlen
] = ':';
1133 WideCharToMultiByte(CP_UTF8
, 0, password
, -1, &auth_data
[userlen
+1], passlen
, NULL
, NULL
);
1134 auth_data_len
= userlen
+ 1 + passlen
;
1135 if (host
&& szRealm
)
1136 cache_basic_authorization(host
, szRealm
, auth_data
, auth_data_len
);
1139 pAuthInfo
->auth_data
= auth_data
;
1140 pAuthInfo
->auth_data_len
= auth_data_len
;
1141 pAuthInfo
->finished
= TRUE
;
1147 LPCWSTR pszAuthData
;
1148 SecBufferDesc out_desc
, in_desc
;
1150 unsigned char *buffer
;
1151 ULONG context_req
= ISC_REQ_CONNECTION
| ISC_REQ_USE_DCE_STYLE
|
1152 ISC_REQ_MUTUAL_AUTH
| ISC_REQ_DELEGATE
;
1154 in
.BufferType
= SECBUFFER_TOKEN
;
1158 in_desc
.ulVersion
= 0;
1159 in_desc
.cBuffers
= 1;
1160 in_desc
.pBuffers
= &in
;
1162 pszAuthData
= pszAuthValue
+ strlenW(pAuthInfo
->scheme
);
1163 if (*pszAuthData
== ' ')
1166 in
.cbBuffer
= HTTP_DecodeBase64(pszAuthData
, NULL
);
1167 in
.pvBuffer
= heap_alloc(in
.cbBuffer
);
1168 HTTP_DecodeBase64(pszAuthData
, in
.pvBuffer
);
1171 buffer
= heap_alloc(pAuthInfo
->max_token
);
1173 out
.BufferType
= SECBUFFER_TOKEN
;
1174 out
.cbBuffer
= pAuthInfo
->max_token
;
1175 out
.pvBuffer
= buffer
;
1177 out_desc
.ulVersion
= 0;
1178 out_desc
.cBuffers
= 1;
1179 out_desc
.pBuffers
= &out
;
1181 sec_status
= InitializeSecurityContextW(first
? &pAuthInfo
->cred
: NULL
,
1182 first
? NULL
: &pAuthInfo
->ctx
,
1183 first
? request
->server
->name
: NULL
,
1184 context_req
, 0, SECURITY_NETWORK_DREP
,
1185 in
.pvBuffer
? &in_desc
: NULL
,
1186 0, &pAuthInfo
->ctx
, &out_desc
,
1187 &pAuthInfo
->attr
, &pAuthInfo
->exp
);
1188 if (sec_status
== SEC_E_OK
)
1190 pAuthInfo
->finished
= TRUE
;
1191 pAuthInfo
->auth_data
= out
.pvBuffer
;
1192 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
1193 TRACE("sending last auth packet\n");
1195 else if (sec_status
== SEC_I_CONTINUE_NEEDED
)
1197 pAuthInfo
->auth_data
= out
.pvBuffer
;
1198 pAuthInfo
->auth_data_len
= out
.cbBuffer
;
1199 TRACE("sending next auth packet\n");
1203 ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status
);
1204 heap_free(out
.pvBuffer
);
1205 destroy_authinfo(pAuthInfo
);
1214 /***********************************************************************
1215 * HTTP_HttpAddRequestHeadersW (internal)
1217 static DWORD
HTTP_HttpAddRequestHeadersW(http_request_t
*request
,
1218 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1223 DWORD len
, res
= ERROR_HTTP_INVALID_HEADER
;
1225 TRACE("copying header: %s\n", debugstr_wn(lpszHeader
, dwHeaderLength
));
1227 if( dwHeaderLength
== ~0U )
1228 len
= strlenW(lpszHeader
);
1230 len
= dwHeaderLength
;
1231 buffer
= heap_alloc(sizeof(WCHAR
)*(len
+1));
1232 lstrcpynW( buffer
, lpszHeader
, len
+ 1);
1238 LPWSTR
* pFieldAndValue
;
1240 lpszEnd
= lpszStart
;
1242 while (*lpszEnd
!= '\0')
1244 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
1249 if (*lpszStart
== '\0')
1252 if (*lpszEnd
== '\r' || *lpszEnd
== '\n')
1255 lpszEnd
++; /* Jump over newline */
1257 TRACE("interpreting header %s\n", debugstr_w(lpszStart
));
1258 if (*lpszStart
== '\0')
1260 /* Skip 0-length headers */
1261 lpszStart
= lpszEnd
;
1262 res
= ERROR_SUCCESS
;
1265 pFieldAndValue
= HTTP_InterpretHttpHeader(lpszStart
);
1268 res
= HTTP_VerifyValidHeader(request
, pFieldAndValue
[0]);
1269 if (res
== ERROR_SUCCESS
)
1270 res
= HTTP_ProcessHeader(request
, pFieldAndValue
[0],
1271 pFieldAndValue
[1], dwModifier
| HTTP_ADDHDR_FLAG_REQ
);
1272 HTTP_FreeTokens(pFieldAndValue
);
1275 lpszStart
= lpszEnd
;
1276 } while (res
== ERROR_SUCCESS
);
1282 /***********************************************************************
1283 * HttpAddRequestHeadersW (WININET.@)
1285 * Adds one or more HTTP header to the request handler
1288 * On Windows if dwHeaderLength includes the trailing '\0', then
1289 * HttpAddRequestHeadersW() adds it too. However this results in an
1290 * invalid HTTP header which is rejected by some servers so we probably
1291 * don't need to match Windows on that point.
1298 BOOL WINAPI
HttpAddRequestHeadersW(HINTERNET hHttpRequest
,
1299 LPCWSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1301 http_request_t
*request
;
1302 DWORD res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
1304 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_wn(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
1309 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
1310 if (request
&& request
->hdr
.htype
== WH_HHTTPREQ
)
1311 res
= HTTP_HttpAddRequestHeadersW( request
, lpszHeader
, dwHeaderLength
, dwModifier
);
1313 WININET_Release( &request
->hdr
);
1315 if(res
!= ERROR_SUCCESS
)
1317 return res
== ERROR_SUCCESS
;
1320 /***********************************************************************
1321 * HttpAddRequestHeadersA (WININET.@)
1323 * Adds one or more HTTP header to the request handler
1330 BOOL WINAPI
HttpAddRequestHeadersA(HINTERNET hHttpRequest
,
1331 LPCSTR lpszHeader
, DWORD dwHeaderLength
, DWORD dwModifier
)
1337 TRACE("%p, %s, %i, %i\n", hHttpRequest
, debugstr_an(lpszHeader
, dwHeaderLength
), dwHeaderLength
, dwModifier
);
1339 len
= MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, NULL
, 0 );
1340 hdr
= heap_alloc(len
*sizeof(WCHAR
));
1341 MultiByteToWideChar( CP_ACP
, 0, lpszHeader
, dwHeaderLength
, hdr
, len
);
1342 if( dwHeaderLength
!= ~0U )
1343 dwHeaderLength
= len
;
1345 r
= HttpAddRequestHeadersW( hHttpRequest
, hdr
, dwHeaderLength
, dwModifier
);
1351 static void free_accept_types( WCHAR
**accept_types
)
1353 WCHAR
*ptr
, **types
= accept_types
;
1356 while ((ptr
= *types
))
1361 heap_free( accept_types
);
1364 static WCHAR
**convert_accept_types( const char **accept_types
)
1367 const char **types
= accept_types
;
1369 BOOL invalid_pointer
= FALSE
;
1371 if (!types
) return NULL
;
1377 /* find out how many there are */
1378 if (*types
&& **types
)
1380 TRACE("accept type: %s\n", debugstr_a(*types
));
1386 WARN("invalid accept type pointer\n");
1387 invalid_pointer
= TRUE
;
1392 if (invalid_pointer
) return NULL
;
1393 if (!(typesW
= heap_alloc( sizeof(WCHAR
*) * (count
+ 1) ))) return NULL
;
1395 types
= accept_types
;
1398 if (*types
&& **types
) typesW
[count
++] = heap_strdupAtoW( *types
);
1401 typesW
[count
] = NULL
;
1405 /***********************************************************************
1406 * HttpOpenRequestA (WININET.@)
1408 * Open a HTTP request handle
1411 * HINTERNET a HTTP request handle on success
1415 HINTERNET WINAPI
HttpOpenRequestA(HINTERNET hHttpSession
,
1416 LPCSTR lpszVerb
, LPCSTR lpszObjectName
, LPCSTR lpszVersion
,
1417 LPCSTR lpszReferrer
, LPCSTR
*lpszAcceptTypes
,
1418 DWORD dwFlags
, DWORD_PTR dwContext
)
1420 LPWSTR szVerb
= NULL
, szObjectName
= NULL
;
1421 LPWSTR szVersion
= NULL
, szReferrer
= NULL
, *szAcceptTypes
= NULL
;
1422 HINTERNET rc
= FALSE
;
1424 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
1425 debugstr_a(lpszVerb
), debugstr_a(lpszObjectName
),
1426 debugstr_a(lpszVersion
), debugstr_a(lpszReferrer
), lpszAcceptTypes
,
1427 dwFlags
, dwContext
);
1431 szVerb
= heap_strdupAtoW(lpszVerb
);
1438 szObjectName
= heap_strdupAtoW(lpszObjectName
);
1439 if ( !szObjectName
)
1445 szVersion
= heap_strdupAtoW(lpszVersion
);
1452 szReferrer
= heap_strdupAtoW(lpszReferrer
);
1457 szAcceptTypes
= convert_accept_types( lpszAcceptTypes
);
1458 rc
= HttpOpenRequestW(hHttpSession
, szVerb
, szObjectName
, szVersion
, szReferrer
,
1459 (const WCHAR
**)szAcceptTypes
, dwFlags
, dwContext
);
1462 free_accept_types(szAcceptTypes
);
1463 heap_free(szReferrer
);
1464 heap_free(szVersion
);
1465 heap_free(szObjectName
);
1470 /***********************************************************************
1473 static UINT
HTTP_EncodeBase64( LPCSTR bin
, unsigned int len
, LPWSTR base64
)
1476 static const CHAR HTTP_Base64Enc
[] =
1477 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1481 /* first 6 bits, all from bin[0] */
1482 base64
[n
++] = HTTP_Base64Enc
[(bin
[0] & 0xfc) >> 2];
1483 x
= (bin
[0] & 3) << 4;
1485 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
1488 base64
[n
++] = HTTP_Base64Enc
[x
];
1493 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[1]&0xf0) >> 4 ) ];
1494 x
= ( bin
[1] & 0x0f ) << 2;
1496 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
1499 base64
[n
++] = HTTP_Base64Enc
[x
];
1503 base64
[n
++] = HTTP_Base64Enc
[ x
| ( (bin
[2]&0xc0 ) >> 6 ) ];
1505 /* last 6 bits, all from bin [2] */
1506 base64
[n
++] = HTTP_Base64Enc
[ bin
[2] & 0x3f ];
1514 #define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
1515 ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
1516 ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
1517 ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
1518 static const signed char HTTP_Base64Dec
[256] =
1520 CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
1521 CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
1522 CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
1523 CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
1524 CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
1525 CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
1526 CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
1527 CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
1528 CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
1529 CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
1530 CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
1531 CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
1532 CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
1533 CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
1534 CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
1535 CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
1536 CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
1537 CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
1538 CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
1539 CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
1540 CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
1541 CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
1542 CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
1543 CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
1544 CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
1545 CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
1549 /***********************************************************************
1552 static UINT
HTTP_DecodeBase64( LPCWSTR base64
, LPSTR bin
)
1560 if (base64
[0] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1561 ((in
[0] = HTTP_Base64Dec
[base64
[0]]) == -1) ||
1562 base64
[1] >= ARRAYSIZE(HTTP_Base64Dec
) ||
1563 ((in
[1] = HTTP_Base64Dec
[base64
[1]]) == -1))
1565 WARN("invalid base64: %s\n", debugstr_w(base64
));
1569 bin
[n
] = (unsigned char) (in
[0] << 2 | in
[1] >> 4);
1572 if ((base64
[2] == '=') && (base64
[3] == '='))
1574 if (base64
[2] > ARRAYSIZE(HTTP_Base64Dec
) ||
1575 ((in
[2] = HTTP_Base64Dec
[base64
[2]]) == -1))
1577 WARN("invalid base64: %s\n", debugstr_w(&base64
[2]));
1581 bin
[n
] = (unsigned char) (in
[1] << 4 | in
[2] >> 2);
1584 if (base64
[3] == '=')
1586 if (base64
[3] > ARRAYSIZE(HTTP_Base64Dec
) ||
1587 ((in
[3] = HTTP_Base64Dec
[base64
[3]]) == -1))
1589 WARN("invalid base64: %s\n", debugstr_w(&base64
[3]));
1593 bin
[n
] = (unsigned char) (((in
[2] << 6) & 0xc0) | in
[3]);
1602 /***********************************************************************
1603 * HTTP_InsertAuthorization
1605 * Insert or delete the authorization field in the request header.
1607 static BOOL
HTTP_InsertAuthorization( http_request_t
*request
, struct HttpAuthInfo
*pAuthInfo
, LPCWSTR header
)
1611 static const WCHAR wszSpace
[] = {' ',0};
1612 static const WCHAR wszBasic
[] = {'B','a','s','i','c',0};
1614 WCHAR
*authorization
= NULL
;
1616 if (pAuthInfo
->auth_data_len
)
1618 /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
1619 len
= strlenW(pAuthInfo
->scheme
)+1+((pAuthInfo
->auth_data_len
+2)*4)/3;
1620 authorization
= heap_alloc((len
+1)*sizeof(WCHAR
));
1624 strcpyW(authorization
, pAuthInfo
->scheme
);
1625 strcatW(authorization
, wszSpace
);
1626 HTTP_EncodeBase64(pAuthInfo
->auth_data
,
1627 pAuthInfo
->auth_data_len
,
1628 authorization
+strlenW(authorization
));
1630 /* clear the data as it isn't valid now that it has been sent to the
1631 * server, unless it's Basic authentication which doesn't do
1632 * connection tracking */
1633 if (strcmpiW(pAuthInfo
->scheme
, wszBasic
))
1635 heap_free(pAuthInfo
->auth_data
);
1636 pAuthInfo
->auth_data
= NULL
;
1637 pAuthInfo
->auth_data_len
= 0;
1641 TRACE("Inserting authorization: %s\n", debugstr_w(authorization
));
1643 HTTP_ProcessHeader(request
, header
, authorization
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
1644 heap_free(authorization
);
1649 static WCHAR
*HTTP_BuildProxyRequestUrl(http_request_t
*req
)
1651 static const WCHAR slash
[] = { '/',0 };
1652 static const WCHAR format
[] = { 'h','t','t','p',':','/','/','%','s',':','%','u',0 };
1653 static const WCHAR formatSSL
[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','u',0 };
1654 http_session_t
*session
= req
->session
;
1655 WCHAR new_location
[INTERNET_MAX_URL_LENGTH
], *url
;
1658 size
= sizeof(new_location
);
1659 if (HTTP_HttpQueryInfoW(req
, HTTP_QUERY_LOCATION
, new_location
, &size
, NULL
) == ERROR_SUCCESS
)
1661 URL_COMPONENTSW UrlComponents
;
1663 if (!(url
= heap_alloc(size
+ sizeof(WCHAR
)))) return NULL
;
1664 strcpyW( url
, new_location
);
1666 ZeroMemory(&UrlComponents
,sizeof(URL_COMPONENTSW
));
1667 if(InternetCrackUrlW(url
, 0, 0, &UrlComponents
)) goto done
;
1671 size
= 16; /* "https://" + sizeof(port#) + ":/\0" */
1672 size
+= strlenW( session
->hostName
) + strlenW( req
->path
);
1674 if (!(url
= heap_alloc(size
* sizeof(WCHAR
)))) return NULL
;
1676 if (req
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)
1677 sprintfW( url
, formatSSL
, session
->hostName
, session
->hostPort
);
1679 sprintfW( url
, format
, session
->hostName
, session
->hostPort
);
1680 if (req
->path
[0] != '/') strcatW( url
, slash
);
1681 strcatW( url
, req
->path
);
1684 TRACE("url=%s\n", debugstr_w(url
));
1688 /***********************************************************************
1689 * HTTP_DealWithProxy
1691 static BOOL
HTTP_DealWithProxy(appinfo_t
*hIC
, http_session_t
*session
, http_request_t
*request
)
1693 WCHAR buf
[INTERNET_MAX_HOST_NAME_LENGTH
];
1694 WCHAR protoProxy
[INTERNET_MAX_URL_LENGTH
];
1695 DWORD protoProxyLen
= INTERNET_MAX_URL_LENGTH
;
1696 WCHAR proxy
[INTERNET_MAX_URL_LENGTH
];
1697 static WCHAR szNul
[] = { 0 };
1698 URL_COMPONENTSW UrlComponents
;
1699 server_t
*new_server
;
1700 static const WCHAR protoHttp
[] = { 'h','t','t','p',0 };
1701 static const WCHAR szHttp
[] = { 'h','t','t','p',':','/','/',0 };
1702 static const WCHAR szFormat
[] = { 'h','t','t','p',':','/','/','%','s',0 };
1704 memset( &UrlComponents
, 0, sizeof UrlComponents
);
1705 UrlComponents
.dwStructSize
= sizeof UrlComponents
;
1706 UrlComponents
.lpszHostName
= buf
;
1707 UrlComponents
.dwHostNameLength
= INTERNET_MAX_HOST_NAME_LENGTH
;
1709 if (!INTERNET_FindProxyForProtocol(hIC
->proxy
, protoHttp
, protoProxy
, &protoProxyLen
))
1711 if( CSTR_EQUAL
!= CompareStringW(LOCALE_SYSTEM_DEFAULT
, NORM_IGNORECASE
,
1712 protoProxy
,strlenW(szHttp
),szHttp
,strlenW(szHttp
)) )
1713 sprintfW(proxy
, szFormat
, protoProxy
);
1715 strcpyW(proxy
, protoProxy
);
1716 if( !InternetCrackUrlW(proxy
, 0, 0, &UrlComponents
) )
1718 if( UrlComponents
.dwHostNameLength
== 0 )
1721 if( !request
->path
)
1722 request
->path
= szNul
;
1724 if(UrlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
1725 UrlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
1727 new_server
= get_server(UrlComponents
.lpszHostName
, UrlComponents
.nPort
);
1731 server_release(request
->server
);
1732 request
->server
= 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
->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 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
1819 CloseHandle(request
->hCacheFile
);
1821 if(HTTP_GetRequestURL(request
, url
)) {
1824 headersLen
= request
->rawHeaders
? strlenW(request
->rawHeaders
) : 0;
1825 CommitUrlCacheEntryW(url
, request
->cacheFile
, request
->expires
,
1826 request
->last_modified
, NORMAL_CACHE_ENTRY
,
1827 request
->rawHeaders
, headersLen
, NULL
, 0);
1830 heap_free(request
->cacheFile
);
1832 request
->read_section
.DebugInfo
->Spare
[0] = 0;
1833 DeleteCriticalSection( &request
->read_section
);
1834 WININET_Release(&request
->session
->hdr
);
1836 destroy_authinfo(request
->authInfo
);
1837 destroy_authinfo(request
->proxyAuthInfo
);
1840 server_release(request
->server
);
1842 heap_free(request
->path
);
1843 heap_free(request
->verb
);
1844 heap_free(request
->rawHeaders
);
1845 heap_free(request
->version
);
1846 heap_free(request
->statusText
);
1848 for (i
= 0; i
< request
->nCustHeaders
; i
++)
1850 heap_free(request
->custHeaders
[i
].lpszField
);
1851 heap_free(request
->custHeaders
[i
].lpszValue
);
1853 destroy_data_stream(request
->data_stream
);
1854 heap_free(request
->custHeaders
);
1857 static void http_release_netconn(http_request_t
*req
, BOOL reuse
)
1859 TRACE("%p %p\n",req
, req
->netconn
);
1864 if(reuse
&& req
->netconn
->keep_alive
) {
1867 EnterCriticalSection(&connection_pool_cs
);
1869 list_add_head(&req
->netconn
->server
->conn_pool
, &req
->netconn
->pool_entry
);
1870 req
->netconn
->keep_until
= GetTickCount64() + COLLECT_TIME
;
1871 req
->netconn
= NULL
;
1873 run_collector
= !collector_running
;
1874 collector_running
= TRUE
;
1876 LeaveCriticalSection(&connection_pool_cs
);
1879 HANDLE thread
= NULL
;
1882 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
, (const WCHAR
*)WININET_hModule
, &module
);
1884 thread
= CreateThread(NULL
, 0, collect_connections_proc
, NULL
, 0, NULL
);
1886 EnterCriticalSection(&connection_pool_cs
);
1887 collector_running
= FALSE
;
1888 LeaveCriticalSection(&connection_pool_cs
);
1891 FreeLibrary(module
);
1894 CloseHandle(thread
);
1899 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
1900 INTERNET_STATUS_CLOSING_CONNECTION
, 0, 0);
1902 free_netconn(req
->netconn
);
1903 req
->netconn
= NULL
;
1905 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
1906 INTERNET_STATUS_CONNECTION_CLOSED
, 0, 0);
1909 static void drain_content(http_request_t
*req
)
1913 if (!req
->netconn
) return;
1915 if (req
->contentLength
== -1)
1917 else if(!strcmpW(req
->verb
, szHEAD
))
1920 try_reuse
= req
->data_stream
->vtbl
->drain_content(req
->data_stream
, req
);
1922 http_release_netconn(req
, try_reuse
);
1925 static BOOL
HTTP_KeepAlive(http_request_t
*request
)
1927 WCHAR szVersion
[10];
1928 WCHAR szConnectionResponse
[20];
1929 DWORD dwBufferSize
= sizeof(szVersion
);
1930 BOOL keepalive
= FALSE
;
1932 /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
1933 * the connection is keep-alive by default */
1934 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_VERSION
, szVersion
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1935 && !strcmpiW(szVersion
, g_szHttp1_1
))
1940 dwBufferSize
= sizeof(szConnectionResponse
);
1941 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_PROXY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
1942 || HTTP_HttpQueryInfoW(request
, HTTP_QUERY_CONNECTION
, szConnectionResponse
, &dwBufferSize
, NULL
) == ERROR_SUCCESS
)
1944 keepalive
= !strcmpiW(szConnectionResponse
, szKeepAlive
);
1950 static void HTTPREQ_CloseConnection(object_header_t
*hdr
)
1952 http_request_t
*req
= (http_request_t
*)hdr
;
1957 static DWORD
HTTPREQ_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
1959 http_request_t
*req
= (http_request_t
*)hdr
;
1962 case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO
:
1964 http_session_t
*session
= req
->session
;
1965 INTERNET_DIAGNOSTIC_SOCKET_INFO
*info
= buffer
;
1967 FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
1969 if (*size
< sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
))
1970 return ERROR_INSUFFICIENT_BUFFER
;
1971 *size
= sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO
);
1972 /* FIXME: can't get a SOCKET from our connection since we don't use
1976 /* FIXME: get source port from req->netConnection */
1977 info
->SourcePort
= 0;
1978 info
->DestPort
= session
->hostPort
;
1980 if (HTTP_KeepAlive(req
))
1981 info
->Flags
|= IDSI_FLAG_KEEP_ALIVE
;
1982 if (session
->appInfo
->proxy
&& session
->appInfo
->proxy
[0] != 0)
1983 info
->Flags
|= IDSI_FLAG_PROXY
;
1984 if (req
->netconn
->useSSL
)
1985 info
->Flags
|= IDSI_FLAG_SECURE
;
1987 return ERROR_SUCCESS
;
1991 TRACE("Queried undocumented option 98, forwarding to INTERNET_OPTION_SECURITY_FLAGS\n");
1993 case INTERNET_OPTION_SECURITY_FLAGS
:
1997 if (*size
< sizeof(ULONG
))
1998 return ERROR_INSUFFICIENT_BUFFER
;
2000 *size
= sizeof(DWORD
);
2001 flags
= req
->netconn
? req
->netconn
->security_flags
: req
->security_flags
| req
->server
->security_flags
;
2002 *(DWORD
*)buffer
= flags
;
2004 TRACE("INTERNET_OPTION_SECURITY_FLAGS %x\n", flags
);
2005 return ERROR_SUCCESS
;
2008 case INTERNET_OPTION_HANDLE_TYPE
:
2009 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2011 if (*size
< sizeof(ULONG
))
2012 return ERROR_INSUFFICIENT_BUFFER
;
2014 *size
= sizeof(DWORD
);
2015 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_HTTP_REQUEST
;
2016 return ERROR_SUCCESS
;
2018 case INTERNET_OPTION_URL
: {
2019 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
2024 static const WCHAR httpW
[] = {'h','t','t','p',':','/','/',0};
2026 TRACE("INTERNET_OPTION_URL\n");
2028 host
= HTTP_GetHeader(req
, hostW
);
2029 strcpyW(url
, httpW
);
2030 strcatW(url
, host
->lpszValue
);
2031 if (NULL
!= (pch
= strchrW(url
+ strlenW(httpW
), ':')))
2033 strcatW(url
, req
->path
);
2035 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url
));
2038 len
= (strlenW(url
)+1) * sizeof(WCHAR
);
2040 return ERROR_INSUFFICIENT_BUFFER
;
2043 strcpyW(buffer
, url
);
2044 return ERROR_SUCCESS
;
2046 len
= WideCharToMultiByte(CP_ACP
, 0, url
, -1, buffer
, *size
, NULL
, NULL
);
2048 return ERROR_INSUFFICIENT_BUFFER
;
2051 return ERROR_SUCCESS
;
2055 case INTERNET_OPTION_CACHE_TIMESTAMPS
: {
2056 INTERNET_CACHE_ENTRY_INFOW
*info
;
2057 INTERNET_CACHE_TIMESTAMPS
*ts
= buffer
;
2058 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
2059 DWORD nbytes
, error
;
2062 TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
2064 if (*size
< sizeof(*ts
))
2066 *size
= sizeof(*ts
);
2067 return ERROR_INSUFFICIENT_BUFFER
;
2070 HTTP_GetRequestURL(req
, url
);
2071 ret
= GetUrlCacheEntryInfoW(url
, NULL
, &nbytes
);
2072 error
= GetLastError();
2073 if (!ret
&& error
== ERROR_INSUFFICIENT_BUFFER
)
2075 if (!(info
= heap_alloc(nbytes
)))
2076 return ERROR_OUTOFMEMORY
;
2078 GetUrlCacheEntryInfoW(url
, info
, &nbytes
);
2080 ts
->ftExpires
= info
->ExpireTime
;
2081 ts
->ftLastModified
= info
->LastModifiedTime
;
2084 *size
= sizeof(*ts
);
2085 return ERROR_SUCCESS
;
2090 case INTERNET_OPTION_DATAFILE_NAME
: {
2093 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
2095 if(!req
->cacheFile
) {
2097 return ERROR_INTERNET_ITEM_NOT_FOUND
;
2101 req_size
= (lstrlenW(req
->cacheFile
)+1) * sizeof(WCHAR
);
2102 if(*size
< req_size
)
2103 return ERROR_INSUFFICIENT_BUFFER
;
2106 memcpy(buffer
, req
->cacheFile
, *size
);
2107 return ERROR_SUCCESS
;
2109 req_size
= WideCharToMultiByte(CP_ACP
, 0, req
->cacheFile
, -1, NULL
, 0, NULL
, NULL
);
2110 if (req_size
> *size
)
2111 return ERROR_INSUFFICIENT_BUFFER
;
2113 *size
= WideCharToMultiByte(CP_ACP
, 0, req
->cacheFile
,
2114 -1, buffer
, *size
, NULL
, NULL
);
2115 return ERROR_SUCCESS
;
2119 case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT
: {
2120 PCCERT_CONTEXT context
;
2122 if(*size
< sizeof(INTERNET_CERTIFICATE_INFOA
)) {
2123 *size
= sizeof(INTERNET_CERTIFICATE_INFOA
);
2124 return ERROR_INSUFFICIENT_BUFFER
;
2127 context
= (PCCERT_CONTEXT
)NETCON_GetCert(req
->netconn
);
2129 INTERNET_CERTIFICATE_INFOA
*info
= (INTERNET_CERTIFICATE_INFOA
*)buffer
;
2132 memset(info
, 0, sizeof(INTERNET_CERTIFICATE_INFOW
));
2133 info
->ftExpiry
= context
->pCertInfo
->NotAfter
;
2134 info
->ftStart
= context
->pCertInfo
->NotBefore
;
2135 len
= CertNameToStrA(context
->dwCertEncodingType
,
2136 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
|CERT_NAME_STR_CRLF_FLAG
, NULL
, 0);
2137 info
->lpszSubjectInfo
= LocalAlloc(0, len
);
2138 if(info
->lpszSubjectInfo
)
2139 CertNameToStrA(context
->dwCertEncodingType
,
2140 &context
->pCertInfo
->Subject
, CERT_SIMPLE_NAME_STR
|CERT_NAME_STR_CRLF_FLAG
,
2141 info
->lpszSubjectInfo
, len
);
2142 len
= CertNameToStrA(context
->dwCertEncodingType
,
2143 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
|CERT_NAME_STR_CRLF_FLAG
, NULL
, 0);
2144 info
->lpszIssuerInfo
= LocalAlloc(0, len
);
2145 if(info
->lpszIssuerInfo
)
2146 CertNameToStrA(context
->dwCertEncodingType
,
2147 &context
->pCertInfo
->Issuer
, CERT_SIMPLE_NAME_STR
|CERT_NAME_STR_CRLF_FLAG
,
2148 info
->lpszIssuerInfo
, len
);
2149 info
->dwKeySize
= NETCON_GetCipherStrength(req
->netconn
);
2150 CertFreeCertificateContext(context
);
2151 return ERROR_SUCCESS
;
2154 case INTERNET_OPTION_CONNECT_TIMEOUT
:
2155 if (*size
< sizeof(DWORD
))
2156 return ERROR_INSUFFICIENT_BUFFER
;
2158 *size
= sizeof(DWORD
);
2159 *(DWORD
*)buffer
= req
->connect_timeout
;
2160 return ERROR_SUCCESS
;
2161 case INTERNET_OPTION_REQUEST_FLAGS
: {
2164 if (*size
< sizeof(DWORD
))
2165 return ERROR_INSUFFICIENT_BUFFER
;
2167 /* FIXME: Add support for:
2168 * INTERNET_REQFLAG_FROM_CACHE
2169 * INTERNET_REQFLAG_CACHE_WRITE_DISABLED
2172 if(req
->session
->appInfo
->proxy
)
2173 flags
|= INTERNET_REQFLAG_VIA_PROXY
;
2174 if(!req
->rawHeaders
)
2175 flags
|= INTERNET_REQFLAG_NO_HEADERS
;
2177 TRACE("INTERNET_OPTION_REQUEST_FLAGS returning %x\n", flags
);
2179 *size
= sizeof(DWORD
);
2180 *(DWORD
*)buffer
= flags
;
2181 return ERROR_SUCCESS
;
2185 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
2188 static DWORD
HTTPREQ_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
2190 http_request_t
*req
= (http_request_t
*)hdr
;
2193 case INTERNET_OPTION_SECURITY_FLAGS
:
2197 if (!buffer
|| size
!= sizeof(DWORD
))
2198 return ERROR_INVALID_PARAMETER
;
2199 flags
= *(DWORD
*)buffer
;
2200 TRACE("INTERNET_OPTION_SECURITY_FLAGS %08x\n", flags
);
2201 flags
&= SECURITY_SET_MASK
;
2202 req
->security_flags
|= flags
;
2204 req
->netconn
->security_flags
|= flags
;
2205 return ERROR_SUCCESS
;
2207 case INTERNET_OPTION_CONNECT_TIMEOUT
:
2208 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
2209 req
->connect_timeout
= *(DWORD
*)buffer
;
2210 return ERROR_SUCCESS
;
2212 case INTERNET_OPTION_SEND_TIMEOUT
:
2213 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
2214 req
->send_timeout
= *(DWORD
*)buffer
;
2215 return ERROR_SUCCESS
;
2217 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
2218 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
2219 req
->receive_timeout
= *(DWORD
*)buffer
;
2220 return ERROR_SUCCESS
;
2222 case INTERNET_OPTION_USERNAME
:
2223 heap_free(req
->session
->userName
);
2224 if (!(req
->session
->userName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
2225 return ERROR_SUCCESS
;
2227 case INTERNET_OPTION_PASSWORD
:
2228 heap_free(req
->session
->password
);
2229 if (!(req
->session
->password
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
2230 return ERROR_SUCCESS
;
2231 case INTERNET_OPTION_HTTP_DECODING
:
2232 if(size
!= sizeof(BOOL
))
2233 return ERROR_INVALID_PARAMETER
;
2234 req
->decoding
= *(BOOL
*)buffer
;
2235 return ERROR_SUCCESS
;
2238 return INET_SetOption(hdr
, option
, buffer
, size
);
2241 /* read some more data into the read buffer (the read section must be held) */
2242 static DWORD
read_more_data( http_request_t
*req
, int maxlen
)
2249 /* move existing data to the start of the buffer */
2251 memmove( req
->read_buf
, req
->read_buf
+ req
->read_pos
, req
->read_size
);
2255 if (maxlen
== -1) maxlen
= sizeof(req
->read_buf
);
2257 res
= NETCON_recv( req
->netconn
, req
->read_buf
+ req
->read_size
,
2258 maxlen
- req
->read_size
, 0, &len
);
2259 if(res
== ERROR_SUCCESS
)
2260 req
->read_size
+= len
;
2265 /* remove some amount of data from the read buffer (the read section must be held) */
2266 static void remove_data( http_request_t
*req
, int count
)
2268 if (!(req
->read_size
-= count
)) req
->read_pos
= 0;
2269 else req
->read_pos
+= count
;
2272 static BOOL
read_line( http_request_t
*req
, LPSTR buffer
, DWORD
*len
)
2274 int count
, bytes_read
, pos
= 0;
2277 EnterCriticalSection( &req
->read_section
);
2280 BYTE
*eol
= memchr( req
->read_buf
+ req
->read_pos
, '\n', req
->read_size
);
2284 count
= eol
- (req
->read_buf
+ req
->read_pos
);
2285 bytes_read
= count
+ 1;
2287 else count
= bytes_read
= req
->read_size
;
2289 count
= min( count
, *len
- pos
);
2290 memcpy( buffer
+ pos
, req
->read_buf
+ req
->read_pos
, count
);
2292 remove_data( req
, bytes_read
);
2295 if ((res
= read_more_data( req
, -1 )) != ERROR_SUCCESS
|| !req
->read_size
)
2298 TRACE( "returning empty string %u\n", res
);
2299 LeaveCriticalSection( &req
->read_section
);
2300 INTERNET_SetLastError(res
);
2304 LeaveCriticalSection( &req
->read_section
);
2308 if (pos
&& buffer
[pos
- 1] == '\r') pos
--;
2311 buffer
[*len
- 1] = 0;
2312 TRACE( "returning %s\n", debugstr_a(buffer
));
2316 /* check if we have reached the end of the data to read (the read section must be held) */
2317 static BOOL
end_of_read_data( http_request_t
*req
)
2319 return !req
->read_size
&& req
->data_stream
->vtbl
->end_of_data(req
->data_stream
, req
);
2322 /* fetch some more data into the read buffer (the read section must be held) */
2323 static DWORD
refill_read_buffer(http_request_t
*req
, read_mode_t read_mode
, DWORD
*read_bytes
)
2327 if(req
->read_size
== sizeof(req
->read_buf
))
2328 return ERROR_SUCCESS
;
2332 memmove(req
->read_buf
, req
->read_buf
+req
->read_pos
, req
->read_size
);
2336 res
= req
->data_stream
->vtbl
->read(req
->data_stream
, req
, req
->read_buf
+req
->read_size
,
2337 sizeof(req
->read_buf
)-req
->read_size
, &read
, read_mode
);
2338 req
->read_size
+= read
;
2340 TRACE("read %u bytes, read_size %u\n", read
, req
->read_size
);
2346 /* return the size of data available to be read immediately (the read section must be held) */
2347 static DWORD
get_avail_data( http_request_t
*req
)
2349 return req
->read_size
+ req
->data_stream
->vtbl
->get_avail_data(req
->data_stream
, req
);
2352 static DWORD
netconn_get_avail_data(data_stream_t
*stream
, http_request_t
*req
)
2354 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2358 NETCON_query_data_available(req
->netconn
, &avail
);
2359 return netconn_stream
->content_length
== ~0u
2361 : min(avail
, netconn_stream
->content_length
-netconn_stream
->content_read
);
2364 static BOOL
netconn_end_of_data(data_stream_t
*stream
, http_request_t
*req
)
2366 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2367 return netconn_stream
->content_read
== netconn_stream
->content_length
|| !req
->netconn
;
2370 static DWORD
netconn_read(data_stream_t
*stream
, http_request_t
*req
, BYTE
*buf
, DWORD size
,
2371 DWORD
*read
, read_mode_t read_mode
)
2373 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2376 size
= min(size
, netconn_stream
->content_length
-netconn_stream
->content_read
);
2378 if(read_mode
== READMODE_NOBLOCK
)
2379 size
= min(size
, netconn_get_avail_data(stream
, req
));
2381 if(size
&& req
->netconn
) {
2382 if(NETCON_recv(req
->netconn
, buf
, size
, read_mode
== READMODE_SYNC
? MSG_WAITALL
: 0, &len
) != ERROR_SUCCESS
)
2385 netconn_stream
->content_length
= netconn_stream
->content_read
;
2388 netconn_stream
->content_read
+= *read
= len
;
2389 TRACE("read %u bytes\n", len
);
2390 return ERROR_SUCCESS
;
2393 static BOOL
netconn_drain_content(data_stream_t
*stream
, http_request_t
*req
)
2395 netconn_stream_t
*netconn_stream
= (netconn_stream_t
*)stream
;
2400 if(netconn_end_of_data(stream
, req
))
2404 avail
= netconn_get_avail_data(stream
, req
);
2408 if(NETCON_recv(req
->netconn
, buf
, min(avail
, sizeof(buf
)), 0, &len
) != ERROR_SUCCESS
)
2411 netconn_stream
->content_read
+= len
;
2412 }while(netconn_stream
->content_read
< netconn_stream
->content_length
);
2417 static void netconn_destroy(data_stream_t
*stream
)
2421 static const data_stream_vtbl_t netconn_stream_vtbl
= {
2422 netconn_get_avail_data
,
2423 netconn_end_of_data
,
2425 netconn_drain_content
,
2429 /* read some more data into the read buffer (the read section must be held) */
2430 static DWORD
read_more_chunked_data(chunked_stream_t
*stream
, http_request_t
*req
, int maxlen
)
2435 if (stream
->buf_pos
)
2437 /* move existing data to the start of the buffer */
2438 if(stream
->buf_size
)
2439 memmove(stream
->buf
, stream
->buf
+ stream
->buf_pos
, stream
->buf_size
);
2440 stream
->buf_pos
= 0;
2443 if (maxlen
== -1) maxlen
= sizeof(stream
->buf
);
2445 res
= NETCON_recv( req
->netconn
, stream
->buf
+ stream
->buf_size
,
2446 maxlen
- stream
->buf_size
, 0, &len
);
2447 if(res
== ERROR_SUCCESS
)
2448 stream
->buf_size
+= len
;
2453 /* remove some amount of data from the read buffer (the read section must be held) */
2454 static void remove_chunked_data(chunked_stream_t
*stream
, int count
)
2456 if (!(stream
->buf_size
-= count
)) stream
->buf_pos
= 0;
2457 else stream
->buf_pos
+= count
;
2460 /* discard data contents until we reach end of line (the read section must be held) */
2461 static DWORD
discard_chunked_eol(chunked_stream_t
*stream
, http_request_t
*req
)
2467 BYTE
*eol
= memchr(stream
->buf
+ stream
->buf_pos
, '\n', stream
->buf_size
);
2470 remove_chunked_data(stream
, (eol
+ 1) - (stream
->buf
+ stream
->buf_pos
));
2473 stream
->buf_pos
= stream
->buf_size
= 0; /* discard everything */
2474 if ((res
= read_more_chunked_data(stream
, req
, -1)) != ERROR_SUCCESS
) return res
;
2475 } while (stream
->buf_size
);
2476 return ERROR_SUCCESS
;
2479 /* read the size of the next chunk (the read section must be held) */
2480 static DWORD
start_next_chunk(chunked_stream_t
*stream
, http_request_t
*req
)
2483 DWORD chunk_size
= 0, res
;
2485 if(stream
->chunk_size
!= ~0u && (res
= discard_chunked_eol(stream
, req
)) != ERROR_SUCCESS
)
2490 while (stream
->buf_size
)
2492 char ch
= stream
->buf
[stream
->buf_pos
];
2493 if (ch
>= '0' && ch
<= '9') chunk_size
= chunk_size
* 16 + ch
- '0';
2494 else if (ch
>= 'a' && ch
<= 'f') chunk_size
= chunk_size
* 16 + ch
- 'a' + 10;
2495 else if (ch
>= 'A' && ch
<= 'F') chunk_size
= chunk_size
* 16 + ch
- 'A' + 10;
2496 else if (ch
== ';' || ch
== '\r' || ch
== '\n')
2498 TRACE( "reading %u byte chunk\n", chunk_size
);
2499 stream
->chunk_size
= chunk_size
;
2500 req
->contentLength
+= chunk_size
;
2501 return discard_chunked_eol(stream
, req
);
2503 remove_chunked_data(stream
, 1);
2505 if ((res
= read_more_chunked_data(stream
, req
, -1)) != ERROR_SUCCESS
) return res
;
2506 if (!stream
->buf_size
)
2508 stream
->chunk_size
= 0;
2509 return ERROR_SUCCESS
;
2514 static DWORD
chunked_get_avail_data(data_stream_t
*stream
, http_request_t
*req
)
2516 /* Allow reading only from read buffer */
2520 static BOOL
chunked_end_of_data(data_stream_t
*stream
, http_request_t
*req
)
2522 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2523 return !chunked_stream
->chunk_size
;
2526 static DWORD
chunked_read(data_stream_t
*stream
, http_request_t
*req
, BYTE
*buf
, DWORD size
,
2527 DWORD
*read
, read_mode_t read_mode
)
2529 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2530 DWORD read_bytes
= 0, ret_read
= 0, res
= ERROR_SUCCESS
;
2532 if(chunked_stream
->chunk_size
== ~0u) {
2533 res
= start_next_chunk(chunked_stream
, req
);
2534 if(res
!= ERROR_SUCCESS
)
2538 while(size
&& chunked_stream
->chunk_size
) {
2539 if(chunked_stream
->buf_size
) {
2540 read_bytes
= min(size
, min(chunked_stream
->buf_size
, chunked_stream
->chunk_size
));
2542 /* this could block */
2543 if(read_mode
== READMODE_NOBLOCK
&& read_bytes
== chunked_stream
->chunk_size
)
2546 memcpy(buf
+ret_read
, chunked_stream
->buf
+chunked_stream
->buf_pos
, read_bytes
);
2547 remove_chunked_data(chunked_stream
, read_bytes
);
2549 read_bytes
= min(size
, chunked_stream
->chunk_size
);
2551 if(read_mode
== READMODE_NOBLOCK
) {
2554 if(!NETCON_query_data_available(req
->netconn
, &avail
) || !avail
)
2556 if(read_bytes
> avail
)
2559 /* this could block */
2560 if(read_bytes
== chunked_stream
->chunk_size
)
2564 res
= NETCON_recv(req
->netconn
, (char *)buf
+ret_read
, read_bytes
, 0, (int*)&read_bytes
);
2565 if(res
!= ERROR_SUCCESS
)
2569 chunked_stream
->chunk_size
-= read_bytes
;
2571 ret_read
+= read_bytes
;
2572 if(!chunked_stream
->chunk_size
) {
2573 assert(read_mode
!= READMODE_NOBLOCK
);
2574 res
= start_next_chunk(chunked_stream
, req
);
2575 if(res
!= ERROR_SUCCESS
)
2579 if(read_mode
== READMODE_ASYNC
)
2580 read_mode
= READMODE_NOBLOCK
;
2583 TRACE("read %u bytes\n", ret_read
);
2588 static BOOL
chunked_drain_content(data_stream_t
*stream
, http_request_t
*req
)
2590 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2592 /* FIXME: we can do better */
2593 return !chunked_stream
->chunk_size
;
2596 static void chunked_destroy(data_stream_t
*stream
)
2598 chunked_stream_t
*chunked_stream
= (chunked_stream_t
*)stream
;
2599 heap_free(chunked_stream
);
2602 static const data_stream_vtbl_t chunked_stream_vtbl
= {
2603 chunked_get_avail_data
,
2604 chunked_end_of_data
,
2606 chunked_drain_content
,
2610 /* set the request content length based on the headers */
2611 static DWORD
set_content_length(http_request_t
*request
)
2613 static const WCHAR szChunked
[] = {'c','h','u','n','k','e','d',0};
2617 if(request
->status_code
== HTTP_STATUS_NO_CONTENT
) {
2618 request
->contentLength
= request
->netconn_stream
.content_length
= 0;
2619 return ERROR_SUCCESS
;
2622 size
= sizeof(request
->contentLength
);
2623 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_FLAG_NUMBER
|HTTP_QUERY_CONTENT_LENGTH
,
2624 &request
->contentLength
, &size
, NULL
) != ERROR_SUCCESS
)
2625 request
->contentLength
= ~0u;
2626 request
->netconn_stream
.content_length
= request
->contentLength
;
2627 request
->netconn_stream
.content_read
= request
->read_size
;
2629 size
= sizeof(encoding
);
2630 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_TRANSFER_ENCODING
, encoding
, &size
, NULL
) == ERROR_SUCCESS
&&
2631 !strcmpiW(encoding
, szChunked
))
2633 chunked_stream_t
*chunked_stream
;
2635 chunked_stream
= heap_alloc(sizeof(*chunked_stream
));
2637 return ERROR_OUTOFMEMORY
;
2639 chunked_stream
->data_stream
.vtbl
= &chunked_stream_vtbl
;
2640 chunked_stream
->buf_size
= chunked_stream
->buf_pos
= 0;
2641 chunked_stream
->chunk_size
= ~0u;
2643 if(request
->read_size
) {
2644 memcpy(chunked_stream
->buf
, request
->read_buf
+request
->read_pos
, request
->read_size
);
2645 chunked_stream
->buf_size
= request
->read_size
;
2646 request
->read_size
= request
->read_pos
= 0;
2649 request
->data_stream
= &chunked_stream
->data_stream
;
2650 request
->contentLength
= ~0u;
2651 request
->read_chunked
= TRUE
;
2654 if(request
->decoding
) {
2657 static const WCHAR gzipW
[] = {'g','z','i','p',0};
2659 encoding_idx
= HTTP_GetCustomHeaderIndex(request
, szContent_Encoding
, 0, FALSE
);
2660 if(encoding_idx
!= -1 && !strcmpiW(request
->custHeaders
[encoding_idx
].lpszValue
, gzipW
))
2661 return init_gzip_stream(request
);
2664 return ERROR_SUCCESS
;
2667 static void send_request_complete(http_request_t
*req
, DWORD_PTR result
, DWORD error
)
2669 INTERNET_ASYNC_RESULT iar
;
2671 iar
.dwResult
= result
;
2672 iar
.dwError
= error
;
2674 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_COMPLETE
, &iar
,
2675 sizeof(INTERNET_ASYNC_RESULT
));
2678 static void HTTP_ReceiveRequestData(http_request_t
*req
, BOOL first_notif
)
2680 DWORD res
, read
= 0, avail
= 0;
2685 EnterCriticalSection( &req
->read_section
);
2687 mode
= first_notif
&& req
->read_size
? READMODE_NOBLOCK
: READMODE_ASYNC
;
2688 res
= refill_read_buffer(req
, mode
, &read
);
2689 if(res
== ERROR_SUCCESS
&& !first_notif
)
2690 avail
= get_avail_data(req
);
2692 LeaveCriticalSection( &req
->read_section
);
2694 if(res
!= ERROR_SUCCESS
|| (mode
!= READMODE_NOBLOCK
&& !read
)) {
2695 WARN("res %u read %u, closing connection\n", res
, read
);
2696 http_release_netconn(req
, FALSE
);
2699 if(res
== ERROR_SUCCESS
)
2700 send_request_complete(req
, req
->session
->hdr
.dwInternalFlags
& INET_OPENURL
? (DWORD_PTR
)req
->hdr
.hInternet
: 1, avail
);
2702 send_request_complete(req
, 0, res
);
2705 /* read data from the http connection (the read section must be held) */
2706 static DWORD
HTTPREQ_Read(http_request_t
*req
, void *buffer
, DWORD size
, DWORD
*read
, BOOL sync
)
2708 DWORD current_read
= 0, ret_read
= 0;
2709 read_mode_t read_mode
;
2710 DWORD res
= ERROR_SUCCESS
;
2712 read_mode
= req
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
? READMODE_ASYNC
: READMODE_SYNC
;
2714 EnterCriticalSection( &req
->read_section
);
2716 if(req
->read_size
) {
2717 ret_read
= min(size
, req
->read_size
);
2718 memcpy(buffer
, req
->read_buf
+req
->read_pos
, ret_read
);
2719 req
->read_size
-= ret_read
;
2720 req
->read_pos
+= ret_read
;
2721 if(read_mode
== READMODE_ASYNC
)
2722 read_mode
= READMODE_NOBLOCK
;
2725 if(ret_read
< size
) {
2726 res
= req
->data_stream
->vtbl
->read(req
->data_stream
, req
, (BYTE
*)buffer
+ret_read
, size
-ret_read
, ¤t_read
, read_mode
);
2727 ret_read
+= current_read
;
2730 LeaveCriticalSection( &req
->read_section
);
2733 TRACE( "retrieved %u bytes (%u)\n", ret_read
, req
->contentLength
);
2735 if(req
->hCacheFile
&& res
== ERROR_SUCCESS
&& ret_read
) {
2739 res
= WriteFile(req
->hCacheFile
, buffer
, ret_read
, &written
, NULL
);
2741 WARN("WriteFile failed: %u\n", GetLastError());
2744 if(size
&& !ret_read
)
2745 http_release_netconn(req
, res
== ERROR_SUCCESS
);
2751 static DWORD
HTTPREQ_ReadFile(object_header_t
*hdr
, void *buffer
, DWORD size
, DWORD
*read
)
2753 http_request_t
*req
= (http_request_t
*)hdr
;
2756 EnterCriticalSection( &req
->read_section
);
2757 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2758 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2760 res
= HTTPREQ_Read(req
, buffer
, size
, read
, TRUE
);
2761 if(res
== ERROR_SUCCESS
)
2763 LeaveCriticalSection( &req
->read_section
);
2768 static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST
*workRequest
)
2770 struct WORKREQ_INTERNETREADFILEEXA
const *data
= &workRequest
->u
.InternetReadFileExA
;
2771 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2774 TRACE("INTERNETREADFILEEXA %p\n", workRequest
->hdr
);
2776 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2777 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2779 send_request_complete(req
, res
== ERROR_SUCCESS
, res
);
2782 static DWORD
HTTPREQ_ReadFileExA(object_header_t
*hdr
, INTERNET_BUFFERSA
*buffers
,
2783 DWORD flags
, DWORD_PTR context
)
2785 http_request_t
*req
= (http_request_t
*)hdr
;
2786 DWORD res
, size
, read
, error
= ERROR_SUCCESS
;
2788 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2789 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2791 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2792 return ERROR_INVALID_PARAMETER
;
2794 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2796 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2798 WORKREQUEST workRequest
;
2800 if (TryEnterCriticalSection( &req
->read_section
))
2802 if (get_avail_data(req
))
2804 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2805 &buffers
->dwBufferLength
, FALSE
);
2806 size
= buffers
->dwBufferLength
;
2807 LeaveCriticalSection( &req
->read_section
);
2810 LeaveCriticalSection( &req
->read_section
);
2813 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExAProc
;
2814 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2815 workRequest
.u
.InternetReadFileExA
.lpBuffersOut
= buffers
;
2817 INTERNET_AsyncCall(&workRequest
);
2819 return ERROR_IO_PENDING
;
2823 size
= buffers
->dwBufferLength
;
2825 EnterCriticalSection( &req
->read_section
);
2826 if(hdr
->dwError
== ERROR_SUCCESS
)
2827 hdr
->dwError
= INTERNET_HANDLE_IN_USE
;
2828 else if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2829 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2832 res
= HTTPREQ_Read(req
, (char*)buffers
->lpvBuffer
+read
, size
-read
,
2833 &buffers
->dwBufferLength
, !(flags
& IRF_NO_WAIT
));
2834 if(res
!= ERROR_SUCCESS
)
2837 read
+= buffers
->dwBufferLength
;
2838 if(read
== size
|| end_of_read_data(req
))
2841 LeaveCriticalSection( &req
->read_section
);
2843 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2844 &buffers
->dwBufferLength
, sizeof(buffers
->dwBufferLength
));
2845 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2846 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2848 EnterCriticalSection( &req
->read_section
);
2851 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2852 hdr
->dwError
= ERROR_SUCCESS
;
2854 error
= hdr
->dwError
;
2856 LeaveCriticalSection( &req
->read_section
);
2857 size
= buffers
->dwBufferLength
;
2858 buffers
->dwBufferLength
= read
;
2861 if (res
== ERROR_SUCCESS
) {
2862 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2863 &size
, sizeof(size
));
2866 return res
==ERROR_SUCCESS
? error
: res
;
2869 static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST
*workRequest
)
2871 struct WORKREQ_INTERNETREADFILEEXW
const *data
= &workRequest
->u
.InternetReadFileExW
;
2872 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2875 TRACE("INTERNETREADFILEEXW %p\n", workRequest
->hdr
);
2877 res
= HTTPREQ_Read(req
, data
->lpBuffersOut
->lpvBuffer
,
2878 data
->lpBuffersOut
->dwBufferLength
, &data
->lpBuffersOut
->dwBufferLength
, TRUE
);
2880 send_request_complete(req
, res
== ERROR_SUCCESS
, res
);
2883 static DWORD
HTTPREQ_ReadFileExW(object_header_t
*hdr
, INTERNET_BUFFERSW
*buffers
,
2884 DWORD flags
, DWORD_PTR context
)
2887 http_request_t
*req
= (http_request_t
*)hdr
;
2888 DWORD res
, size
, read
, error
= ERROR_SUCCESS
;
2890 if (flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
))
2891 FIXME("these dwFlags aren't implemented: 0x%x\n", flags
& ~(IRF_ASYNC
|IRF_NO_WAIT
));
2893 if (buffers
->dwStructSize
!= sizeof(*buffers
))
2894 return ERROR_INVALID_PARAMETER
;
2896 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2898 if (hdr
->dwFlags
& INTERNET_FLAG_ASYNC
)
2900 WORKREQUEST workRequest
;
2902 if (TryEnterCriticalSection( &req
->read_section
))
2904 if (get_avail_data(req
))
2906 res
= HTTPREQ_Read(req
, buffers
->lpvBuffer
, buffers
->dwBufferLength
,
2907 &buffers
->dwBufferLength
, FALSE
);
2908 size
= buffers
->dwBufferLength
;
2909 LeaveCriticalSection( &req
->read_section
);
2912 LeaveCriticalSection( &req
->read_section
);
2915 workRequest
.asyncproc
= HTTPREQ_AsyncReadFileExWProc
;
2916 workRequest
.hdr
= WININET_AddRef(&req
->hdr
);
2917 workRequest
.u
.InternetReadFileExW
.lpBuffersOut
= buffers
;
2919 INTERNET_AsyncCall(&workRequest
);
2921 return ERROR_IO_PENDING
;
2925 size
= buffers
->dwBufferLength
;
2927 EnterCriticalSection( &req
->read_section
);
2928 if(hdr
->dwError
== ERROR_SUCCESS
)
2929 hdr
->dwError
= INTERNET_HANDLE_IN_USE
;
2930 else if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2931 hdr
->dwError
= ERROR_INTERNET_INTERNAL_ERROR
;
2934 res
= HTTPREQ_Read(req
, (char*)buffers
->lpvBuffer
+read
, size
-read
,
2935 &buffers
->dwBufferLength
, !(flags
& IRF_NO_WAIT
));
2936 if(res
!= ERROR_SUCCESS
)
2939 read
+= buffers
->dwBufferLength
;
2940 if(read
== size
|| end_of_read_data(req
))
2943 LeaveCriticalSection( &req
->read_section
);
2945 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2946 &buffers
->dwBufferLength
, sizeof(buffers
->dwBufferLength
));
2947 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
,
2948 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
2950 EnterCriticalSection( &req
->read_section
);
2953 if(hdr
->dwError
== INTERNET_HANDLE_IN_USE
)
2954 hdr
->dwError
= ERROR_SUCCESS
;
2956 error
= hdr
->dwError
;
2958 LeaveCriticalSection( &req
->read_section
);
2959 size
= buffers
->dwBufferLength
;
2960 buffers
->dwBufferLength
= read
;
2963 if (res
== ERROR_SUCCESS
) {
2964 INTERNET_SendCallback(&req
->hdr
, req
->hdr
.dwContext
, INTERNET_STATUS_RESPONSE_RECEIVED
,
2965 &size
, sizeof(size
));
2968 return res
==ERROR_SUCCESS
? error
: res
;
2971 static DWORD
HTTPREQ_WriteFile(object_header_t
*hdr
, const void *buffer
, DWORD size
, DWORD
*written
)
2974 http_request_t
*request
= (http_request_t
*)hdr
;
2976 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
2979 res
= NETCON_send(request
->netconn
, buffer
, size
, 0, (LPINT
)written
);
2980 if (res
== ERROR_SUCCESS
)
2981 request
->bytesWritten
+= *written
;
2983 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REQUEST_SENT
, written
, sizeof(DWORD
));
2987 static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST
*workRequest
)
2989 http_request_t
*req
= (http_request_t
*)workRequest
->hdr
;
2991 HTTP_ReceiveRequestData(req
, FALSE
);
2994 static DWORD
HTTPREQ_QueryDataAvailable(object_header_t
*hdr
, DWORD
*available
, DWORD flags
, DWORD_PTR ctx
)
2996 http_request_t
*req
= (http_request_t
*)hdr
;
2998 TRACE("(%p %p %x %lx)\n", req
, available
, flags
, ctx
);
3000 if (req
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
3002 WORKREQUEST workRequest
;
3004 /* never wait, if we can't enter the section we queue an async request right away */
3005 if (TryEnterCriticalSection( &req
->read_section
))
3007 refill_read_buffer(req
, READMODE_NOBLOCK
, NULL
);
3008 if ((*available
= get_avail_data( req
))) goto done
;
3009 if (end_of_read_data( req
)) goto done
;
3010 LeaveCriticalSection( &req
->read_section
);
3013 workRequest
.asyncproc
= HTTPREQ_AsyncQueryDataAvailableProc
;
3014 workRequest
.hdr
= WININET_AddRef( &req
->hdr
);
3016 INTERNET_AsyncCall(&workRequest
);
3018 return ERROR_IO_PENDING
;
3021 EnterCriticalSection( &req
->read_section
);
3023 if (!(*available
= get_avail_data( req
)) && !end_of_read_data( req
))
3025 refill_read_buffer( req
, READMODE_ASYNC
, NULL
);
3026 *available
= get_avail_data( req
);
3030 LeaveCriticalSection( &req
->read_section
);
3032 TRACE( "returning %u\n", *available
);
3033 return ERROR_SUCCESS
;
3036 static const object_vtbl_t HTTPREQVtbl
= {
3038 HTTPREQ_CloseConnection
,
3039 HTTPREQ_QueryOption
,
3042 HTTPREQ_ReadFileExA
,
3043 HTTPREQ_ReadFileExW
,
3045 HTTPREQ_QueryDataAvailable
,
3049 /***********************************************************************
3050 * HTTP_HttpOpenRequestW (internal)
3052 * Open a HTTP request handle
3055 * HINTERNET a HTTP request handle on success
3059 static DWORD
HTTP_HttpOpenRequestW(http_session_t
*session
,
3060 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
3061 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
3062 DWORD dwFlags
, DWORD_PTR dwContext
, HINTERNET
*ret
)
3064 appinfo_t
*hIC
= session
->appInfo
;
3065 http_request_t
*request
;
3067 DWORD len
, res
= ERROR_SUCCESS
;
3071 request
= alloc_object(&session
->hdr
, &HTTPREQVtbl
, sizeof(http_request_t
));
3073 return ERROR_OUTOFMEMORY
;
3075 request
->hdr
.htype
= WH_HHTTPREQ
;
3076 request
->hdr
.dwFlags
= dwFlags
;
3077 request
->hdr
.dwContext
= dwContext
;
3078 request
->contentLength
= ~0u;
3080 request
->netconn_stream
.data_stream
.vtbl
= &netconn_stream_vtbl
;
3081 request
->data_stream
= &request
->netconn_stream
.data_stream
;
3082 request
->connect_timeout
= session
->connect_timeout
;
3083 request
->send_timeout
= session
->send_timeout
;
3084 request
->receive_timeout
= session
->receive_timeout
;
3086 InitializeCriticalSection( &request
->read_section
);
3087 request
->read_section
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": http_request_t.read_section");
3089 WININET_AddRef( &session
->hdr
);
3090 request
->session
= session
;
3091 list_add_head( &session
->hdr
.children
, &request
->hdr
.entry
);
3093 port
= session
->hostPort
;
3094 if(port
== INTERNET_INVALID_PORT_NUMBER
)
3095 port
= dwFlags
& INTERNET_FLAG_SECURE
? INTERNET_DEFAULT_HTTPS_PORT
: INTERNET_DEFAULT_HTTP_PORT
;
3097 request
->server
= get_server(session
->hostName
, port
);
3098 if(!request
->server
) {
3099 WININET_Release(&request
->hdr
);
3100 return ERROR_OUTOFMEMORY
;
3103 if (dwFlags
& INTERNET_FLAG_IGNORE_CERT_CN_INVALID
)
3104 request
->security_flags
|= SECURITY_FLAG_IGNORE_CERT_CN_INVALID
;
3105 if (dwFlags
& INTERNET_FLAG_IGNORE_CERT_DATE_INVALID
)
3106 request
->security_flags
|= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
;
3108 if (lpszObjectName
&& *lpszObjectName
) {
3112 rc
= UrlEscapeW(lpszObjectName
, NULL
, &len
, URL_ESCAPE_SPACES_ONLY
);
3113 if (rc
!= E_POINTER
)
3114 len
= strlenW(lpszObjectName
)+1;
3115 request
->path
= heap_alloc(len
*sizeof(WCHAR
));
3116 rc
= UrlEscapeW(lpszObjectName
, request
->path
, &len
,
3117 URL_ESCAPE_SPACES_ONLY
);
3120 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName
),rc
);
3121 strcpyW(request
->path
,lpszObjectName
);
3124 static const WCHAR slashW
[] = {'/',0};
3126 request
->path
= heap_strdupW(slashW
);
3129 if (lpszReferrer
&& *lpszReferrer
)
3130 HTTP_ProcessHeader(request
, HTTP_REFERER
, lpszReferrer
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
3132 if (lpszAcceptTypes
)
3135 for (i
= 0; lpszAcceptTypes
[i
]; i
++)
3137 if (!*lpszAcceptTypes
[i
]) continue;
3138 HTTP_ProcessHeader(request
, HTTP_ACCEPT
, lpszAcceptTypes
[i
],
3139 HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
|
3140 HTTP_ADDHDR_FLAG_REQ
|
3141 (i
== 0 ? HTTP_ADDHDR_FLAG_REPLACE
: 0));
3145 request
->verb
= heap_strdupW(lpszVerb
&& *lpszVerb
? lpszVerb
: szGET
);
3146 request
->version
= heap_strdupW(lpszVersion
? lpszVersion
: g_szHttp1_1
);
3148 if (session
->hostPort
!= INTERNET_INVALID_PORT_NUMBER
&&
3149 session
->hostPort
!= INTERNET_DEFAULT_HTTP_PORT
&&
3150 session
->hostPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
3154 static const WCHAR host_formatW
[] = {'%','s',':','%','u',0};
3156 host_name
= heap_alloc((strlenW(session
->hostName
) + 7 /* length of ":65535" + 1 */) * sizeof(WCHAR
));
3158 res
= ERROR_OUTOFMEMORY
;
3162 sprintfW(host_name
, host_formatW
, session
->hostName
, session
->hostPort
);
3163 HTTP_ProcessHeader(request
, hostW
, host_name
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
3164 heap_free(host_name
);
3167 HTTP_ProcessHeader(request
, hostW
, session
->hostName
,
3168 HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REQ
);
3170 if (session
->hostPort
== INTERNET_INVALID_PORT_NUMBER
)
3171 session
->hostPort
= (dwFlags
& INTERNET_FLAG_SECURE
?
3172 INTERNET_DEFAULT_HTTPS_PORT
:
3173 INTERNET_DEFAULT_HTTP_PORT
);
3175 if (hIC
->proxy
&& hIC
->proxy
[0])
3176 HTTP_DealWithProxy( hIC
, session
, request
);
3178 INTERNET_SendCallback(&session
->hdr
, dwContext
,
3179 INTERNET_STATUS_HANDLE_CREATED
, &request
->hdr
.hInternet
,
3183 TRACE("<-- %u (%p)\n", res
, request
);
3185 if(res
!= ERROR_SUCCESS
) {
3186 WININET_Release( &request
->hdr
);
3191 *ret
= request
->hdr
.hInternet
;
3192 return ERROR_SUCCESS
;
3195 /***********************************************************************
3196 * HttpOpenRequestW (WININET.@)
3198 * Open a HTTP request handle
3201 * HINTERNET a HTTP request handle on success
3205 HINTERNET WINAPI
HttpOpenRequestW(HINTERNET hHttpSession
,
3206 LPCWSTR lpszVerb
, LPCWSTR lpszObjectName
, LPCWSTR lpszVersion
,
3207 LPCWSTR lpszReferrer
, LPCWSTR
*lpszAcceptTypes
,
3208 DWORD dwFlags
, DWORD_PTR dwContext
)
3210 http_session_t
*session
;
3211 HINTERNET handle
= NULL
;
3214 TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession
,
3215 debugstr_w(lpszVerb
), debugstr_w(lpszObjectName
),
3216 debugstr_w(lpszVersion
), debugstr_w(lpszReferrer
), lpszAcceptTypes
,
3217 dwFlags
, dwContext
);
3218 if(lpszAcceptTypes
!=NULL
)
3221 for(i
=0;lpszAcceptTypes
[i
]!=NULL
;i
++)
3222 TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes
[i
]));
3225 session
= (http_session_t
*) get_handle_object( hHttpSession
);
3226 if (NULL
== session
|| session
->hdr
.htype
!= WH_HHTTPSESSION
)
3228 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
3233 * My tests seem to show that the windows version does not
3234 * become asynchronous until after this point. And anyhow
3235 * if this call was asynchronous then how would you get the
3236 * necessary HINTERNET pointer returned by this function.
3239 res
= HTTP_HttpOpenRequestW(session
, lpszVerb
, lpszObjectName
,
3240 lpszVersion
, lpszReferrer
, lpszAcceptTypes
,
3241 dwFlags
, dwContext
, &handle
);
3244 WININET_Release( &session
->hdr
);
3245 TRACE("returning %p\n", handle
);
3246 if(res
!= ERROR_SUCCESS
)
3251 static const LPCWSTR header_lookup
[] = {
3252 szMime_Version
, /* HTTP_QUERY_MIME_VERSION = 0 */
3253 szContent_Type
, /* HTTP_QUERY_CONTENT_TYPE = 1 */
3254 szContent_Transfer_Encoding
,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
3255 szContent_ID
, /* HTTP_QUERY_CONTENT_ID = 3 */
3256 NULL
, /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
3257 szContent_Length
, /* HTTP_QUERY_CONTENT_LENGTH = 5 */
3258 szContent_Language
, /* HTTP_QUERY_CONTENT_LANGUAGE = 6 */
3259 szAllow
, /* HTTP_QUERY_ALLOW = 7 */
3260 szPublic
, /* HTTP_QUERY_PUBLIC = 8 */
3261 szDate
, /* HTTP_QUERY_DATE = 9 */
3262 szExpires
, /* HTTP_QUERY_EXPIRES = 10 */
3263 szLast_Modified
, /* HTTP_QUERY_LAST_MODIFIED = 11 */
3264 NULL
, /* HTTP_QUERY_MESSAGE_ID = 12 */
3265 szURI
, /* HTTP_QUERY_URI = 13 */
3266 szFrom
, /* HTTP_QUERY_DERIVED_FROM = 14 */
3267 NULL
, /* HTTP_QUERY_COST = 15 */
3268 NULL
, /* HTTP_QUERY_LINK = 16 */
3269 szPragma
, /* HTTP_QUERY_PRAGMA = 17 */
3270 NULL
, /* HTTP_QUERY_VERSION = 18 */
3271 szStatus
, /* HTTP_QUERY_STATUS_CODE = 19 */
3272 NULL
, /* HTTP_QUERY_STATUS_TEXT = 20 */
3273 NULL
, /* HTTP_QUERY_RAW_HEADERS = 21 */
3274 NULL
, /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
3275 szConnection
, /* HTTP_QUERY_CONNECTION = 23 */
3276 szAccept
, /* HTTP_QUERY_ACCEPT = 24 */
3277 szAccept_Charset
, /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
3278 szAccept_Encoding
, /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
3279 szAccept_Language
, /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
3280 szAuthorization
, /* HTTP_QUERY_AUTHORIZATION = 28 */
3281 szContent_Encoding
, /* HTTP_QUERY_CONTENT_ENCODING = 29 */
3282 NULL
, /* HTTP_QUERY_FORWARDED = 30 */
3283 NULL
, /* HTTP_QUERY_FROM = 31 */
3284 szIf_Modified_Since
, /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
3285 szLocation
, /* HTTP_QUERY_LOCATION = 33 */
3286 NULL
, /* HTTP_QUERY_ORIG_URI = 34 */
3287 szReferer
, /* HTTP_QUERY_REFERER = 35 */
3288 szRetry_After
, /* HTTP_QUERY_RETRY_AFTER = 36 */
3289 szServer
, /* HTTP_QUERY_SERVER = 37 */
3290 NULL
, /* HTTP_TITLE = 38 */
3291 szUser_Agent
, /* HTTP_QUERY_USER_AGENT = 39 */
3292 szWWW_Authenticate
, /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
3293 szProxy_Authenticate
, /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
3294 szAccept_Ranges
, /* HTTP_QUERY_ACCEPT_RANGES = 42 */
3295 szSet_Cookie
, /* HTTP_QUERY_SET_COOKIE = 43 */
3296 szCookie
, /* HTTP_QUERY_COOKIE = 44 */
3297 NULL
, /* HTTP_QUERY_REQUEST_METHOD = 45 */
3298 NULL
, /* HTTP_QUERY_REFRESH = 46 */
3299 szContent_Disposition
, /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
3300 szAge
, /* HTTP_QUERY_AGE = 48 */
3301 szCache_Control
, /* HTTP_QUERY_CACHE_CONTROL = 49 */
3302 szContent_Base
, /* HTTP_QUERY_CONTENT_BASE = 50 */
3303 szContent_Location
, /* HTTP_QUERY_CONTENT_LOCATION = 51 */
3304 szContent_MD5
, /* HTTP_QUERY_CONTENT_MD5 = 52 */
3305 szContent_Range
, /* HTTP_QUERY_CONTENT_RANGE = 53 */
3306 szETag
, /* HTTP_QUERY_ETAG = 54 */
3307 hostW
, /* HTTP_QUERY_HOST = 55 */
3308 szIf_Match
, /* HTTP_QUERY_IF_MATCH = 56 */
3309 szIf_None_Match
, /* HTTP_QUERY_IF_NONE_MATCH = 57 */
3310 szIf_Range
, /* HTTP_QUERY_IF_RANGE = 58 */
3311 szIf_Unmodified_Since
, /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
3312 szMax_Forwards
, /* HTTP_QUERY_MAX_FORWARDS = 60 */
3313 szProxy_Authorization
, /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
3314 szRange
, /* HTTP_QUERY_RANGE = 62 */
3315 szTransfer_Encoding
, /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
3316 szUpgrade
, /* HTTP_QUERY_UPGRADE = 64 */
3317 szVary
, /* HTTP_QUERY_VARY = 65 */
3318 szVia
, /* HTTP_QUERY_VIA = 66 */
3319 szWarning
, /* HTTP_QUERY_WARNING = 67 */
3320 szExpect
, /* HTTP_QUERY_EXPECT = 68 */
3321 szProxy_Connection
, /* HTTP_QUERY_PROXY_CONNECTION = 69 */
3322 szUnless_Modified_Since
, /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
3325 #define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
3327 /***********************************************************************
3328 * HTTP_HttpQueryInfoW (internal)
3330 static DWORD
HTTP_HttpQueryInfoW(http_request_t
*request
, DWORD dwInfoLevel
,
3331 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3333 LPHTTPHEADERW lphttpHdr
= NULL
;
3334 BOOL request_only
= dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
;
3335 INT requested_index
= lpdwIndex
? *lpdwIndex
: 0;
3336 DWORD level
= (dwInfoLevel
& ~HTTP_QUERY_MODIFIER_FLAGS_MASK
);
3339 /* Find requested header structure */
3342 case HTTP_QUERY_CUSTOM
:
3343 if (!lpBuffer
) return ERROR_INVALID_PARAMETER
;
3344 index
= HTTP_GetCustomHeaderIndex(request
, lpBuffer
, requested_index
, request_only
);
3346 case HTTP_QUERY_RAW_HEADERS_CRLF
:
3350 DWORD res
= ERROR_INVALID_PARAMETER
;
3353 headers
= HTTP_BuildHeaderRequestString(request
, request
->verb
, request
->path
, request
->version
);
3355 headers
= request
->rawHeaders
;
3358 len
= strlenW(headers
) * sizeof(WCHAR
);
3360 if (len
+ sizeof(WCHAR
) > *lpdwBufferLength
)
3362 len
+= sizeof(WCHAR
);
3363 res
= ERROR_INSUFFICIENT_BUFFER
;
3368 memcpy(lpBuffer
, headers
, len
+ sizeof(WCHAR
));
3371 len
= strlenW(szCrLf
) * sizeof(WCHAR
);
3372 memcpy(lpBuffer
, szCrLf
, sizeof(szCrLf
));
3374 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
/ sizeof(WCHAR
)));
3375 res
= ERROR_SUCCESS
;
3377 *lpdwBufferLength
= len
;
3379 if (request_only
) heap_free(headers
);
3382 case HTTP_QUERY_RAW_HEADERS
:
3384 LPWSTR
* ppszRawHeaderLines
= HTTP_Tokenize(request
->rawHeaders
, szCrLf
);
3386 LPWSTR pszString
= lpBuffer
;
3388 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
3389 size
+= strlenW(ppszRawHeaderLines
[i
]) + 1;
3391 if (size
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3393 HTTP_FreeTokens(ppszRawHeaderLines
);
3394 *lpdwBufferLength
= (size
+ 1) * sizeof(WCHAR
);
3395 return ERROR_INSUFFICIENT_BUFFER
;
3399 for (i
= 0; ppszRawHeaderLines
[i
]; i
++)
3401 DWORD len
= strlenW(ppszRawHeaderLines
[i
]);
3402 memcpy(pszString
, ppszRawHeaderLines
[i
], (len
+1)*sizeof(WCHAR
));
3406 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, size
));
3408 *lpdwBufferLength
= size
* sizeof(WCHAR
);
3409 HTTP_FreeTokens(ppszRawHeaderLines
);
3411 return ERROR_SUCCESS
;
3413 case HTTP_QUERY_STATUS_TEXT
:
3414 if (request
->statusText
)
3416 DWORD len
= strlenW(request
->statusText
);
3417 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3419 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
3420 return ERROR_INSUFFICIENT_BUFFER
;
3424 memcpy(lpBuffer
, request
->statusText
, (len
+ 1) * sizeof(WCHAR
));
3425 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
3427 *lpdwBufferLength
= len
* sizeof(WCHAR
);
3428 return ERROR_SUCCESS
;
3431 case HTTP_QUERY_VERSION
:
3432 if (request
->version
)
3434 DWORD len
= strlenW(request
->version
);
3435 if (len
+ 1 > *lpdwBufferLength
/sizeof(WCHAR
))
3437 *lpdwBufferLength
= (len
+ 1) * sizeof(WCHAR
);
3438 return ERROR_INSUFFICIENT_BUFFER
;
3442 memcpy(lpBuffer
, request
->version
, (len
+ 1) * sizeof(WCHAR
));
3443 TRACE("returning data: %s\n", debugstr_wn(lpBuffer
, len
));
3445 *lpdwBufferLength
= len
* sizeof(WCHAR
);
3446 return ERROR_SUCCESS
;
3449 case HTTP_QUERY_CONTENT_ENCODING
:
3450 index
= HTTP_GetCustomHeaderIndex(request
, header_lookup
[request
->read_gzip
? HTTP_QUERY_CONTENT_TYPE
: level
],
3451 requested_index
,request_only
);
3453 case HTTP_QUERY_STATUS_CODE
: {
3454 DWORD res
= ERROR_SUCCESS
;
3457 return ERROR_HTTP_INVALID_QUERY_REQUEST
;
3462 if(dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
) {
3463 if(*lpdwBufferLength
>= sizeof(DWORD
))
3464 *(DWORD
*)lpBuffer
= request
->status_code
;
3466 res
= ERROR_INSUFFICIENT_BUFFER
;
3467 *lpdwBufferLength
= sizeof(DWORD
);
3471 static const WCHAR formatW
[] = {'%','u',0};
3473 size
= (sprintfW(buf
, formatW
, request
->status_code
)+1) * sizeof(WCHAR
);
3475 if(size
<= *lpdwBufferLength
)
3476 memcpy(lpBuffer
, buf
, size
);
3478 res
= ERROR_INSUFFICIENT_BUFFER
;
3480 *lpdwBufferLength
= size
;
3485 assert (LAST_TABLE_HEADER
== (HTTP_QUERY_UNLESS_MODIFIED_SINCE
+ 1));
3487 if (level
< LAST_TABLE_HEADER
&& header_lookup
[level
])
3488 index
= HTTP_GetCustomHeaderIndex(request
, header_lookup
[level
],
3489 requested_index
,request_only
);
3493 lphttpHdr
= &request
->custHeaders
[index
];
3495 /* Ensure header satisfies requested attributes */
3497 ((dwInfoLevel
& HTTP_QUERY_FLAG_REQUEST_HEADERS
) &&
3498 (~lphttpHdr
->wFlags
& HDR_ISREQUEST
)))
3500 return ERROR_HTTP_HEADER_NOT_FOUND
;
3503 if (lpdwIndex
) (*lpdwIndex
)++;
3505 /* coalesce value to requested type */
3506 if (dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
&& lpBuffer
)
3508 *(int *)lpBuffer
= atoiW(lphttpHdr
->lpszValue
);
3509 TRACE(" returning number: %d\n", *(int *)lpBuffer
);
3511 else if (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
&& lpBuffer
)
3517 tmpTime
= ConvertTimeString(lphttpHdr
->lpszValue
);
3519 tmpTM
= *gmtime(&tmpTime
);
3520 STHook
= (SYSTEMTIME
*)lpBuffer
;
3521 STHook
->wDay
= tmpTM
.tm_mday
;
3522 STHook
->wHour
= tmpTM
.tm_hour
;
3523 STHook
->wMilliseconds
= 0;
3524 STHook
->wMinute
= tmpTM
.tm_min
;
3525 STHook
->wDayOfWeek
= tmpTM
.tm_wday
;
3526 STHook
->wMonth
= tmpTM
.tm_mon
+ 1;
3527 STHook
->wSecond
= tmpTM
.tm_sec
;
3528 STHook
->wYear
= tmpTM
.tm_year
;
3530 TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
3531 STHook
->wYear
, STHook
->wMonth
, STHook
->wDay
, STHook
->wDayOfWeek
,
3532 STHook
->wHour
, STHook
->wMinute
, STHook
->wSecond
, STHook
->wMilliseconds
);
3534 else if (lphttpHdr
->lpszValue
)
3536 DWORD len
= (strlenW(lphttpHdr
->lpszValue
) + 1) * sizeof(WCHAR
);
3538 if (len
> *lpdwBufferLength
)
3540 *lpdwBufferLength
= len
;
3541 return ERROR_INSUFFICIENT_BUFFER
;
3545 memcpy(lpBuffer
, lphttpHdr
->lpszValue
, len
);
3546 TRACE("! returning string: %s\n", debugstr_w(lpBuffer
));
3548 *lpdwBufferLength
= len
- sizeof(WCHAR
);
3550 return ERROR_SUCCESS
;
3553 /***********************************************************************
3554 * HttpQueryInfoW (WININET.@)
3556 * Queries for information about an HTTP request
3563 BOOL WINAPI
HttpQueryInfoW(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3564 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3566 http_request_t
*request
;
3569 if (TRACE_ON(wininet
)) {
3570 #define FE(x) { x, #x }
3571 static const wininet_flag_info query_flags
[] = {
3572 FE(HTTP_QUERY_MIME_VERSION
),
3573 FE(HTTP_QUERY_CONTENT_TYPE
),
3574 FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING
),
3575 FE(HTTP_QUERY_CONTENT_ID
),
3576 FE(HTTP_QUERY_CONTENT_DESCRIPTION
),
3577 FE(HTTP_QUERY_CONTENT_LENGTH
),
3578 FE(HTTP_QUERY_CONTENT_LANGUAGE
),
3579 FE(HTTP_QUERY_ALLOW
),
3580 FE(HTTP_QUERY_PUBLIC
),
3581 FE(HTTP_QUERY_DATE
),
3582 FE(HTTP_QUERY_EXPIRES
),
3583 FE(HTTP_QUERY_LAST_MODIFIED
),
3584 FE(HTTP_QUERY_MESSAGE_ID
),
3586 FE(HTTP_QUERY_DERIVED_FROM
),
3587 FE(HTTP_QUERY_COST
),
3588 FE(HTTP_QUERY_LINK
),
3589 FE(HTTP_QUERY_PRAGMA
),
3590 FE(HTTP_QUERY_VERSION
),
3591 FE(HTTP_QUERY_STATUS_CODE
),
3592 FE(HTTP_QUERY_STATUS_TEXT
),
3593 FE(HTTP_QUERY_RAW_HEADERS
),
3594 FE(HTTP_QUERY_RAW_HEADERS_CRLF
),
3595 FE(HTTP_QUERY_CONNECTION
),
3596 FE(HTTP_QUERY_ACCEPT
),
3597 FE(HTTP_QUERY_ACCEPT_CHARSET
),
3598 FE(HTTP_QUERY_ACCEPT_ENCODING
),
3599 FE(HTTP_QUERY_ACCEPT_LANGUAGE
),
3600 FE(HTTP_QUERY_AUTHORIZATION
),
3601 FE(HTTP_QUERY_CONTENT_ENCODING
),
3602 FE(HTTP_QUERY_FORWARDED
),
3603 FE(HTTP_QUERY_FROM
),
3604 FE(HTTP_QUERY_IF_MODIFIED_SINCE
),
3605 FE(HTTP_QUERY_LOCATION
),
3606 FE(HTTP_QUERY_ORIG_URI
),
3607 FE(HTTP_QUERY_REFERER
),
3608 FE(HTTP_QUERY_RETRY_AFTER
),
3609 FE(HTTP_QUERY_SERVER
),
3610 FE(HTTP_QUERY_TITLE
),
3611 FE(HTTP_QUERY_USER_AGENT
),
3612 FE(HTTP_QUERY_WWW_AUTHENTICATE
),
3613 FE(HTTP_QUERY_PROXY_AUTHENTICATE
),
3614 FE(HTTP_QUERY_ACCEPT_RANGES
),
3615 FE(HTTP_QUERY_SET_COOKIE
),
3616 FE(HTTP_QUERY_COOKIE
),
3617 FE(HTTP_QUERY_REQUEST_METHOD
),
3618 FE(HTTP_QUERY_REFRESH
),
3619 FE(HTTP_QUERY_CONTENT_DISPOSITION
),
3621 FE(HTTP_QUERY_CACHE_CONTROL
),
3622 FE(HTTP_QUERY_CONTENT_BASE
),
3623 FE(HTTP_QUERY_CONTENT_LOCATION
),
3624 FE(HTTP_QUERY_CONTENT_MD5
),
3625 FE(HTTP_QUERY_CONTENT_RANGE
),
3626 FE(HTTP_QUERY_ETAG
),
3627 FE(HTTP_QUERY_HOST
),
3628 FE(HTTP_QUERY_IF_MATCH
),
3629 FE(HTTP_QUERY_IF_NONE_MATCH
),
3630 FE(HTTP_QUERY_IF_RANGE
),
3631 FE(HTTP_QUERY_IF_UNMODIFIED_SINCE
),
3632 FE(HTTP_QUERY_MAX_FORWARDS
),
3633 FE(HTTP_QUERY_PROXY_AUTHORIZATION
),
3634 FE(HTTP_QUERY_RANGE
),
3635 FE(HTTP_QUERY_TRANSFER_ENCODING
),
3636 FE(HTTP_QUERY_UPGRADE
),
3637 FE(HTTP_QUERY_VARY
),
3639 FE(HTTP_QUERY_WARNING
),
3640 FE(HTTP_QUERY_CUSTOM
)
3642 static const wininet_flag_info modifier_flags
[] = {
3643 FE(HTTP_QUERY_FLAG_REQUEST_HEADERS
),
3644 FE(HTTP_QUERY_FLAG_SYSTEMTIME
),
3645 FE(HTTP_QUERY_FLAG_NUMBER
),
3646 FE(HTTP_QUERY_FLAG_COALESCE
)
3649 DWORD info_mod
= dwInfoLevel
& HTTP_QUERY_MODIFIER_FLAGS_MASK
;
3650 DWORD info
= dwInfoLevel
& HTTP_QUERY_HEADER_MASK
;
3653 TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest
, dwInfoLevel
, info
);
3654 TRACE(" Attribute:");
3655 for (i
= 0; i
< (sizeof(query_flags
) / sizeof(query_flags
[0])); i
++) {
3656 if (query_flags
[i
].val
== info
) {
3657 TRACE(" %s", query_flags
[i
].name
);
3661 if (i
== (sizeof(query_flags
) / sizeof(query_flags
[0]))) {
3662 TRACE(" Unknown (%08x)", info
);
3665 TRACE(" Modifier:");
3666 for (i
= 0; i
< (sizeof(modifier_flags
) / sizeof(modifier_flags
[0])); i
++) {
3667 if (modifier_flags
[i
].val
& info_mod
) {
3668 TRACE(" %s", modifier_flags
[i
].name
);
3669 info_mod
&= ~ modifier_flags
[i
].val
;
3674 TRACE(" Unknown (%08x)", info_mod
);
3679 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
3680 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
3682 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
3686 if (lpBuffer
== NULL
)
3687 *lpdwBufferLength
= 0;
3688 res
= HTTP_HttpQueryInfoW( request
, dwInfoLevel
,
3689 lpBuffer
, lpdwBufferLength
, lpdwIndex
);
3693 WININET_Release( &request
->hdr
);
3695 TRACE("%u <--\n", res
);
3696 if(res
!= ERROR_SUCCESS
)
3698 return res
== ERROR_SUCCESS
;
3701 /***********************************************************************
3702 * HttpQueryInfoA (WININET.@)
3704 * Queries for information about an HTTP request
3711 BOOL WINAPI
HttpQueryInfoA(HINTERNET hHttpRequest
, DWORD dwInfoLevel
,
3712 LPVOID lpBuffer
, LPDWORD lpdwBufferLength
, LPDWORD lpdwIndex
)
3718 if((dwInfoLevel
& HTTP_QUERY_FLAG_NUMBER
) ||
3719 (dwInfoLevel
& HTTP_QUERY_FLAG_SYSTEMTIME
))
3721 return HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, lpBuffer
,
3722 lpdwBufferLength
, lpdwIndex
);
3728 len
= (*lpdwBufferLength
)*sizeof(WCHAR
);
3729 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3731 alloclen
= MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, NULL
, 0 ) * sizeof(WCHAR
);
3737 bufferW
= heap_alloc(alloclen
);
3738 /* buffer is in/out because of HTTP_QUERY_CUSTOM */
3739 if ((dwInfoLevel
& HTTP_QUERY_HEADER_MASK
) == HTTP_QUERY_CUSTOM
)
3740 MultiByteToWideChar( CP_ACP
, 0, lpBuffer
, -1, bufferW
, alloclen
/ sizeof(WCHAR
) );
3747 result
= HttpQueryInfoW( hHttpRequest
, dwInfoLevel
, bufferW
,
3751 len
= WideCharToMultiByte( CP_ACP
,0, bufferW
, len
/ sizeof(WCHAR
) + 1,
3752 lpBuffer
, *lpdwBufferLength
, NULL
, NULL
);
3753 *lpdwBufferLength
= len
- 1;
3755 TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer
));
3758 /* since the strings being returned from HttpQueryInfoW should be
3759 * only ASCII characters, it is reasonable to assume that all of
3760 * the Unicode characters can be reduced to a single byte */
3761 *lpdwBufferLength
= len
/ sizeof(WCHAR
);
3763 heap_free( bufferW
);
3767 /***********************************************************************
3768 * HTTP_GetRedirectURL (internal)
3770 static LPWSTR
HTTP_GetRedirectURL(http_request_t
*request
, LPCWSTR lpszUrl
)
3772 static WCHAR szHttp
[] = {'h','t','t','p',0};
3773 static WCHAR szHttps
[] = {'h','t','t','p','s',0};
3774 http_session_t
*session
= request
->session
;
3775 URL_COMPONENTSW urlComponents
;
3776 DWORD url_length
= 0;
3778 LPWSTR combined_url
;
3780 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3781 urlComponents
.lpszScheme
= (request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) ? szHttps
: szHttp
;
3782 urlComponents
.dwSchemeLength
= 0;
3783 urlComponents
.lpszHostName
= session
->hostName
;
3784 urlComponents
.dwHostNameLength
= 0;
3785 urlComponents
.nPort
= session
->hostPort
;
3786 urlComponents
.lpszUserName
= session
->userName
;
3787 urlComponents
.dwUserNameLength
= 0;
3788 urlComponents
.lpszPassword
= NULL
;
3789 urlComponents
.dwPasswordLength
= 0;
3790 urlComponents
.lpszUrlPath
= request
->path
;
3791 urlComponents
.dwUrlPathLength
= 0;
3792 urlComponents
.lpszExtraInfo
= NULL
;
3793 urlComponents
.dwExtraInfoLength
= 0;
3795 if (!InternetCreateUrlW(&urlComponents
, 0, NULL
, &url_length
) &&
3796 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3799 orig_url
= heap_alloc(url_length
);
3801 /* convert from bytes to characters */
3802 url_length
= url_length
/ sizeof(WCHAR
) - 1;
3803 if (!InternetCreateUrlW(&urlComponents
, 0, orig_url
, &url_length
))
3805 heap_free(orig_url
);
3810 if (!InternetCombineUrlW(orig_url
, lpszUrl
, NULL
, &url_length
, ICU_ENCODE_SPACES_ONLY
) &&
3811 (GetLastError() != ERROR_INSUFFICIENT_BUFFER
))
3813 heap_free(orig_url
);
3816 combined_url
= heap_alloc(url_length
* sizeof(WCHAR
));
3818 if (!InternetCombineUrlW(orig_url
, lpszUrl
, combined_url
, &url_length
, ICU_ENCODE_SPACES_ONLY
))
3820 heap_free(orig_url
);
3821 heap_free(combined_url
);
3824 heap_free(orig_url
);
3825 return combined_url
;
3829 /***********************************************************************
3830 * HTTP_HandleRedirect (internal)
3832 static DWORD
HTTP_HandleRedirect(http_request_t
*request
, LPCWSTR lpszUrl
)
3834 http_session_t
*session
= request
->session
;
3835 appinfo_t
*hIC
= session
->appInfo
;
3836 BOOL using_proxy
= hIC
->proxy
&& hIC
->proxy
[0];
3837 WCHAR path
[INTERNET_MAX_PATH_LENGTH
];
3842 /* if it's an absolute path, keep the same session info */
3843 lstrcpynW(path
, lpszUrl
, INTERNET_MAX_URL_LENGTH
);
3847 URL_COMPONENTSW urlComponents
;
3848 WCHAR protocol
[INTERNET_MAX_SCHEME_LENGTH
];
3849 WCHAR hostName
[INTERNET_MAX_HOST_NAME_LENGTH
];
3850 WCHAR userName
[INTERNET_MAX_USER_NAME_LENGTH
];
3851 BOOL custom_port
= FALSE
;
3853 static WCHAR httpW
[] = {'h','t','t','p',0};
3854 static WCHAR httpsW
[] = {'h','t','t','p','s',0};
3860 urlComponents
.dwStructSize
= sizeof(URL_COMPONENTSW
);
3861 urlComponents
.lpszScheme
= protocol
;
3862 urlComponents
.dwSchemeLength
= INTERNET_MAX_SCHEME_LENGTH
;
3863 urlComponents
.lpszHostName
= hostName
;
3864 urlComponents
.dwHostNameLength
= INTERNET_MAX_HOST_NAME_LENGTH
;
3865 urlComponents
.lpszUserName
= userName
;
3866 urlComponents
.dwUserNameLength
= INTERNET_MAX_USER_NAME_LENGTH
;
3867 urlComponents
.lpszPassword
= NULL
;
3868 urlComponents
.dwPasswordLength
= 0;
3869 urlComponents
.lpszUrlPath
= path
;
3870 urlComponents
.dwUrlPathLength
= INTERNET_MAX_PATH_LENGTH
;
3871 urlComponents
.lpszExtraInfo
= NULL
;
3872 urlComponents
.dwExtraInfoLength
= 0;
3873 if(!InternetCrackUrlW(lpszUrl
, strlenW(lpszUrl
), 0, &urlComponents
))
3874 return INTERNET_GetLastError();
3876 if(!strcmpiW(protocol
, httpW
)) {
3877 if(request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) {
3878 TRACE("redirect from secure page to non-secure page\n");
3879 /* FIXME: warn about from secure redirect to non-secure page */
3880 request
->hdr
.dwFlags
&= ~INTERNET_FLAG_SECURE
;
3883 if(urlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
3884 urlComponents
.nPort
= INTERNET_DEFAULT_HTTP_PORT
;
3885 else if(urlComponents
.nPort
!= INTERNET_DEFAULT_HTTP_PORT
)
3887 }else if(!strcmpiW(protocol
, httpsW
)) {
3888 if(!(request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
)) {
3889 TRACE("redirect from non-secure page to secure page\n");
3890 /* FIXME: notify about redirect to secure page */
3891 request
->hdr
.dwFlags
|= INTERNET_FLAG_SECURE
;
3894 if(urlComponents
.nPort
== INTERNET_INVALID_PORT_NUMBER
)
3895 urlComponents
.nPort
= INTERNET_DEFAULT_HTTPS_PORT
;
3896 else if(urlComponents
.nPort
!= INTERNET_DEFAULT_HTTPS_PORT
)
3900 heap_free(session
->hostName
);
3904 static const WCHAR fmt
[] = {'%','s',':','%','u',0};
3905 len
= lstrlenW(hostName
);
3906 len
+= 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
3907 session
->hostName
= heap_alloc(len
*sizeof(WCHAR
));
3908 sprintfW(session
->hostName
, fmt
, hostName
, urlComponents
.nPort
);
3911 session
->hostName
= heap_strdupW(hostName
);
3913 HTTP_ProcessHeader(request
, hostW
, session
->hostName
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDREQ_FLAG_REPLACE
| HTTP_ADDHDR_FLAG_REQ
);
3915 heap_free(session
->userName
);
3916 session
->userName
= NULL
;
3918 session
->userName
= heap_strdupW(userName
);
3920 reset_data_stream(request
);
3922 if(!using_proxy
&& (strcmpiW(request
->server
->name
, hostName
) || request
->server
->port
!= urlComponents
.nPort
)) {
3923 server_t
*new_server
;
3925 new_server
= get_server(hostName
, urlComponents
.nPort
);
3926 server_release(request
->server
);
3927 request
->server
= new_server
;
3930 heap_free(request
->path
);
3937 rc
= UrlEscapeW(path
, NULL
, &needed
, URL_ESCAPE_SPACES_ONLY
);
3938 if (rc
!= E_POINTER
)
3939 needed
= strlenW(path
)+1;
3940 request
->path
= heap_alloc(needed
*sizeof(WCHAR
));
3941 rc
= UrlEscapeW(path
, request
->path
, &needed
,
3942 URL_ESCAPE_SPACES_ONLY
);
3945 ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path
),rc
);
3946 strcpyW(request
->path
,path
);
3950 /* Remove custom content-type/length headers on redirects. */
3951 index
= HTTP_GetCustomHeaderIndex(request
, szContent_Type
, 0, TRUE
);
3953 HTTP_DeleteCustomHeader(request
, index
);
3954 index
= HTTP_GetCustomHeaderIndex(request
, szContent_Length
, 0, TRUE
);
3956 HTTP_DeleteCustomHeader(request
, index
);
3958 return ERROR_SUCCESS
;
3961 /***********************************************************************
3962 * HTTP_build_req (internal)
3964 * concatenate all the strings in the request together
3966 static LPWSTR
HTTP_build_req( LPCWSTR
*list
, int len
)
3971 for( t
= list
; *t
; t
++ )
3972 len
+= strlenW( *t
);
3975 str
= heap_alloc(len
*sizeof(WCHAR
));
3978 for( t
= list
; *t
; t
++ )
3984 static DWORD
HTTP_SecureProxyConnect(http_request_t
*request
)
3987 LPWSTR requestString
;
3993 static const WCHAR szConnect
[] = {'C','O','N','N','E','C','T',0};
3994 static const WCHAR szFormat
[] = {'%','s',':','%','u',0};
3995 http_session_t
*session
= request
->session
;
3999 lpszPath
= heap_alloc((lstrlenW( session
->hostName
) + 13)*sizeof(WCHAR
));
4000 sprintfW( lpszPath
, szFormat
, session
->hostName
, session
->hostPort
);
4001 requestString
= HTTP_BuildHeaderRequestString( request
, szConnect
, lpszPath
, g_szHttp1_1
);
4002 heap_free( lpszPath
);
4004 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
4005 NULL
, 0, NULL
, NULL
);
4006 len
--; /* the nul terminator isn't needed */
4007 ascii_req
= heap_alloc(len
);
4008 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1, ascii_req
, len
, NULL
, NULL
);
4009 heap_free( requestString
);
4011 TRACE("full request -> %s\n", debugstr_an( ascii_req
, len
) );
4013 NETCON_set_timeout( request
->netconn
, TRUE
, request
->send_timeout
);
4014 res
= NETCON_send( request
->netconn
, ascii_req
, len
, 0, &cnt
);
4015 heap_free( ascii_req
);
4016 if (res
!= ERROR_SUCCESS
)
4019 responseLen
= HTTP_GetResponseHeaders( request
, TRUE
);
4021 return ERROR_HTTP_INVALID_HEADER
;
4023 return ERROR_SUCCESS
;
4026 static void HTTP_InsertCookies(http_request_t
*request
)
4028 DWORD cookie_size
, size
, cnt
= 0;
4032 static const WCHAR cookieW
[] = {'C','o','o','k','i','e',':',' ',0};
4034 host
= HTTP_GetHeader(request
, hostW
);
4038 if(!get_cookie(host
->lpszValue
, request
->path
, NULL
, &cookie_size
))
4041 size
= sizeof(cookieW
) + cookie_size
* sizeof(WCHAR
) + sizeof(szCrLf
);
4042 if(!(cookies
= heap_alloc(size
)))
4045 cnt
+= sprintfW(cookies
, cookieW
);
4046 get_cookie(host
->lpszValue
, request
->path
, cookies
+cnt
, &cookie_size
);
4047 strcatW(cookies
, szCrLf
);
4049 HTTP_HttpAddRequestHeadersW(request
, cookies
, strlenW(cookies
), HTTP_ADDREQ_FLAG_REPLACE
);
4054 static WORD
HTTP_ParseWkday(LPCWSTR day
)
4056 static const WCHAR days
[7][4] = {{ 's','u','n',0 },
4064 for (i
= 0; i
< sizeof(days
)/sizeof(*days
); i
++)
4065 if (!strcmpiW(day
, days
[i
]))
4072 static WORD
HTTP_ParseMonth(LPCWSTR month
)
4074 static const WCHAR jan
[] = { 'j','a','n',0 };
4075 static const WCHAR feb
[] = { 'f','e','b',0 };
4076 static const WCHAR mar
[] = { 'm','a','r',0 };
4077 static const WCHAR apr
[] = { 'a','p','r',0 };
4078 static const WCHAR may
[] = { 'm','a','y',0 };
4079 static const WCHAR jun
[] = { 'j','u','n',0 };
4080 static const WCHAR jul
[] = { 'j','u','l',0 };
4081 static const WCHAR aug
[] = { 'a','u','g',0 };
4082 static const WCHAR sep
[] = { 's','e','p',0 };
4083 static const WCHAR oct
[] = { 'o','c','t',0 };
4084 static const WCHAR nov
[] = { 'n','o','v',0 };
4085 static const WCHAR dec
[] = { 'd','e','c',0 };
4087 if (!strcmpiW(month
, jan
)) return 1;
4088 if (!strcmpiW(month
, feb
)) return 2;
4089 if (!strcmpiW(month
, mar
)) return 3;
4090 if (!strcmpiW(month
, apr
)) return 4;
4091 if (!strcmpiW(month
, may
)) return 5;
4092 if (!strcmpiW(month
, jun
)) return 6;
4093 if (!strcmpiW(month
, jul
)) return 7;
4094 if (!strcmpiW(month
, aug
)) return 8;
4095 if (!strcmpiW(month
, sep
)) return 9;
4096 if (!strcmpiW(month
, oct
)) return 10;
4097 if (!strcmpiW(month
, nov
)) return 11;
4098 if (!strcmpiW(month
, dec
)) return 12;
4103 /* Parses the string pointed to by *str, assumed to be a 24-hour time HH:MM:SS,
4104 * optionally preceded by whitespace.
4105 * Upon success, returns TRUE, sets the wHour, wMinute, and wSecond fields of
4106 * st, and sets *str to the first character after the time format.
4108 static BOOL
HTTP_ParseTime(SYSTEMTIME
*st
, LPCWSTR
*str
)
4114 while (isspaceW(*ptr
))
4117 num
= strtoulW(ptr
, &nextPtr
, 10);
4118 if (!nextPtr
|| nextPtr
<= ptr
|| *nextPtr
!= ':')
4120 ERR("unexpected time format %s\n", debugstr_w(ptr
));
4125 ERR("unexpected hour in time format %s\n", debugstr_w(ptr
));
4129 st
->wHour
= (WORD
)num
;
4130 num
= strtoulW(ptr
, &nextPtr
, 10);
4131 if (!nextPtr
|| nextPtr
<= ptr
|| *nextPtr
!= ':')
4133 ERR("unexpected time format %s\n", debugstr_w(ptr
));
4138 ERR("unexpected minute in time format %s\n", debugstr_w(ptr
));
4142 st
->wMinute
= (WORD
)num
;
4143 num
= strtoulW(ptr
, &nextPtr
, 10);
4144 if (!nextPtr
|| nextPtr
<= ptr
)
4146 ERR("unexpected time format %s\n", debugstr_w(ptr
));
4151 ERR("unexpected second in time format %s\n", debugstr_w(ptr
));
4156 st
->wSecond
= (WORD
)num
;
4160 static BOOL
HTTP_ParseDateAsAsctime(LPCWSTR value
, FILETIME
*ft
)
4162 static const WCHAR gmt
[]= { 'G','M','T',0 };
4163 WCHAR day
[4], *dayPtr
, month
[4], *monthPtr
, *nextPtr
;
4165 SYSTEMTIME st
= { 0 };
4168 for (ptr
= value
, dayPtr
= day
; *ptr
&& !isspaceW(*ptr
) &&
4169 dayPtr
- day
< sizeof(day
) / sizeof(day
[0]) - 1; ptr
++, dayPtr
++)
4172 st
.wDayOfWeek
= HTTP_ParseWkday(day
);
4173 if (st
.wDayOfWeek
>= 7)
4175 ERR("unexpected weekday %s\n", debugstr_w(day
));
4179 while (isspaceW(*ptr
))
4182 for (monthPtr
= month
; !isspace(*ptr
) &&
4183 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
4187 st
.wMonth
= HTTP_ParseMonth(month
);
4188 if (!st
.wMonth
|| st
.wMonth
> 12)
4190 ERR("unexpected month %s\n", debugstr_w(month
));
4194 while (isspaceW(*ptr
))
4197 num
= strtoulW(ptr
, &nextPtr
, 10);
4198 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
4200 ERR("unexpected day %s\n", debugstr_w(ptr
));
4204 st
.wDay
= (WORD
)num
;
4206 while (isspaceW(*ptr
))
4209 if (!HTTP_ParseTime(&st
, &ptr
))
4212 while (isspaceW(*ptr
))
4215 num
= strtoulW(ptr
, &nextPtr
, 10);
4216 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
4218 ERR("unexpected year %s\n", debugstr_w(ptr
));
4222 st
.wYear
= (WORD
)num
;
4224 while (isspaceW(*ptr
))
4227 /* asctime() doesn't report a timezone, but some web servers do, so accept
4228 * with or without GMT.
4230 if (*ptr
&& strcmpW(ptr
, gmt
))
4232 ERR("unexpected timezone %s\n", debugstr_w(ptr
));
4235 return SystemTimeToFileTime(&st
, ft
);
4238 static BOOL
HTTP_ParseRfc1123Date(LPCWSTR value
, FILETIME
*ft
)
4240 static const WCHAR gmt
[]= { 'G','M','T',0 };
4241 WCHAR
*nextPtr
, day
[4], month
[4], *monthPtr
;
4244 SYSTEMTIME st
= { 0 };
4246 ptr
= strchrW(value
, ',');
4249 if (ptr
- value
!= 3)
4251 WARN("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4254 memcpy(day
, value
, (ptr
- value
) * sizeof(WCHAR
));
4256 st
.wDayOfWeek
= HTTP_ParseWkday(day
);
4257 if (st
.wDayOfWeek
> 6)
4259 WARN("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4264 while (isspaceW(*ptr
))
4267 num
= strtoulW(ptr
, &nextPtr
, 10);
4268 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
4270 WARN("unexpected day %s\n", debugstr_w(value
));
4274 st
.wDay
= (WORD
)num
;
4276 while (isspaceW(*ptr
))
4279 for (monthPtr
= month
; !isspace(*ptr
) &&
4280 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
4284 st
.wMonth
= HTTP_ParseMonth(month
);
4285 if (!st
.wMonth
|| st
.wMonth
> 12)
4287 WARN("unexpected month %s\n", debugstr_w(month
));
4291 while (isspaceW(*ptr
))
4294 num
= strtoulW(ptr
, &nextPtr
, 10);
4295 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
4297 ERR("unexpected year %s\n", debugstr_w(value
));
4301 st
.wYear
= (WORD
)num
;
4303 if (!HTTP_ParseTime(&st
, &ptr
))
4306 while (isspaceW(*ptr
))
4309 if (strcmpW(ptr
, gmt
))
4311 ERR("unexpected time zone %s\n", debugstr_w(ptr
));
4314 return SystemTimeToFileTime(&st
, ft
);
4317 static WORD
HTTP_ParseWeekday(LPCWSTR day
)
4319 static const WCHAR days
[7][10] = {{ 's','u','n','d','a','y',0 },
4320 { 'm','o','n','d','a','y',0 },
4321 { 't','u','e','s','d','a','y',0 },
4322 { 'w','e','d','n','e','s','d','a','y',0 },
4323 { 't','h','u','r','s','d','a','y',0 },
4324 { 'f','r','i','d','a','y',0 },
4325 { 's','a','t','u','r','d','a','y',0 }};
4327 for (i
= 0; i
< sizeof(days
)/sizeof(*days
); i
++)
4328 if (!strcmpiW(day
, days
[i
]))
4335 static BOOL
HTTP_ParseRfc850Date(LPCWSTR value
, FILETIME
*ft
)
4337 static const WCHAR gmt
[]= { 'G','M','T',0 };
4338 WCHAR
*nextPtr
, day
[10], month
[4], *monthPtr
;
4341 SYSTEMTIME st
= { 0 };
4343 ptr
= strchrW(value
, ',');
4346 if (ptr
- value
== 3)
4348 memcpy(day
, value
, (ptr
- value
) * sizeof(WCHAR
));
4350 st
.wDayOfWeek
= HTTP_ParseWkday(day
);
4351 if (st
.wDayOfWeek
> 6)
4353 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4357 else if (ptr
- value
< sizeof(day
) / sizeof(day
[0]))
4359 memcpy(day
, value
, (ptr
- value
) * sizeof(WCHAR
));
4360 day
[ptr
- value
+ 1] = 0;
4361 st
.wDayOfWeek
= HTTP_ParseWeekday(day
);
4362 if (st
.wDayOfWeek
> 6)
4364 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4370 ERR("unexpected weekday %s\n", debugstr_wn(value
, ptr
- value
));
4375 while (isspaceW(*ptr
))
4378 num
= strtoulW(ptr
, &nextPtr
, 10);
4379 if (!nextPtr
|| nextPtr
<= ptr
|| !num
|| num
> 31)
4381 ERR("unexpected day %s\n", debugstr_w(value
));
4385 st
.wDay
= (WORD
)num
;
4389 ERR("unexpected month format %s\n", debugstr_w(ptr
));
4394 for (monthPtr
= month
; *ptr
!= '-' &&
4395 monthPtr
- month
< sizeof(month
) / sizeof(month
[0]) - 1;
4399 st
.wMonth
= HTTP_ParseMonth(month
);
4400 if (!st
.wMonth
|| st
.wMonth
> 12)
4402 ERR("unexpected month %s\n", debugstr_w(month
));
4408 ERR("unexpected year format %s\n", debugstr_w(ptr
));
4413 num
= strtoulW(ptr
, &nextPtr
, 10);
4414 if (!nextPtr
|| nextPtr
<= ptr
|| num
< 1601 || num
> 30827)
4416 ERR("unexpected year %s\n", debugstr_w(value
));
4420 st
.wYear
= (WORD
)num
;
4422 if (!HTTP_ParseTime(&st
, &ptr
))
4425 while (isspaceW(*ptr
))
4428 if (strcmpW(ptr
, gmt
))
4430 ERR("unexpected time zone %s\n", debugstr_w(ptr
));
4433 return SystemTimeToFileTime(&st
, ft
);
4436 static BOOL
HTTP_ParseDate(LPCWSTR value
, FILETIME
*ft
)
4438 static const WCHAR zero
[] = { '0',0 };
4441 if (!strcmpW(value
, zero
))
4443 ft
->dwLowDateTime
= ft
->dwHighDateTime
= 0;
4446 else if (strchrW(value
, ','))
4448 ret
= HTTP_ParseRfc1123Date(value
, ft
);
4451 ret
= HTTP_ParseRfc850Date(value
, ft
);
4453 ERR("unexpected date format %s\n", debugstr_w(value
));
4458 ret
= HTTP_ParseDateAsAsctime(value
, ft
);
4460 ERR("unexpected date format %s\n", debugstr_w(value
));
4465 static void HTTP_ProcessExpires(http_request_t
*request
)
4467 BOOL expirationFound
= FALSE
;
4470 /* Look for a Cache-Control header with a max-age directive, as it takes
4471 * precedence over the Expires header.
4473 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szCache_Control
, 0, FALSE
);
4474 if (headerIndex
!= -1)
4476 LPHTTPHEADERW ccHeader
= &request
->custHeaders
[headerIndex
];
4479 for (ptr
= ccHeader
->lpszValue
; ptr
&& *ptr
; )
4481 LPWSTR comma
= strchrW(ptr
, ','), end
, equal
;
4486 end
= ptr
+ strlenW(ptr
);
4487 for (equal
= end
- 1; equal
> ptr
&& *equal
!= '='; equal
--)
4491 static const WCHAR max_age
[] = {
4492 'm','a','x','-','a','g','e',0 };
4494 if (!strncmpiW(ptr
, max_age
, equal
- ptr
- 1))
4499 age
= strtoulW(equal
+ 1, &nextPtr
, 10);
4500 if (nextPtr
> equal
+ 1)
4504 NtQuerySystemTime( &ft
);
4505 /* Age is in seconds, FILETIME resolution is in
4506 * 100 nanosecond intervals.
4508 ft
.QuadPart
+= age
* (ULONGLONG
)1000000;
4509 request
->expires
.dwLowDateTime
= ft
.u
.LowPart
;
4510 request
->expires
.dwHighDateTime
= ft
.u
.HighPart
;
4511 expirationFound
= TRUE
;
4518 while (isspaceW(*ptr
))
4525 if (!expirationFound
)
4527 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szExpires
, 0, FALSE
);
4528 if (headerIndex
!= -1)
4530 LPHTTPHEADERW expiresHeader
= &request
->custHeaders
[headerIndex
];
4533 if (HTTP_ParseDate(expiresHeader
->lpszValue
, &ft
))
4535 expirationFound
= TRUE
;
4536 request
->expires
= ft
;
4540 if (!expirationFound
)
4544 /* With no known age, default to 10 minutes until expiration. */
4545 NtQuerySystemTime( &t
);
4546 t
.QuadPart
+= 10 * 60 * (ULONGLONG
)10000000;
4547 request
->expires
.dwLowDateTime
= t
.u
.LowPart
;
4548 request
->expires
.dwHighDateTime
= t
.u
.HighPart
;
4552 static void HTTP_ProcessLastModified(http_request_t
*request
)
4556 headerIndex
= HTTP_GetCustomHeaderIndex(request
, szLast_Modified
, 0, FALSE
);
4557 if (headerIndex
!= -1)
4559 LPHTTPHEADERW expiresHeader
= &request
->custHeaders
[headerIndex
];
4562 if (HTTP_ParseDate(expiresHeader
->lpszValue
, &ft
))
4563 request
->last_modified
= ft
;
4567 static void http_process_keep_alive(http_request_t
*req
)
4571 index
= HTTP_GetCustomHeaderIndex(req
, szConnection
, 0, FALSE
);
4573 req
->netconn
->keep_alive
= !strcmpiW(req
->custHeaders
[index
].lpszValue
, szKeepAlive
);
4575 req
->netconn
->keep_alive
= !strcmpiW(req
->version
, g_szHttp1_1
);
4578 static void HTTP_CacheRequest(http_request_t
*request
)
4580 WCHAR url
[INTERNET_MAX_URL_LENGTH
];
4581 WCHAR cacheFileName
[MAX_PATH
+1];
4584 b
= HTTP_GetRequestURL(request
, url
);
4586 WARN("Could not get URL\n");
4590 b
= CreateUrlCacheEntryW(url
, request
->contentLength
, NULL
, cacheFileName
, 0);
4592 heap_free(request
->cacheFile
);
4593 CloseHandle(request
->hCacheFile
);
4595 request
->cacheFile
= heap_strdupW(cacheFileName
);
4596 request
->hCacheFile
= CreateFileW(request
->cacheFile
, GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
4597 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
4598 if(request
->hCacheFile
== INVALID_HANDLE_VALUE
) {
4599 WARN("Could not create file: %u\n", GetLastError());
4600 request
->hCacheFile
= NULL
;
4603 WARN("Could not create cache entry: %08x\n", GetLastError());
4607 static DWORD
open_http_connection(http_request_t
*request
, BOOL
*reusing
)
4609 const BOOL is_https
= (request
->hdr
.dwFlags
& INTERNET_FLAG_SECURE
) != 0;
4610 netconn_t
*netconn
= NULL
;
4613 assert(!request
->netconn
);
4614 reset_data_stream(request
);
4616 res
= HTTP_ResolveName(request
);
4617 if(res
!= ERROR_SUCCESS
)
4620 EnterCriticalSection(&connection_pool_cs
);
4622 while(!list_empty(&request
->server
->conn_pool
)) {
4623 netconn
= LIST_ENTRY(list_head(&request
->server
->conn_pool
), netconn_t
, pool_entry
);
4624 list_remove(&netconn
->pool_entry
);
4626 if(NETCON_is_alive(netconn
))
4629 TRACE("connection %p closed during idle\n", netconn
);
4630 free_netconn(netconn
);
4634 LeaveCriticalSection(&connection_pool_cs
);
4637 TRACE("<-- reusing %p netconn\n", netconn
);
4638 request
->netconn
= netconn
;
4640 return ERROR_SUCCESS
;
4643 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4644 INTERNET_STATUS_CONNECTING_TO_SERVER
,
4645 request
->server
->addr_str
,
4646 strlen(request
->server
->addr_str
)+1);
4648 res
= create_netconn(is_https
, request
->server
, request
->security_flags
,
4649 (request
->hdr
.ErrorMask
& INTERNET_ERROR_MASK_COMBINED_SEC_CERT
) != 0,
4650 request
->connect_timeout
, &netconn
);
4651 if(res
!= ERROR_SUCCESS
) {
4652 ERR("create_netconn failed: %u\n", res
);
4656 request
->netconn
= netconn
;
4658 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4659 INTERNET_STATUS_CONNECTED_TO_SERVER
,
4660 request
->server
->addr_str
, strlen(request
->server
->addr_str
)+1);
4663 /* Note: we differ from Microsoft's WinINet here. they seem to have
4664 * a bug that causes no status callbacks to be sent when starting
4665 * a tunnel to a proxy server using the CONNECT verb. i believe our
4666 * behaviour to be more correct and to not cause any incompatibilities
4667 * because using a secure connection through a proxy server is a rare
4668 * case that would be hard for anyone to depend on */
4669 if(request
->session
->appInfo
->proxy
)
4670 res
= HTTP_SecureProxyConnect(request
);
4671 if(res
== ERROR_SUCCESS
)
4672 res
= NETCON_secure_connect(request
->netconn
);
4675 if(res
!= ERROR_SUCCESS
) {
4676 http_release_netconn(request
, FALSE
);
4681 TRACE("Created connection to %s: %p\n", debugstr_w(request
->server
->name
), netconn
);
4682 return ERROR_SUCCESS
;
4685 /***********************************************************************
4686 * HTTP_HttpSendRequestW (internal)
4688 * Sends the specified request to the HTTP server
4691 * ERROR_SUCCESS on success
4692 * win32 error code on failure
4695 static DWORD
HTTP_HttpSendRequestW(http_request_t
*request
, LPCWSTR lpszHeaders
,
4696 DWORD dwHeaderLength
, LPVOID lpOptional
, DWORD dwOptionalLength
,
4697 DWORD dwContentLength
, BOOL bEndRequest
)
4700 BOOL redirected
= FALSE
;
4701 LPWSTR requestString
= NULL
;
4704 static const WCHAR szPost
[] = { 'P','O','S','T',0 };
4705 static const WCHAR szContentLength
[] =
4706 { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
4707 WCHAR contentLengthStr
[sizeof szContentLength
/2 /* includes \r\n */ + 20 /* int */ ];
4710 TRACE("--> %p\n", request
);
4712 assert(request
->hdr
.htype
== WH_HHTTPREQ
);
4714 /* if the verb is NULL default to GET */
4716 request
->verb
= heap_strdupW(szGET
);
4718 if (dwContentLength
|| strcmpW(request
->verb
, szGET
))
4720 sprintfW(contentLengthStr
, szContentLength
, dwContentLength
);
4721 HTTP_HttpAddRequestHeadersW(request
, contentLengthStr
, -1L, HTTP_ADDREQ_FLAG_REPLACE
);
4722 request
->bytesToWrite
= dwContentLength
;
4724 if (request
->session
->appInfo
->agent
)
4726 WCHAR
*agent_header
;
4727 static const WCHAR user_agent
[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
4730 len
= strlenW(request
->session
->appInfo
->agent
) + strlenW(user_agent
);
4731 agent_header
= heap_alloc(len
* sizeof(WCHAR
));
4732 sprintfW(agent_header
, user_agent
, request
->session
->appInfo
->agent
);
4734 HTTP_HttpAddRequestHeadersW(request
, agent_header
, strlenW(agent_header
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4735 heap_free(agent_header
);
4737 if (request
->hdr
.dwFlags
& INTERNET_FLAG_PRAGMA_NOCACHE
)
4739 static const WCHAR pragma_nocache
[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
4740 HTTP_HttpAddRequestHeadersW(request
, pragma_nocache
, strlenW(pragma_nocache
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4742 if ((request
->hdr
.dwFlags
& INTERNET_FLAG_NO_CACHE_WRITE
) && !strcmpW(request
->verb
, szPost
))
4744 static const WCHAR cache_control
[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
4745 ' ','n','o','-','c','a','c','h','e','\r','\n',0};
4746 HTTP_HttpAddRequestHeadersW(request
, cache_control
, strlenW(cache_control
), HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
4749 /* add the headers the caller supplied */
4750 if( lpszHeaders
&& dwHeaderLength
)
4751 HTTP_HttpAddRequestHeadersW(request
, lpszHeaders
, dwHeaderLength
, HTTP_ADDREQ_FLAG_ADD
| HTTP_ADDHDR_FLAG_REPLACE
);
4756 BOOL reusing_connection
;
4761 /* like native, just in case the caller forgot to call InternetReadFile
4762 * for all the data */
4763 drain_content(request
);
4765 request
->contentLength
= ~0u;
4766 request
->bytesToWrite
= 0;
4769 if (TRACE_ON(wininet
))
4771 LPHTTPHEADERW Host
= HTTP_GetHeader(request
, hostW
);
4772 TRACE("Going to url %s %s\n", debugstr_w(Host
->lpszValue
), debugstr_w(request
->path
));
4775 HTTP_FixURL(request
);
4776 if (request
->hdr
.dwFlags
& INTERNET_FLAG_KEEP_CONNECTION
)
4778 HTTP_ProcessHeader(request
, szConnection
, szKeepAlive
, HTTP_ADDHDR_FLAG_REQ
| HTTP_ADDHDR_FLAG_REPLACE
);
4780 HTTP_InsertAuthorization(request
, request
->authInfo
, szAuthorization
);
4781 HTTP_InsertAuthorization(request
, request
->proxyAuthInfo
, szProxy_Authorization
);
4783 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_COOKIES
))
4784 HTTP_InsertCookies(request
);
4786 if (request
->session
->appInfo
->proxy
&& request
->session
->appInfo
->proxy
[0])
4788 WCHAR
*url
= HTTP_BuildProxyRequestUrl(request
);
4789 requestString
= HTTP_BuildHeaderRequestString(request
, request
->verb
, url
, request
->version
);
4793 requestString
= HTTP_BuildHeaderRequestString(request
, request
->verb
, request
->path
, request
->version
);
4796 TRACE("Request header -> %s\n", debugstr_w(requestString
) );
4798 if ((res
= open_http_connection(request
, &reusing_connection
)) != ERROR_SUCCESS
)
4801 /* send the request as ASCII, tack on the optional data */
4802 if (!lpOptional
|| redirected
)
4803 dwOptionalLength
= 0;
4804 len
= WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
4805 NULL
, 0, NULL
, NULL
);
4806 ascii_req
= heap_alloc(len
+ dwOptionalLength
);
4807 WideCharToMultiByte( CP_ACP
, 0, requestString
, -1,
4808 ascii_req
, len
, NULL
, NULL
);
4810 memcpy( &ascii_req
[len
-1], lpOptional
, dwOptionalLength
);
4811 len
= (len
+ dwOptionalLength
- 1);
4813 TRACE("full request -> %s\n", debugstr_a(ascii_req
) );
4815 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4816 INTERNET_STATUS_SENDING_REQUEST
, NULL
, 0);
4818 NETCON_set_timeout( request
->netconn
, TRUE
, request
->send_timeout
);
4819 res
= NETCON_send(request
->netconn
, ascii_req
, len
, 0, &cnt
);
4820 heap_free( ascii_req
);
4821 if(res
!= ERROR_SUCCESS
) {
4822 TRACE("send failed: %u\n", res
);
4823 if(!reusing_connection
)
4825 http_release_netconn(request
, FALSE
);
4830 request
->bytesWritten
= dwOptionalLength
;
4832 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4833 INTERNET_STATUS_REQUEST_SENT
,
4834 &len
, sizeof(DWORD
));
4840 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4841 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
4843 responseLen
= HTTP_GetResponseHeaders(request
, TRUE
);
4844 /* FIXME: We should know that connection is closed before sending
4845 * headers. Otherwise wrong callbacks are executed */
4846 if(!responseLen
&& reusing_connection
) {
4847 TRACE("Connection closed by server, reconnecting\n");
4848 http_release_netconn(request
, FALSE
);
4853 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
4854 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
,
4857 http_process_keep_alive(request
);
4858 HTTP_ProcessCookies(request
);
4859 HTTP_ProcessExpires(request
);
4860 HTTP_ProcessLastModified(request
);
4862 res
= set_content_length(request
);
4863 if(res
!= ERROR_SUCCESS
)
4865 if(!request
->contentLength
)
4866 http_release_netconn(request
, TRUE
);
4868 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
) && responseLen
)
4870 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
4871 dwBufferSize
=sizeof(szNewLocation
);
4872 switch(request
->status_code
) {
4873 case HTTP_STATUS_REDIRECT
:
4874 case HTTP_STATUS_MOVED
:
4875 case HTTP_STATUS_REDIRECT_KEEP_VERB
:
4876 case HTTP_STATUS_REDIRECT_METHOD
:
4877 if(HTTP_HttpQueryInfoW(request
,HTTP_QUERY_LOCATION
,szNewLocation
,&dwBufferSize
,NULL
) != ERROR_SUCCESS
)
4880 if (strcmpW(request
->verb
, szGET
) && strcmpW(request
->verb
, szHEAD
) &&
4881 request
->status_code
!= HTTP_STATUS_REDIRECT_KEEP_VERB
)
4883 heap_free(request
->verb
);
4884 request
->verb
= heap_strdupW(szGET
);
4886 drain_content(request
);
4887 if ((new_url
= HTTP_GetRedirectURL( request
, szNewLocation
)))
4889 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
4890 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
4891 res
= HTTP_HandleRedirect(request
, new_url
);
4892 if (res
== ERROR_SUCCESS
)
4894 heap_free(requestString
);
4897 heap_free( new_url
);
4902 if (!(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTH
) && res
== ERROR_SUCCESS
)
4904 WCHAR szAuthValue
[2048];
4906 if (request
->status_code
== HTTP_STATUS_DENIED
)
4908 LPHTTPHEADERW Host
= HTTP_GetHeader(request
, hostW
);
4910 while (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_WWW_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
4912 if (HTTP_DoAuthorization(request
, szAuthValue
,
4914 request
->session
->userName
,
4915 request
->session
->password
,
4918 heap_free(requestString
);
4925 TRACE("Cleaning wrong authorization data\n");
4926 destroy_authinfo(request
->authInfo
);
4927 request
->authInfo
= NULL
;
4930 if (request
->status_code
== HTTP_STATUS_PROXY_AUTH_REQ
)
4933 while (HTTP_HttpQueryInfoW(request
,HTTP_QUERY_PROXY_AUTHENTICATE
,szAuthValue
,&dwBufferSize
,&dwIndex
) == ERROR_SUCCESS
)
4935 if (HTTP_DoAuthorization(request
, szAuthValue
,
4936 &request
->proxyAuthInfo
,
4937 request
->session
->appInfo
->proxyUsername
,
4938 request
->session
->appInfo
->proxyPassword
,
4947 TRACE("Cleaning wrong proxy authorization data\n");
4948 destroy_authinfo(request
->proxyAuthInfo
);
4949 request
->proxyAuthInfo
= NULL
;
4955 res
= ERROR_SUCCESS
;
4959 if(res
== ERROR_SUCCESS
)
4960 HTTP_CacheRequest(request
);
4963 heap_free(requestString
);
4965 /* TODO: send notification for P3P header */
4967 if (request
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
4969 if (res
== ERROR_SUCCESS
) {
4970 if(bEndRequest
&& request
->contentLength
&& request
->bytesWritten
== request
->bytesToWrite
)
4971 HTTP_ReceiveRequestData(request
, TRUE
);
4973 send_request_complete(request
,
4974 request
->session
->hdr
.dwInternalFlags
& INET_OPENURL
? (DWORD_PTR
)request
->hdr
.hInternet
: 1, 0);
4976 send_request_complete(request
, 0, res
);
4984 /***********************************************************************
4986 * Helper functions for the HttpSendRequest(Ex) functions
4989 static void AsyncHttpSendRequestProc(WORKREQUEST
*workRequest
)
4991 struct WORKREQ_HTTPSENDREQUESTW
const *req
= &workRequest
->u
.HttpSendRequestW
;
4992 http_request_t
*request
= (http_request_t
*) workRequest
->hdr
;
4994 TRACE("%p\n", request
);
4996 HTTP_HttpSendRequestW(request
, req
->lpszHeader
,
4997 req
->dwHeaderLength
, req
->lpOptional
, req
->dwOptionalLength
,
4998 req
->dwContentLength
, req
->bEndRequest
);
5000 heap_free(req
->lpszHeader
);
5004 static DWORD
HTTP_HttpEndRequestW(http_request_t
*request
, DWORD dwFlags
, DWORD_PTR dwContext
)
5008 DWORD res
= ERROR_SUCCESS
;
5010 if(!request
->netconn
) {
5011 WARN("Not connected\n");
5012 send_request_complete(request
, 0, ERROR_INTERNET_OPERATION_CANCELLED
);
5013 return ERROR_INTERNET_OPERATION_CANCELLED
;
5016 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
5017 INTERNET_STATUS_RECEIVING_RESPONSE
, NULL
, 0);
5019 responseLen
= HTTP_GetResponseHeaders(request
, TRUE
);
5021 res
= ERROR_HTTP_HEADER_NOT_FOUND
;
5023 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
,
5024 INTERNET_STATUS_RESPONSE_RECEIVED
, &responseLen
, sizeof(DWORD
));
5026 /* process cookies here. Is this right? */
5027 http_process_keep_alive(request
);
5028 HTTP_ProcessCookies(request
);
5029 HTTP_ProcessExpires(request
);
5030 HTTP_ProcessLastModified(request
);
5032 if ((res
= set_content_length(request
)) == ERROR_SUCCESS
) {
5033 if(!request
->contentLength
)
5034 http_release_netconn(request
, TRUE
);
5037 if (res
== ERROR_SUCCESS
&& !(request
->hdr
.dwFlags
& INTERNET_FLAG_NO_AUTO_REDIRECT
))
5039 switch(request
->status_code
) {
5040 case HTTP_STATUS_REDIRECT
:
5041 case HTTP_STATUS_MOVED
:
5042 case HTTP_STATUS_REDIRECT_METHOD
:
5043 case HTTP_STATUS_REDIRECT_KEEP_VERB
: {
5044 WCHAR
*new_url
, szNewLocation
[INTERNET_MAX_URL_LENGTH
];
5045 dwBufferSize
=sizeof(szNewLocation
);
5046 if (HTTP_HttpQueryInfoW(request
, HTTP_QUERY_LOCATION
, szNewLocation
, &dwBufferSize
, NULL
) != ERROR_SUCCESS
)
5049 if (strcmpW(request
->verb
, szGET
) && strcmpW(request
->verb
, szHEAD
) &&
5050 request
->status_code
!= HTTP_STATUS_REDIRECT_KEEP_VERB
)
5052 heap_free(request
->verb
);
5053 request
->verb
= heap_strdupW(szGET
);
5055 drain_content(request
);
5056 if ((new_url
= HTTP_GetRedirectURL( request
, szNewLocation
)))
5058 INTERNET_SendCallback(&request
->hdr
, request
->hdr
.dwContext
, INTERNET_STATUS_REDIRECT
,
5059 new_url
, (strlenW(new_url
) + 1) * sizeof(WCHAR
));
5060 res
= HTTP_HandleRedirect(request
, new_url
);
5061 if (res
== ERROR_SUCCESS
)
5062 res
= HTTP_HttpSendRequestW(request
, NULL
, 0, NULL
, 0, 0, TRUE
);
5063 heap_free( new_url
);
5069 if (res
== ERROR_SUCCESS
&& request
->contentLength
)
5070 HTTP_ReceiveRequestData(request
, TRUE
);
5072 send_request_complete(request
, res
== ERROR_SUCCESS
, res
);
5077 /***********************************************************************
5078 * HttpEndRequestA (WININET.@)
5080 * Ends an HTTP request that was started by HttpSendRequestEx
5083 * TRUE if successful
5087 BOOL WINAPI
HttpEndRequestA(HINTERNET hRequest
,
5088 LPINTERNET_BUFFERSA lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
5090 TRACE("(%p, %p, %08x, %08lx)\n", hRequest
, lpBuffersOut
, dwFlags
, dwContext
);
5094 SetLastError(ERROR_INVALID_PARAMETER
);
5098 return HttpEndRequestW(hRequest
, NULL
, dwFlags
, dwContext
);
5101 static void AsyncHttpEndRequestProc(WORKREQUEST
*work
)
5103 struct WORKREQ_HTTPENDREQUESTW
const *req
= &work
->u
.HttpEndRequestW
;
5104 http_request_t
*request
= (http_request_t
*)work
->hdr
;
5106 TRACE("%p\n", request
);
5108 HTTP_HttpEndRequestW(request
, req
->dwFlags
, req
->dwContext
);
5111 /***********************************************************************
5112 * HttpEndRequestW (WININET.@)
5114 * Ends an HTTP request that was started by HttpSendRequestEx
5117 * TRUE if successful
5121 BOOL WINAPI
HttpEndRequestW(HINTERNET hRequest
,
5122 LPINTERNET_BUFFERSW lpBuffersOut
, DWORD dwFlags
, DWORD_PTR dwContext
)
5124 http_request_t
*request
;
5127 TRACE("%p %p %x %lx -->\n", hRequest
, lpBuffersOut
, dwFlags
, dwContext
);
5131 SetLastError(ERROR_INVALID_PARAMETER
);
5135 request
= (http_request_t
*) get_handle_object( hRequest
);
5137 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
5139 SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE
);
5141 WININET_Release( &request
->hdr
);
5144 request
->hdr
.dwFlags
|= dwFlags
;
5146 if (request
->session
->appInfo
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
5149 struct WORKREQ_HTTPENDREQUESTW
*work_endrequest
;
5151 work
.asyncproc
= AsyncHttpEndRequestProc
;
5152 work
.hdr
= WININET_AddRef( &request
->hdr
);
5154 work_endrequest
= &work
.u
.HttpEndRequestW
;
5155 work_endrequest
->dwFlags
= dwFlags
;
5156 work_endrequest
->dwContext
= dwContext
;
5158 INTERNET_AsyncCall(&work
);
5159 res
= ERROR_IO_PENDING
;
5162 res
= HTTP_HttpEndRequestW(request
, dwFlags
, dwContext
);
5164 WININET_Release( &request
->hdr
);
5165 TRACE("%u <--\n", res
);
5166 if(res
!= ERROR_SUCCESS
)
5168 return res
== ERROR_SUCCESS
;
5171 /***********************************************************************
5172 * HttpSendRequestExA (WININET.@)
5174 * Sends the specified request to the HTTP server and allows chunked
5179 * Failure: FALSE, call GetLastError() for more information.
5181 BOOL WINAPI
HttpSendRequestExA(HINTERNET hRequest
,
5182 LPINTERNET_BUFFERSA lpBuffersIn
,
5183 LPINTERNET_BUFFERSA lpBuffersOut
,
5184 DWORD dwFlags
, DWORD_PTR dwContext
)
5186 INTERNET_BUFFERSW BuffersInW
;
5189 LPWSTR header
= NULL
;
5191 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
5192 lpBuffersOut
, dwFlags
, dwContext
);
5196 BuffersInW
.dwStructSize
= sizeof(LPINTERNET_BUFFERSW
);
5197 if (lpBuffersIn
->lpcszHeader
)
5199 headerlen
= MultiByteToWideChar(CP_ACP
,0,lpBuffersIn
->lpcszHeader
,
5200 lpBuffersIn
->dwHeadersLength
,0,0);
5201 header
= heap_alloc(headerlen
*sizeof(WCHAR
));
5202 if (!(BuffersInW
.lpcszHeader
= header
))
5204 SetLastError(ERROR_OUTOFMEMORY
);
5207 BuffersInW
.dwHeadersLength
= MultiByteToWideChar(CP_ACP
, 0,
5208 lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
5212 BuffersInW
.lpcszHeader
= NULL
;
5213 BuffersInW
.dwHeadersTotal
= lpBuffersIn
->dwHeadersTotal
;
5214 BuffersInW
.lpvBuffer
= lpBuffersIn
->lpvBuffer
;
5215 BuffersInW
.dwBufferLength
= lpBuffersIn
->dwBufferLength
;
5216 BuffersInW
.dwBufferTotal
= lpBuffersIn
->dwBufferTotal
;
5217 BuffersInW
.Next
= NULL
;
5220 rc
= HttpSendRequestExW(hRequest
, lpBuffersIn
? &BuffersInW
: NULL
, NULL
, dwFlags
, dwContext
);
5226 /***********************************************************************
5227 * HttpSendRequestExW (WININET.@)
5229 * Sends the specified request to the HTTP server and allows chunked
5234 * Failure: FALSE, call GetLastError() for more information.
5236 BOOL WINAPI
HttpSendRequestExW(HINTERNET hRequest
,
5237 LPINTERNET_BUFFERSW lpBuffersIn
,
5238 LPINTERNET_BUFFERSW lpBuffersOut
,
5239 DWORD dwFlags
, DWORD_PTR dwContext
)
5241 http_request_t
*request
;
5242 http_session_t
*session
;
5246 TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest
, lpBuffersIn
,
5247 lpBuffersOut
, dwFlags
, dwContext
);
5249 request
= (http_request_t
*) get_handle_object( hRequest
);
5251 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
5253 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5257 session
= request
->session
;
5258 assert(session
->hdr
.htype
== WH_HHTTPSESSION
);
5259 hIC
= session
->appInfo
;
5260 assert(hIC
->hdr
.htype
== WH_HINIT
);
5262 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
5264 WORKREQUEST workRequest
;
5265 struct WORKREQ_HTTPSENDREQUESTW
*req
;
5267 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
5268 workRequest
.hdr
= WININET_AddRef( &request
->hdr
);
5269 req
= &workRequest
.u
.HttpSendRequestW
;
5274 if (lpBuffersIn
->lpcszHeader
)
5276 if (lpBuffersIn
->dwHeadersLength
== ~0u)
5277 size
= (strlenW( lpBuffersIn
->lpcszHeader
) + 1) * sizeof(WCHAR
);
5279 size
= lpBuffersIn
->dwHeadersLength
* sizeof(WCHAR
);
5281 req
->lpszHeader
= heap_alloc(size
);
5282 memcpy( req
->lpszHeader
, lpBuffersIn
->lpcszHeader
, size
);
5284 else req
->lpszHeader
= NULL
;
5286 req
->dwHeaderLength
= size
/ sizeof(WCHAR
);
5287 req
->lpOptional
= lpBuffersIn
->lpvBuffer
;
5288 req
->dwOptionalLength
= lpBuffersIn
->dwBufferLength
;
5289 req
->dwContentLength
= lpBuffersIn
->dwBufferTotal
;
5293 req
->lpszHeader
= NULL
;
5294 req
->dwHeaderLength
= 0;
5295 req
->lpOptional
= NULL
;
5296 req
->dwOptionalLength
= 0;
5297 req
->dwContentLength
= 0;
5300 req
->bEndRequest
= FALSE
;
5302 INTERNET_AsyncCall(&workRequest
);
5304 * This is from windows.
5306 res
= ERROR_IO_PENDING
;
5311 res
= HTTP_HttpSendRequestW(request
, lpBuffersIn
->lpcszHeader
, lpBuffersIn
->dwHeadersLength
,
5312 lpBuffersIn
->lpvBuffer
, lpBuffersIn
->dwBufferLength
,
5313 lpBuffersIn
->dwBufferTotal
, FALSE
);
5315 res
= HTTP_HttpSendRequestW(request
, NULL
, 0, NULL
, 0, 0, FALSE
);
5320 WININET_Release( &request
->hdr
);
5324 return res
== ERROR_SUCCESS
;
5327 /***********************************************************************
5328 * HttpSendRequestW (WININET.@)
5330 * Sends the specified request to the HTTP server
5337 BOOL WINAPI
HttpSendRequestW(HINTERNET hHttpRequest
, LPCWSTR lpszHeaders
,
5338 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
5340 http_request_t
*request
;
5341 http_session_t
*session
= NULL
;
5342 appinfo_t
*hIC
= NULL
;
5343 DWORD res
= ERROR_SUCCESS
;
5345 TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest
,
5346 debugstr_wn(lpszHeaders
, dwHeaderLength
), dwHeaderLength
, lpOptional
, dwOptionalLength
);
5348 request
= (http_request_t
*) get_handle_object( hHttpRequest
);
5349 if (NULL
== request
|| request
->hdr
.htype
!= WH_HHTTPREQ
)
5351 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5355 session
= request
->session
;
5356 if (NULL
== session
|| session
->hdr
.htype
!= WH_HHTTPSESSION
)
5358 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5362 hIC
= session
->appInfo
;
5363 if (NULL
== hIC
|| hIC
->hdr
.htype
!= WH_HINIT
)
5365 res
= ERROR_INTERNET_INCORRECT_HANDLE_TYPE
;
5369 if (hIC
->hdr
.dwFlags
& INTERNET_FLAG_ASYNC
)
5371 WORKREQUEST workRequest
;
5372 struct WORKREQ_HTTPSENDREQUESTW
*req
;
5374 workRequest
.asyncproc
= AsyncHttpSendRequestProc
;
5375 workRequest
.hdr
= WININET_AddRef( &request
->hdr
);
5376 req
= &workRequest
.u
.HttpSendRequestW
;
5381 if (dwHeaderLength
== ~0u) size
= (strlenW(lpszHeaders
) + 1) * sizeof(WCHAR
);
5382 else size
= dwHeaderLength
* sizeof(WCHAR
);
5384 req
->lpszHeader
= heap_alloc(size
);
5385 memcpy(req
->lpszHeader
, lpszHeaders
, size
);
5388 req
->lpszHeader
= 0;
5389 req
->dwHeaderLength
= dwHeaderLength
;
5390 req
->lpOptional
= lpOptional
;
5391 req
->dwOptionalLength
= dwOptionalLength
;
5392 req
->dwContentLength
= dwOptionalLength
;
5393 req
->bEndRequest
= TRUE
;
5395 INTERNET_AsyncCall(&workRequest
);
5397 * This is from windows.
5399 res
= ERROR_IO_PENDING
;
5403 res
= HTTP_HttpSendRequestW(request
, lpszHeaders
,
5404 dwHeaderLength
, lpOptional
, dwOptionalLength
,
5405 dwOptionalLength
, TRUE
);
5409 WININET_Release( &request
->hdr
);
5412 return res
== ERROR_SUCCESS
;
5415 /***********************************************************************
5416 * HttpSendRequestA (WININET.@)
5418 * Sends the specified request to the HTTP server
5425 BOOL WINAPI
HttpSendRequestA(HINTERNET hHttpRequest
, LPCSTR lpszHeaders
,
5426 DWORD dwHeaderLength
, LPVOID lpOptional
,DWORD dwOptionalLength
)
5429 LPWSTR szHeaders
=NULL
;
5430 DWORD nLen
=dwHeaderLength
;
5431 if(lpszHeaders
!=NULL
)
5433 nLen
=MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,NULL
,0);
5434 szHeaders
= heap_alloc(nLen
*sizeof(WCHAR
));
5435 MultiByteToWideChar(CP_ACP
,0,lpszHeaders
,dwHeaderLength
,szHeaders
,nLen
);
5437 result
= HttpSendRequestW(hHttpRequest
, szHeaders
, nLen
, lpOptional
, dwOptionalLength
);
5438 heap_free(szHeaders
);
5442 /***********************************************************************
5443 * HTTPSESSION_Destroy (internal)
5445 * Deallocate session handle
5448 static void HTTPSESSION_Destroy(object_header_t
*hdr
)
5450 http_session_t
*session
= (http_session_t
*) hdr
;
5452 TRACE("%p\n", session
);
5454 WININET_Release(&session
->appInfo
->hdr
);
5456 heap_free(session
->hostName
);
5457 heap_free(session
->password
);
5458 heap_free(session
->userName
);
5461 static DWORD
HTTPSESSION_QueryOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD
*size
, BOOL unicode
)
5463 http_session_t
*ses
= (http_session_t
*)hdr
;
5466 case INTERNET_OPTION_HANDLE_TYPE
:
5467 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
5469 if (*size
< sizeof(ULONG
))
5470 return ERROR_INSUFFICIENT_BUFFER
;
5472 *size
= sizeof(DWORD
);
5473 *(DWORD
*)buffer
= INTERNET_HANDLE_TYPE_CONNECT_HTTP
;
5474 return ERROR_SUCCESS
;
5475 case INTERNET_OPTION_CONNECT_TIMEOUT
:
5476 TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
5478 if (*size
< sizeof(DWORD
))
5479 return ERROR_INSUFFICIENT_BUFFER
;
5481 *size
= sizeof(DWORD
);
5482 *(DWORD
*)buffer
= ses
->connect_timeout
;
5483 return ERROR_SUCCESS
;
5485 case INTERNET_OPTION_SEND_TIMEOUT
:
5486 TRACE("INTERNET_OPTION_SEND_TIMEOUT\n");
5488 if (*size
< sizeof(DWORD
))
5489 return ERROR_INSUFFICIENT_BUFFER
;
5491 *size
= sizeof(DWORD
);
5492 *(DWORD
*)buffer
= ses
->send_timeout
;
5493 return ERROR_SUCCESS
;
5495 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
5496 TRACE("INTERNET_OPTION_RECEIVE_TIMEOUT\n");
5498 if (*size
< sizeof(DWORD
))
5499 return ERROR_INSUFFICIENT_BUFFER
;
5501 *size
= sizeof(DWORD
);
5502 *(DWORD
*)buffer
= ses
->receive_timeout
;
5503 return ERROR_SUCCESS
;
5506 return INET_QueryOption(hdr
, option
, buffer
, size
, unicode
);
5509 static DWORD
HTTPSESSION_SetOption(object_header_t
*hdr
, DWORD option
, void *buffer
, DWORD size
)
5511 http_session_t
*ses
= (http_session_t
*)hdr
;
5514 case INTERNET_OPTION_USERNAME
:
5516 heap_free(ses
->userName
);
5517 if (!(ses
->userName
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
5518 return ERROR_SUCCESS
;
5520 case INTERNET_OPTION_PASSWORD
:
5522 heap_free(ses
->password
);
5523 if (!(ses
->password
= heap_strdupW(buffer
))) return ERROR_OUTOFMEMORY
;
5524 return ERROR_SUCCESS
;
5526 case INTERNET_OPTION_CONNECT_TIMEOUT
:
5528 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
5529 ses
->connect_timeout
= *(DWORD
*)buffer
;
5530 return ERROR_SUCCESS
;
5532 case INTERNET_OPTION_SEND_TIMEOUT
:
5534 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
5535 ses
->send_timeout
= *(DWORD
*)buffer
;
5536 return ERROR_SUCCESS
;
5538 case INTERNET_OPTION_RECEIVE_TIMEOUT
:
5540 if (!buffer
|| size
!= sizeof(DWORD
)) return ERROR_INVALID_PARAMETER
;
5541 ses
->receive_timeout
= *(DWORD
*)buffer
;
5542 return ERROR_SUCCESS
;
5547 return INET_SetOption(hdr
, option
, buffer
, size
);
5550 static const object_vtbl_t HTTPSESSIONVtbl
= {
5551 HTTPSESSION_Destroy
,
5553 HTTPSESSION_QueryOption
,
5554 HTTPSESSION_SetOption
,
5563 /***********************************************************************
5564 * HTTP_Connect (internal)
5566 * Create http session handle
5569 * HINTERNET a session handle on success
5573 DWORD
HTTP_Connect(appinfo_t
*hIC
, LPCWSTR lpszServerName
,
5574 INTERNET_PORT serverPort
, LPCWSTR lpszUserName
,
5575 LPCWSTR lpszPassword
, DWORD dwFlags
, DWORD_PTR dwContext
,
5576 DWORD dwInternalFlags
, HINTERNET
*ret
)
5578 http_session_t
*session
= NULL
;
5582 if (!lpszServerName
|| !lpszServerName
[0])
5583 return ERROR_INVALID_PARAMETER
;
5585 assert( hIC
->hdr
.htype
== WH_HINIT
);
5587 session
= alloc_object(&hIC
->hdr
, &HTTPSESSIONVtbl
, sizeof(http_session_t
));
5589 return ERROR_OUTOFMEMORY
;
5592 * According to my tests. The name is not resolved until a request is sent
5595 session
->hdr
.htype
= WH_HHTTPSESSION
;
5596 session
->hdr
.dwFlags
= dwFlags
;
5597 session
->hdr
.dwContext
= dwContext
;
5598 session
->hdr
.dwInternalFlags
|= dwInternalFlags
;
5600 WININET_AddRef( &hIC
->hdr
);
5601 session
->appInfo
= hIC
;
5602 list_add_head( &hIC
->hdr
.children
, &session
->hdr
.entry
);
5604 if(hIC
->proxy
&& hIC
->accessType
== INTERNET_OPEN_TYPE_PROXY
) {
5605 if(hIC
->proxyBypass
)
5606 FIXME("Proxy bypass is ignored.\n");
5608 session
->hostName
= heap_strdupW(lpszServerName
);
5609 if (lpszUserName
&& lpszUserName
[0])
5610 session
->userName
= heap_strdupW(lpszUserName
);
5611 if (lpszPassword
&& lpszPassword
[0])
5612 session
->password
= heap_strdupW(lpszPassword
);
5613 session
->hostPort
= serverPort
;
5614 session
->connect_timeout
= hIC
->connect_timeout
;
5615 session
->send_timeout
= INFINITE
;
5616 session
->receive_timeout
= INFINITE
;
5618 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
5619 if (!(session
->hdr
.dwInternalFlags
& INET_OPENURL
))
5621 INTERNET_SendCallback(&hIC
->hdr
, dwContext
,
5622 INTERNET_STATUS_HANDLE_CREATED
, &session
->hdr
.hInternet
,
5627 * an INTERNET_STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on
5631 TRACE("%p --> %p\n", hIC
, session
);
5633 *ret
= session
->hdr
.hInternet
;
5634 return ERROR_SUCCESS
;
5637 /***********************************************************************
5638 * HTTP_clear_response_headers (internal)
5640 * clear out any old response headers
5642 static void HTTP_clear_response_headers( http_request_t
*request
)
5646 for( i
=0; i
<request
->nCustHeaders
; i
++)
5648 if( !request
->custHeaders
[i
].lpszField
)
5650 if( !request
->custHeaders
[i
].lpszValue
)
5652 if ( request
->custHeaders
[i
].wFlags
& HDR_ISREQUEST
)
5654 HTTP_DeleteCustomHeader( request
, i
);
5659 /***********************************************************************
5660 * HTTP_GetResponseHeaders (internal)
5662 * Read server response
5669 static INT
HTTP_GetResponseHeaders(http_request_t
*request
, BOOL clear
)
5672 WCHAR buffer
[MAX_REPLY_LEN
];
5673 DWORD buflen
= MAX_REPLY_LEN
;
5674 BOOL bSuccess
= FALSE
;
5676 char bufferA
[MAX_REPLY_LEN
];
5677 LPWSTR status_code
= NULL
, status_text
= NULL
;
5678 DWORD cchMaxRawHeaders
= 1024;
5679 LPWSTR lpszRawHeaders
= NULL
;
5681 DWORD cchRawHeaders
= 0;
5682 BOOL codeHundred
= FALSE
;
5686 if(!request
->netconn
)
5689 NETCON_set_timeout( request
->netconn
, FALSE
, request
->receive_timeout
);
5691 static const WCHAR szHundred
[] = {'1','0','0',0};
5693 * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
5695 buflen
= MAX_REPLY_LEN
;
5696 if (!read_line(request
, bufferA
, &buflen
))
5699 /* clear old response headers (eg. from a redirect response) */
5701 HTTP_clear_response_headers( request
);
5706 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
5707 /* check is this a status code line? */
5708 if (!strncmpW(buffer
, g_szHttp1_0
, 4))
5710 /* split the version from the status code */
5711 status_code
= strchrW( buffer
, ' ' );
5716 /* split the status code from the status text */
5717 status_text
= strchrW( status_code
, ' ' );
5722 request
->status_code
= atoiW(status_code
);
5724 TRACE("version [%s] status code [%s] status text [%s]\n",
5725 debugstr_w(buffer
), debugstr_w(status_code
), debugstr_w(status_text
) );
5727 codeHundred
= (!strcmpW(status_code
, szHundred
));
5729 else if (!codeHundred
)
5731 WARN("No status line at head of response (%s)\n", debugstr_w(buffer
));
5733 heap_free(request
->version
);
5734 heap_free(request
->statusText
);
5736 request
->status_code
= HTTP_STATUS_OK
;
5737 request
->version
= heap_strdupW(g_szHttp1_0
);
5738 request
->statusText
= heap_strdupW(szOK
);
5740 heap_free(request
->rawHeaders
);
5741 request
->rawHeaders
= heap_strdupW(szDefaultHeader
);
5746 } while (codeHundred
);
5748 /* Add status code */
5749 HTTP_ProcessHeader(request
, szStatus
, status_code
,
5750 HTTP_ADDHDR_FLAG_REPLACE
);
5752 heap_free(request
->version
);
5753 heap_free(request
->statusText
);
5755 request
->version
= heap_strdupW(buffer
);
5756 request
->statusText
= heap_strdupW(status_text
);
5758 /* Restore the spaces */
5759 *(status_code
-1) = ' ';
5760 *(status_text
-1) = ' ';
5762 /* regenerate raw headers */
5763 lpszRawHeaders
= heap_alloc((cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
5764 if (!lpszRawHeaders
) goto lend
;
5766 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5767 cchMaxRawHeaders
*= 2;
5768 temp
= heap_realloc(lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
5769 if (temp
== NULL
) goto lend
;
5770 lpszRawHeaders
= temp
;
5771 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
5772 cchRawHeaders
+= (buflen
-1);
5773 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
5774 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
5775 lpszRawHeaders
[cchRawHeaders
] = '\0';
5777 /* Parse each response line */
5780 buflen
= MAX_REPLY_LEN
;
5781 if (read_line(request
, bufferA
, &buflen
))
5783 LPWSTR
* pFieldAndValue
;
5785 TRACE("got line %s, now interpreting\n", debugstr_a(bufferA
));
5787 if (!bufferA
[0]) break;
5788 MultiByteToWideChar( CP_ACP
, 0, bufferA
, buflen
, buffer
, MAX_REPLY_LEN
);
5790 pFieldAndValue
= HTTP_InterpretHttpHeader(buffer
);
5793 while (cchRawHeaders
+ buflen
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5794 cchMaxRawHeaders
*= 2;
5795 temp
= heap_realloc(lpszRawHeaders
, (cchMaxRawHeaders
+1)*sizeof(WCHAR
));
5796 if (temp
== NULL
) goto lend
;
5797 lpszRawHeaders
= temp
;
5798 memcpy(lpszRawHeaders
+cchRawHeaders
, buffer
, (buflen
-1)*sizeof(WCHAR
));
5799 cchRawHeaders
+= (buflen
-1);
5800 memcpy(lpszRawHeaders
+cchRawHeaders
, szCrLf
, sizeof(szCrLf
));
5801 cchRawHeaders
+= sizeof(szCrLf
)/sizeof(szCrLf
[0])-1;
5802 lpszRawHeaders
[cchRawHeaders
] = '\0';
5804 HTTP_ProcessHeader(request
, pFieldAndValue
[0], pFieldAndValue
[1],
5805 HTTP_ADDREQ_FLAG_ADD
);
5807 HTTP_FreeTokens(pFieldAndValue
);
5818 /* make sure the response header is terminated with an empty line. Some apps really
5819 truly care about that empty line being there for some reason. Just add it to the
5821 if (cchRawHeaders
+ strlenW(szCrLf
) > cchMaxRawHeaders
)
5823 cchMaxRawHeaders
= cchRawHeaders
+ strlenW(szCrLf
);
5824 temp
= heap_realloc(lpszRawHeaders
, (cchMaxRawHeaders
+ 1) * sizeof(WCHAR
));
5825 if (temp
== NULL
) goto lend
;
5826 lpszRawHeaders
= temp
;
5829 memcpy(&lpszRawHeaders
[cchRawHeaders
], szCrLf
, sizeof(szCrLf
));
5831 heap_free(request
->rawHeaders
);
5832 request
->rawHeaders
= lpszRawHeaders
;
5833 TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders
));
5843 heap_free(lpszRawHeaders
);
5848 /***********************************************************************
5849 * HTTP_InterpretHttpHeader (internal)
5851 * Parse server response
5855 * Pointer to array of field, value, NULL on success.
5858 static LPWSTR
* HTTP_InterpretHttpHeader(LPCWSTR buffer
)
5860 LPWSTR
* pTokenPair
;
5864 pTokenPair
= heap_alloc_zero(sizeof(*pTokenPair
)*3);
5866 pszColon
= strchrW(buffer
, ':');
5867 /* must have two tokens */
5870 HTTP_FreeTokens(pTokenPair
);
5872 TRACE("No ':' in line: %s\n", debugstr_w(buffer
));
5876 pTokenPair
[0] = heap_alloc((pszColon
- buffer
+ 1) * sizeof(WCHAR
));
5879 HTTP_FreeTokens(pTokenPair
);
5882 memcpy(pTokenPair
[0], buffer
, (pszColon
- buffer
) * sizeof(WCHAR
));
5883 pTokenPair
[0][pszColon
- buffer
] = '\0';
5887 len
= strlenW(pszColon
);
5888 pTokenPair
[1] = heap_alloc((len
+ 1) * sizeof(WCHAR
));
5891 HTTP_FreeTokens(pTokenPair
);
5894 memcpy(pTokenPair
[1], pszColon
, (len
+ 1) * sizeof(WCHAR
));
5896 strip_spaces(pTokenPair
[0]);
5897 strip_spaces(pTokenPair
[1]);
5899 TRACE("field(%s) Value(%s)\n", debugstr_w(pTokenPair
[0]), debugstr_w(pTokenPair
[1]));
5903 /***********************************************************************
5904 * HTTP_ProcessHeader (internal)
5906 * Stuff header into header tables according to <dwModifier>
5910 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
5912 static DWORD
HTTP_ProcessHeader(http_request_t
*request
, LPCWSTR field
, LPCWSTR value
, DWORD dwModifier
)
5914 LPHTTPHEADERW lphttpHdr
= NULL
;
5916 BOOL request_only
= dwModifier
& HTTP_ADDHDR_FLAG_REQ
;
5917 DWORD res
= ERROR_HTTP_INVALID_HEADER
;
5919 TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field
), debugstr_w(value
), dwModifier
);
5921 /* REPLACE wins out over ADD */
5922 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
5923 dwModifier
&= ~HTTP_ADDHDR_FLAG_ADD
;
5925 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD
)
5928 index
= HTTP_GetCustomHeaderIndex(request
, field
, 0, request_only
);
5932 if (dwModifier
& HTTP_ADDHDR_FLAG_ADD_IF_NEW
)
5933 return ERROR_HTTP_INVALID_HEADER
;
5934 lphttpHdr
= &request
->custHeaders
[index
];
5940 hdr
.lpszField
= (LPWSTR
)field
;
5941 hdr
.lpszValue
= (LPWSTR
)value
;
5942 hdr
.wFlags
= hdr
.wCount
= 0;
5944 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5945 hdr
.wFlags
|= HDR_ISREQUEST
;
5947 return HTTP_InsertCustomHeader(request
, &hdr
);
5949 /* no value to delete */
5950 else return ERROR_SUCCESS
;
5952 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5953 lphttpHdr
->wFlags
|= HDR_ISREQUEST
;
5955 lphttpHdr
->wFlags
&= ~HDR_ISREQUEST
;
5957 if (dwModifier
& HTTP_ADDHDR_FLAG_REPLACE
)
5959 HTTP_DeleteCustomHeader( request
, index
);
5965 hdr
.lpszField
= (LPWSTR
)field
;
5966 hdr
.lpszValue
= (LPWSTR
)value
;
5967 hdr
.wFlags
= hdr
.wCount
= 0;
5969 if (dwModifier
& HTTP_ADDHDR_FLAG_REQ
)
5970 hdr
.wFlags
|= HDR_ISREQUEST
;
5972 return HTTP_InsertCustomHeader(request
, &hdr
);
5975 return ERROR_SUCCESS
;
5977 else if (dwModifier
& COALESCEFLAGS
)
5982 INT origlen
= strlenW(lphttpHdr
->lpszValue
);
5983 INT valuelen
= strlenW(value
);
5985 if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA
)
5988 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
5990 else if (dwModifier
& HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON
)
5993 lphttpHdr
->wFlags
|= HDR_COMMADELIMITED
;
5996 len
= origlen
+ valuelen
+ ((ch
> 0) ? 2 : 0);
5998 lpsztmp
= heap_realloc(lphttpHdr
->lpszValue
, (len
+1)*sizeof(WCHAR
));
6001 lphttpHdr
->lpszValue
= lpsztmp
;
6002 /* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */
6005 lphttpHdr
->lpszValue
[origlen
] = ch
;
6007 lphttpHdr
->lpszValue
[origlen
] = ' ';
6011 memcpy(&lphttpHdr
->lpszValue
[origlen
], value
, valuelen
*sizeof(WCHAR
));
6012 lphttpHdr
->lpszValue
[len
] = '\0';
6013 res
= ERROR_SUCCESS
;
6017 WARN("heap_realloc (%d bytes) failed\n",len
+1);
6018 res
= ERROR_OUTOFMEMORY
;
6021 TRACE("<-- %d\n", res
);
6025 /***********************************************************************
6026 * HTTP_GetCustomHeaderIndex (internal)
6028 * Return index of custom header from header array
6031 static INT
HTTP_GetCustomHeaderIndex(http_request_t
*request
, LPCWSTR lpszField
,
6032 int requested_index
, BOOL request_only
)
6036 TRACE("%s, %d, %d\n", debugstr_w(lpszField
), requested_index
, request_only
);
6038 for (index
= 0; index
< request
->nCustHeaders
; index
++)
6040 if (strcmpiW(request
->custHeaders
[index
].lpszField
, lpszField
))
6043 if (request_only
&& !(request
->custHeaders
[index
].wFlags
& HDR_ISREQUEST
))
6046 if (!request_only
&& (request
->custHeaders
[index
].wFlags
& HDR_ISREQUEST
))
6049 if (requested_index
== 0)
6054 if (index
>= request
->nCustHeaders
)
6057 TRACE("Return: %d\n", index
);
6062 /***********************************************************************
6063 * HTTP_InsertCustomHeader (internal)
6065 * Insert header into array
6068 static DWORD
HTTP_InsertCustomHeader(http_request_t
*request
, LPHTTPHEADERW lpHdr
)
6071 LPHTTPHEADERW lph
= NULL
;
6073 TRACE("--> %s: %s\n", debugstr_w(lpHdr
->lpszField
), debugstr_w(lpHdr
->lpszValue
));
6074 count
= request
->nCustHeaders
+ 1;
6076 lph
= heap_realloc_zero(request
->custHeaders
, sizeof(HTTPHEADERW
) * count
);
6078 lph
= heap_alloc_zero(sizeof(HTTPHEADERW
) * count
);
6081 return ERROR_OUTOFMEMORY
;
6083 request
->custHeaders
= lph
;
6084 request
->custHeaders
[count
-1].lpszField
= heap_strdupW(lpHdr
->lpszField
);
6085 request
->custHeaders
[count
-1].lpszValue
= heap_strdupW(lpHdr
->lpszValue
);
6086 request
->custHeaders
[count
-1].wFlags
= lpHdr
->wFlags
;
6087 request
->custHeaders
[count
-1].wCount
= lpHdr
->wCount
;
6088 request
->nCustHeaders
++;
6090 return ERROR_SUCCESS
;
6094 /***********************************************************************
6095 * HTTP_DeleteCustomHeader (internal)
6097 * Delete header from array
6098 * If this function is called, the indexs may change.
6100 static BOOL
HTTP_DeleteCustomHeader(http_request_t
*request
, DWORD index
)
6102 if( request
->nCustHeaders
<= 0 )
6104 if( index
>= request
->nCustHeaders
)
6106 request
->nCustHeaders
--;
6108 heap_free(request
->custHeaders
[index
].lpszField
);
6109 heap_free(request
->custHeaders
[index
].lpszValue
);
6111 memmove( &request
->custHeaders
[index
], &request
->custHeaders
[index
+1],
6112 (request
->nCustHeaders
- index
)* sizeof(HTTPHEADERW
) );
6113 memset( &request
->custHeaders
[request
->nCustHeaders
], 0, sizeof(HTTPHEADERW
) );
6119 /***********************************************************************
6120 * HTTP_VerifyValidHeader (internal)
6122 * Verify the given header is not invalid for the given http request
6125 static BOOL
HTTP_VerifyValidHeader(http_request_t
*request
, LPCWSTR field
)
6127 /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
6128 if (!strcmpW(request
->version
, g_szHttp1_0
) && !strcmpiW(field
, szAccept_Encoding
))
6129 return ERROR_HTTP_INVALID_HEADER
;
6131 return ERROR_SUCCESS
;
6134 /***********************************************************************
6135 * IsHostInProxyBypassList (@)
6140 BOOL WINAPI
IsHostInProxyBypassList(DWORD flags
, LPCSTR szHost
, DWORD length
)
6142 FIXME("STUB: flags=%d host=%s length=%d\n",flags
,szHost
,length
);
6146 /***********************************************************************
6147 * InternetShowSecurityInfoByURLA (@)
6149 BOOL WINAPI
InternetShowSecurityInfoByURLA(LPCSTR url
, HWND window
)
6151 FIXME("stub: %s %p\n", url
, window
);
6155 /***********************************************************************
6156 * InternetShowSecurityInfoByURLW (@)
6158 BOOL WINAPI
InternetShowSecurityInfoByURLW(LPCWSTR url
, HWND window
)
6160 FIXME("stub: %s %p\n", debugstr_w(url
), window
);
6164 /***********************************************************************
6165 * ShowX509EncodedCertificate (@)
6167 DWORD WINAPI
ShowX509EncodedCertificate(HWND parent
, LPBYTE cert
, DWORD len
)
6169 PCCERT_CONTEXT certContext
= CertCreateCertificateContext(X509_ASN_ENCODING
,
6175 CRYPTUI_VIEWCERTIFICATE_STRUCTW view
;
6177 memset(&view
, 0, sizeof(view
));
6178 view
.hwndParent
= parent
;
6179 view
.pCertContext
= certContext
;
6180 if (CryptUIDlgViewCertificateW(&view
, NULL
))
6181 ret
= ERROR_SUCCESS
;
6183 ret
= GetLastError();
6184 CertFreeCertificateContext(certContext
);
6187 ret
= GetLastError();